From deabb1e506ae678eeec608649388a082ada7c11a Mon Sep 17 00:00:00 2001 From: Matyja Lukas Adam Date: Tue, 24 Oct 2023 09:17:40 +0200 Subject: [lib] delete some files --- src/lib/auditor_api_curl_defaults.c | 61 -- src/lib/auditor_api_curl_defaults.h | 38 - src/lib/auditor_api_deposit_confirmation.c | 418 -------- src/lib/auditor_api_exchanges.c | 244 ----- src/lib/auditor_api_get_config.c | 288 ----- src/lib/exchange_api_age_withdraw.c | 1117 -------------------- src/lib/exchange_api_age_withdraw_reveal.c | 471 --------- src/lib/exchange_api_auditor_add_denomination.c | 238 ----- src/lib/exchange_api_csr_melt.c | 317 ------ src/lib/exchange_api_kyc_check.c | 329 ------ src/lib/exchange_api_kyc_proof.c | 217 ---- src/lib/exchange_api_kyc_wallet.c | 230 ---- src/lib/exchange_api_lookup_aml_decision.c | 419 -------- src/lib/exchange_api_lookup_aml_decisions.c | 378 ------- src/lib/exchange_api_management_add_partner.c | 218 ---- src/lib/exchange_api_management_auditor_disable.c | 219 ---- src/lib/exchange_api_management_auditor_enable.c | 224 ---- src/lib/exchange_api_management_drain_profits.c | 213 ---- src/lib/exchange_api_management_post_extensions.c | 213 ---- src/lib/exchange_api_management_post_keys.c | 237 ----- ...change_api_management_revoke_denomination_key.c | 221 ---- .../exchange_api_management_revoke_signing_key.c | 211 ---- src/lib/exchange_api_management_set_global_fee.c | 235 ---- src/lib/exchange_api_management_set_wire_fee.c | 227 ---- .../exchange_api_management_update_aml_officer.c | 229 ---- src/lib/exchange_api_management_wire_disable.c | 220 ---- src/lib/exchange_api_management_wire_enable.c | 245 ----- src/lib/exchange_api_melt.c | 596 ----------- src/lib/exchange_api_recoup.c | 369 ------- src/lib/exchange_api_recoup_refresh.c | 363 ------- src/lib/exchange_api_refresh_common.c | 249 ----- src/lib/exchange_api_refresh_common.h | 201 ---- src/lib/exchange_api_refreshes_reveal.c | 531 ---------- src/lib/exchange_api_refund.c | 480 --------- src/lib/exchange_api_stefan.c | 320 ------ src/lib/test_stefan.c | 206 ---- 36 files changed, 10992 deletions(-) delete mode 100644 src/lib/auditor_api_curl_defaults.c delete mode 100644 src/lib/auditor_api_curl_defaults.h delete mode 100644 src/lib/auditor_api_deposit_confirmation.c delete mode 100644 src/lib/auditor_api_exchanges.c delete mode 100644 src/lib/auditor_api_get_config.c delete mode 100644 src/lib/exchange_api_age_withdraw.c delete mode 100644 src/lib/exchange_api_age_withdraw_reveal.c delete mode 100644 src/lib/exchange_api_auditor_add_denomination.c delete mode 100644 src/lib/exchange_api_csr_melt.c delete mode 100644 src/lib/exchange_api_kyc_check.c delete mode 100644 src/lib/exchange_api_kyc_proof.c delete mode 100644 src/lib/exchange_api_kyc_wallet.c delete mode 100644 src/lib/exchange_api_lookup_aml_decision.c delete mode 100644 src/lib/exchange_api_lookup_aml_decisions.c delete mode 100644 src/lib/exchange_api_management_add_partner.c delete mode 100644 src/lib/exchange_api_management_auditor_disable.c delete mode 100644 src/lib/exchange_api_management_auditor_enable.c delete mode 100644 src/lib/exchange_api_management_drain_profits.c delete mode 100644 src/lib/exchange_api_management_post_extensions.c delete mode 100644 src/lib/exchange_api_management_post_keys.c delete mode 100644 src/lib/exchange_api_management_revoke_denomination_key.c delete mode 100644 src/lib/exchange_api_management_revoke_signing_key.c delete mode 100644 src/lib/exchange_api_management_set_global_fee.c delete mode 100644 src/lib/exchange_api_management_set_wire_fee.c delete mode 100644 src/lib/exchange_api_management_update_aml_officer.c delete mode 100644 src/lib/exchange_api_management_wire_disable.c delete mode 100644 src/lib/exchange_api_management_wire_enable.c delete mode 100644 src/lib/exchange_api_melt.c delete mode 100644 src/lib/exchange_api_recoup.c delete mode 100644 src/lib/exchange_api_recoup_refresh.c delete mode 100644 src/lib/exchange_api_refresh_common.c delete mode 100644 src/lib/exchange_api_refresh_common.h delete mode 100644 src/lib/exchange_api_refreshes_reveal.c delete mode 100644 src/lib/exchange_api_refund.c delete mode 100644 src/lib/exchange_api_stefan.c delete mode 100644 src/lib/test_stefan.c diff --git a/src/lib/auditor_api_curl_defaults.c b/src/lib/auditor_api_curl_defaults.c deleted file mode 100644 index 81fcd7bac..000000000 --- a/src/lib/auditor_api_curl_defaults.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/auditor_api_curl_defaults.c - * @brief curl easy handle defaults - * @author Florian Dold - */ -#include "auditor_api_curl_defaults.h" - - -CURL * -TALER_AUDITOR_curl_easy_get_ (const char *url) -{ - CURL *eh; - struct GNUNET_AsyncScopeSave scope; - - GNUNET_async_scope_get (&scope); - - eh = curl_easy_init (); - if (NULL == eh) - return NULL; - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_URL, - url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_FOLLOWLOCATION, - 1L)); - /* Enable compression (using whatever curl likes), see - https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html */ - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_ACCEPT_ENCODING, - "")); - /* limit MAXREDIRS to 5 as a simple security measure against - a potential infinite loop caused by a malicious target */ - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_MAXREDIRS, - 5L)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TCP_FASTOPEN, - 1L)); - return eh; -} diff --git a/src/lib/auditor_api_curl_defaults.h b/src/lib/auditor_api_curl_defaults.h deleted file mode 100644 index 99e1e07e6..000000000 --- a/src/lib/auditor_api_curl_defaults.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/auditor_api_curl_defaults.h - * @brief curl easy handle defaults - * @author Florian Dold - */ -#ifndef _TALER_CURL_DEFAULTS_H -#define _TALER_CURL_DEFAULTS_H - -#include "platform.h" -#include - - -/** - * Get a curl handle with the right defaults - * for the auditor lib. In the future, we might manage a pool of connections here. - * - * @param url URL to query - */ -CURL * -TALER_AUDITOR_curl_easy_get_ (const char *url); - -#endif /* _TALER_CURL_DEFAULTS_H */ diff --git a/src/lib/auditor_api_deposit_confirmation.c b/src/lib/auditor_api_deposit_confirmation.c deleted file mode 100644 index 1e2ecc6cc..000000000 --- a/src/lib/auditor_api_deposit_confirmation.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/auditor_api_deposit_confirmation.c - * @brief Implementation of the /deposit request of the auditor's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_util.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" -#include "taler_auditor_service.h" -#include "taler_signatures.h" -#include "auditor_api_curl_defaults.h" - - -/** - * @brief A DepositConfirmation Handle - */ -struct TALER_AUDITOR_DepositConfirmationHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_AUDITOR_DepositConfirmationResultCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - -}; - - -/** - * Function called when we're done processing the - * HTTP /deposit-confirmation request. - * - * @param cls the `struct TALER_AUDITOR_DepositConfirmationHandle` - * @param response_code HTTP response code, 0 on error - * @param djson parsed JSON result, NULL on error - */ -static void -handle_deposit_confirmation_finished (void *cls, - long response_code, - const void *djson) -{ - const json_t *json = djson; - struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls; - struct TALER_AUDITOR_DepositConfirmationResponse dcr = { - .hr.reply = json, - .hr.http_status = (unsigned int) response_code - }; - - dh->job = NULL; - switch (response_code) - { - case 0: - dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - dcr.hr.ec = TALER_EC_NONE; - break; - case MHD_HTTP_BAD_REQUEST: - dcr.hr.ec = TALER_JSON_get_error_code (json); - dcr.hr.hint = TALER_JSON_get_error_hint (json); - /* This should never happen, either us or the auditor is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - dcr.hr.ec = TALER_JSON_get_error_code (json); - dcr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, auditor says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - dcr.hr.ec = TALER_JSON_get_error_code (json); - dcr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - break; - case MHD_HTTP_GONE: - dcr.hr.ec = TALER_JSON_get_error_code (json); - dcr.hr.hint = TALER_JSON_get_error_hint (json); - /* Nothing really to verify, auditor says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - dcr.hr.ec = TALER_JSON_get_error_code (json); - dcr.hr.hint = TALER_JSON_get_error_hint (json); - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - dcr.hr.ec = TALER_JSON_get_error_code (json); - dcr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for auditor deposit confirmation\n", - (unsigned int) response_code, - dcr.hr.ec); - break; - } - dh->cb (dh->cb_cls, - &dcr); - TALER_AUDITOR_deposit_confirmation_cancel (dh); -} - - -/** - * Verify signature information about the deposit-confirmation. - * - * @param h_wire hash of merchant wire details - * @param h_policy hash over the policy extension, if any - * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor) - * @param exchange_timestamp timestamp when the deposit was received by the wallet - * @param wire_deadline by what time must the amount be wired to the merchant - * @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline - * @param amount_without_fee the amount confirmed to be wired by the exchange to the merchant - * @param coin_pub coin’s public key - * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) - * @param exchange_sig the signature made with purpose #TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT - * @param exchange_pub the public key of the exchange that matches @a exchange_sig - * @param master_pub master public key of the exchange - * @param ep_start when does @a exchange_pub validity start - * @param ep_expire when does @a exchange_pub usage end - * @param ep_end when does @a exchange_pub legal validity end - * @param master_sig master signature affirming validity of @a exchange_pub - * @return #GNUNET_OK if signatures are OK, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_signatures ( - const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExtensionPolicyHashP *h_policy, - const struct TALER_PrivateContractHashP *h_contract_terms, - struct GNUNET_TIME_Timestamp exchange_timestamp, - struct GNUNET_TIME_Timestamp wire_deadline, - struct GNUNET_TIME_Timestamp refund_deadline, - const struct TALER_Amount *amount_without_fee, - unsigned int num_coins, - const struct TALER_CoinSpendSignatureP *coin_sigs[ - static num_coins], - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct TALER_ExchangeSignatureP *exchange_sig, - const struct TALER_MasterPublicKeyP *master_pub, - struct GNUNET_TIME_Timestamp ep_start, - struct GNUNET_TIME_Timestamp ep_expire, - struct GNUNET_TIME_Timestamp ep_end, - const struct TALER_MasterSignatureP *master_sig) -{ - if (GNUNET_OK != - TALER_exchange_online_deposit_confirmation_verify ( - h_contract_terms, - h_wire, - h_policy, - exchange_timestamp, - wire_deadline, - refund_deadline, - amount_without_fee, - num_coins, - coin_sigs, - merchant_pub, - exchange_pub, - exchange_sig)) - { - GNUNET_break_op (0); - TALER_LOG_WARNING ( - "Invalid signature on /deposit-confirmation request!\n"); - { - TALER_LOG_DEBUG ("... amount_without_fee was %s\n", - TALER_amount2s (amount_without_fee)); - } - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - TALER_exchange_offline_signkey_validity_verify ( - exchange_pub, - ep_start, - ep_expire, - ep_end, - master_pub, - master_sig)) - { - GNUNET_break (0); - TALER_LOG_WARNING ("Invalid signature on exchange signing key!\n"); - return GNUNET_SYSERR; - } - if (GNUNET_TIME_absolute_is_past (ep_end.abs_time)) - { - GNUNET_break (0); - TALER_LOG_WARNING ("Exchange signing key is no longer valid!\n"); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -struct TALER_AUDITOR_DepositConfirmationHandle * -TALER_AUDITOR_deposit_confirmation ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExtensionPolicyHashP *h_policy, - const struct TALER_PrivateContractHashP *h_contract_terms, - struct GNUNET_TIME_Timestamp exchange_timestamp, - struct GNUNET_TIME_Timestamp wire_deadline, - struct GNUNET_TIME_Timestamp refund_deadline, - const struct TALER_Amount *total_without_fee, - unsigned int num_coins, - const struct TALER_CoinSpendPublicKeyP *coin_pubs[ - static num_coins], - const struct TALER_CoinSpendSignatureP *coin_sigs[ - static num_coins], - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct TALER_ExchangeSignatureP *exchange_sig, - const struct TALER_MasterPublicKeyP *master_pub, - struct GNUNET_TIME_Timestamp ep_start, - struct GNUNET_TIME_Timestamp ep_expire, - struct GNUNET_TIME_Timestamp ep_end, - const struct TALER_MasterSignatureP *master_sig, - TALER_AUDITOR_DepositConfirmationResultCallback cb, - void *cb_cls) -{ - struct TALER_AUDITOR_DepositConfirmationHandle *dh; - json_t *deposit_confirmation_obj; - CURL *eh; - json_t *jcoin_sigs; - json_t *jcoin_pubs; - - if (0 == num_coins) - { - GNUNET_break (0); - return NULL; - } - if (GNUNET_OK != - verify_signatures (h_wire, - h_policy, - h_contract_terms, - exchange_timestamp, - wire_deadline, - refund_deadline, - total_without_fee, - num_coins, - coin_sigs, - merchant_pub, - exchange_pub, - exchange_sig, - master_pub, - ep_start, - ep_expire, - ep_end, - master_sig)) - { - GNUNET_break_op (0); - return NULL; - } - jcoin_sigs = json_array (); - GNUNET_assert (NULL != jcoin_sigs); - jcoin_pubs = json_array (); - GNUNET_assert (NULL != jcoin_pubs); - for (unsigned int i = 0; icb = cb; - dh->cb_cls = cb_cls; - dh->url = TALER_url_join (url, - "deposit-confirmation", - NULL); - if (NULL == dh->url) - { - GNUNET_free (dh); - return NULL; - } - eh = TALER_AUDITOR_curl_easy_get_ (dh->url); - if ( (NULL == eh) || - (CURLE_OK != - curl_easy_setopt (eh, - CURLOPT_CUSTOMREQUEST, - "PUT")) || - (GNUNET_OK != - TALER_curl_easy_post (&dh->ctx, - eh, - deposit_confirmation_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (deposit_confirmation_obj); - GNUNET_free (dh->url); - GNUNET_free (dh); - return NULL; - } - json_decref (deposit_confirmation_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "URL for deposit-confirmation: `%s'\n", - dh->url); - dh->job = GNUNET_CURL_job_add2 (ctx, - eh, - dh->ctx.headers, - &handle_deposit_confirmation_finished, - dh); - { - /* Disable 100 continue processing */ - struct curl_slist *x_headers; - - x_headers = curl_slist_append (NULL, - "Expect:"); - GNUNET_CURL_extend_headers (dh->job, - x_headers); - curl_slist_free_all (x_headers); - } - return dh; -} - - -void -TALER_AUDITOR_deposit_confirmation_cancel ( - struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation) -{ - if (NULL != deposit_confirmation->job) - { - GNUNET_CURL_job_cancel (deposit_confirmation->job); - deposit_confirmation->job = NULL; - } - GNUNET_free (deposit_confirmation->url); - TALER_curl_easy_post_finished (&deposit_confirmation->ctx); - GNUNET_free (deposit_confirmation); -} - - -/* end of auditor_api_deposit_confirmation.c */ diff --git a/src/lib/auditor_api_exchanges.c b/src/lib/auditor_api_exchanges.c deleted file mode 100644 index 897dfe60f..000000000 --- a/src/lib/auditor_api_exchanges.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/auditor_api_exchanges.c - * @brief Implementation of the /exchanges request of the auditor's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_json_lib.h" -#include "taler_auditor_service.h" -#include "taler_util.h" -#include "taler_curl_lib.h" -#include "taler_signatures.h" -#include "auditor_api_curl_defaults.h" - -/** - * How many exchanges do we allow a single auditor to - * audit at most? - */ -#define MAX_EXCHANGES 1024 - - -/** - * @brief A ListExchanges Handle - */ -struct TALER_AUDITOR_ListExchangesHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_AUDITOR_ListExchangesResultCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - -}; - - -/** - * Function called when we're done processing the - * HTTP /exchanges request. - * - * @param cls the `struct TALER_AUDITOR_ListExchangesHandle` - * @param response_code HTTP response code, 0 on error - * @param djson parsed JSON result, NULL on error - */ -static void -handle_exchanges_finished (void *cls, - long response_code, - const void *djson) -{ - const json_t *json = djson; - const json_t *ja; - unsigned int ja_len; - struct TALER_AUDITOR_ListExchangesHandle *leh = cls; - struct TALER_AUDITOR_ListExchangesResponse ler = { - .hr.reply = json, - .hr.http_status = (unsigned int) response_code - }; - - leh->job = NULL; - switch (response_code) - { - case 0: - ler.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - ja = json_object_get (json, - "exchanges"); - if ( (NULL == ja) || - (! json_is_array (ja)) ) - { - GNUNET_break (0); - ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - ler.hr.http_status = 0; - break; - } - - ja_len = json_array_size (ja); - if (ja_len > MAX_EXCHANGES) - { - GNUNET_break (0); - ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - ler.hr.http_status = 0; - break; - } - { - struct TALER_AUDITOR_ExchangeInfo ei[GNUNET_NZL (ja_len)]; - bool ok = true; - - for (unsigned int i = 0; icb (leh->cb_cls, - &ler); - TALER_AUDITOR_list_exchanges_cancel (leh); - return; - } - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the auditor is buggy - (or API version conflict); just pass JSON reply to the application */ - ler.hr.ec = TALER_JSON_get_error_code (json); - ler.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - ler.hr.ec = TALER_JSON_get_error_code (json); - ler.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - ler.hr.ec = TALER_JSON_get_error_code (json); - ler.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - ler.hr.ec = TALER_JSON_get_error_code (json); - ler.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for auditor list-exchanges request\n", - (unsigned int) response_code, - (int) ler.hr.ec); - GNUNET_break_op (0); - break; - } - if (NULL != leh->cb) - leh->cb (leh->cb_cls, - &ler); - TALER_AUDITOR_list_exchanges_cancel (leh); -} - - -struct TALER_AUDITOR_ListExchangesHandle * -TALER_AUDITOR_list_exchanges (struct GNUNET_CURL_Context *ctx, - const char *url, - TALER_AUDITOR_ListExchangesResultCallback cb, - void *cb_cls) -{ - struct TALER_AUDITOR_ListExchangesHandle *leh; - CURL *eh; - - leh = GNUNET_new (struct TALER_AUDITOR_ListExchangesHandle); - leh->cb = cb; - leh->cb_cls = cb_cls; - leh->url = TALER_url_join (url, - "exchanges", - NULL); - if (NULL == leh->url) - { - GNUNET_free (leh); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "URL for list-exchanges: `%s'\n", - leh->url); - eh = TALER_AUDITOR_curl_easy_get_ (leh->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (leh->url); - GNUNET_free (leh); - return NULL; - } - leh->job = GNUNET_CURL_job_add (ctx, - eh, - &handle_exchanges_finished, - leh); - return leh; -} - - -void -TALER_AUDITOR_list_exchanges_cancel ( - struct TALER_AUDITOR_ListExchangesHandle *leh) -{ - if (NULL != leh->job) - { - GNUNET_CURL_job_cancel (leh->job); - leh->job = NULL; - } - GNUNET_free (leh->url); - GNUNET_free (leh); -} - - -/* end of auditor_api_exchanges.c */ diff --git a/src/lib/auditor_api_get_config.c b/src/lib/auditor_api_get_config.c deleted file mode 100644 index c9f366568..000000000 --- a/src/lib/auditor_api_get_config.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/auditor_api_get_config.c - * @brief Implementation of /config for the auditor's HTTP API - * @author Sree Harsha Totakura - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include "taler_json_lib.h" -#include "taler_auditor_service.h" -#include "taler_signatures.h" -#include "auditor_api_curl_defaults.h" - - -/** - * Which revision of the Taler auditor protocol is implemented - * by this library? Used to determine compatibility. - */ -#define TALER_PROTOCOL_CURRENT 0 - -/** - * How many revisions back are we compatible to? - */ -#define TALER_PROTOCOL_AGE 0 - - -/** - * Log error related to CURL operations. - * - * @param type log level - * @param function which function failed to run - * @param code what was the curl error code - */ -#define CURL_STRERROR(type, function, code) \ - GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \ - function, __FILE__, __LINE__, curl_easy_strerror (code)); - - -/** - * Handle for the get config request. - */ -struct TALER_AUDITOR_GetConfigHandle -{ - /** - * The context of this handle - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Function to call with the auditor's certification data, - * NULL if this has already been done. - */ - TALER_AUDITOR_ConfigCallback config_cb; - - /** - * Closure to pass to @e config_cb. - */ - void *config_cb_cls; - - /** - * Data for the request to get the /config of a auditor, - * NULL once we are past stage #MHS_INIT. - */ - struct GNUNET_CURL_Job *vr; - - /** - * The url for the @e vr job. - */ - char *vr_url; - -}; - - -/* ***************** Internal /config fetching ************* */ - -/** - * Decode the JSON in @a resp_obj from the /config response and store the data - * in the @a key_data. - * - * @param[in] resp_obj JSON object to parse - * @param[in,out] vi where to store the results we decoded - * @param[out] vc where to store config compatibility data - * @return #TALER_EC_NONE on success - */ -static enum TALER_ErrorCode -decode_config_json (const json_t *resp_obj, - struct TALER_AUDITOR_ConfigInformation *vi, - enum TALER_AUDITOR_VersionCompatibility *vc) -{ - unsigned int age; - unsigned int revision; - unsigned int current; - char dummy; - const char *ver; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("version", - &ver), - GNUNET_JSON_spec_fixed_auto ("auditor_public_key", - &vi->auditor_pub), - GNUNET_JSON_spec_end () - }; - - if (JSON_OBJECT != json_typeof (resp_obj)) - { - GNUNET_break_op (0); - return TALER_EC_GENERIC_JSON_INVALID; - } - /* check the config */ - if (GNUNET_OK != - GNUNET_JSON_parse (resp_obj, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return TALER_EC_GENERIC_JSON_INVALID; - } - if (3 != sscanf (ver, - "%u:%u:%u%c", - ¤t, - &revision, - &age, - &dummy)) - { - GNUNET_break_op (0); - return TALER_EC_GENERIC_VERSION_MALFORMED; - } - vi->version = ver; - *vc = TALER_AUDITOR_VC_MATCH; - if (TALER_PROTOCOL_CURRENT < current) - { - *vc |= TALER_AUDITOR_VC_NEWER; - if (TALER_PROTOCOL_CURRENT < current - age) - *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; - } - if (TALER_PROTOCOL_CURRENT > current) - { - *vc |= TALER_AUDITOR_VC_OLDER; - if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > current) - *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; - } - return TALER_EC_NONE; -} - - -/** - * Callback used when downloading the reply to a /config request - * is complete. - * - * @param cls the `struct TALER_AUDITOR_GetConfigHandle` - * @param response_code HTTP response code, 0 on error - * @param gresp_obj parsed JSON result, NULL on error, must be a `const json_t *` - */ -static void -config_completed_cb (void *cls, - long response_code, - const void *gresp_obj) -{ - struct TALER_AUDITOR_GetConfigHandle *auditor = cls; - const json_t *resp_obj = gresp_obj; - struct TALER_AUDITOR_ConfigResponse vr = { - .hr.reply = resp_obj, - .hr.http_status = (unsigned int) response_code - }; - - auditor->vr = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received config from URL `%s' with status %ld.\n", - auditor->vr_url, - response_code); - switch (response_code) - { - case 0: - GNUNET_break_op (0); - vr.hr.ec = TALER_EC_INVALID; - break; - case MHD_HTTP_OK: - if (NULL == resp_obj) - { - GNUNET_break_op (0); - vr.hr.http_status = 0; - vr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - vr.hr.ec = decode_config_json (resp_obj, - &vr.details.ok.vi, - &vr.details.ok.compat); - if (TALER_EC_NONE != vr.hr.ec) - { - GNUNET_break_op (0); - vr.hr.http_status = 0; - break; - } - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - vr.hr.ec = TALER_JSON_get_error_code (resp_obj); - vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); - break; - default: - vr.hr.ec = TALER_JSON_get_error_code (resp_obj); - vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) vr.hr.ec); - break; - } - auditor->config_cb (auditor->config_cb_cls, - &vr); - TALER_AUDITOR_get_config_cancel (auditor); -} - - -struct TALER_AUDITOR_GetConfigHandle * -TALER_AUDITOR_get_config (struct GNUNET_CURL_Context *ctx, - const char *url, - TALER_AUDITOR_ConfigCallback config_cb, - void *config_cb_cls) -{ - struct TALER_AUDITOR_GetConfigHandle *auditor; - CURL *eh; - - auditor = GNUNET_new (struct TALER_AUDITOR_GetConfigHandle); - auditor->config_cb = config_cb; - auditor->config_cb_cls = config_cb_cls; - auditor->ctx = ctx; - auditor->vr_url = TALER_url_join (url, - "config", - NULL); - if (NULL == auditor->vr_url) - { - GNUNET_break (0); - GNUNET_free (auditor->vr_url); - GNUNET_free (auditor); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting auditor config with URL `%s'.\n", - auditor->vr_url); - eh = TALER_AUDITOR_curl_easy_get_ (auditor->vr_url); - if (NULL == eh) - { - GNUNET_break (0); - TALER_AUDITOR_get_config_cancel (auditor); - return NULL; - } - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT, - (long) 300)); - auditor->vr = GNUNET_CURL_job_add (auditor->ctx, - eh, - &config_completed_cb, - auditor); - return auditor; -} - - -void -TALER_AUDITOR_get_config_cancel (struct TALER_AUDITOR_GetConfigHandle *auditor) -{ - if (NULL != auditor->vr) - { - GNUNET_CURL_job_cancel (auditor->vr); - auditor->vr = NULL; - } - GNUNET_free (auditor->vr_url); - GNUNET_free (auditor); -} - - -/* end of auditor_api_get_config.c */ diff --git a/src/lib/exchange_api_age_withdraw.c b/src/lib/exchange_api_age_withdraw.c deleted file mode 100644 index ea9c0371e..000000000 --- a/src/lib/exchange_api_age_withdraw.c +++ /dev/null @@ -1,1117 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_age_withdraw.c - * @brief Implementation of /reserves/$RESERVE_PUB/age-withdraw requests - * @author Özgür Kesim - */ - -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include -#include "taler_curl_lib.h" -#include "taler_error_codes.h" -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_common.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" -#include "taler_util.h" - -/** - * A CoinCandidate is populated from a master secret - */ -struct CoinCandidate -{ - /** - * Master key material for the coin candidates. - */ - struct TALER_PlanchetMasterSecretP secret; - - /** - * The details derived form the master secrets - */ - struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details; - - /** - * Blinded hash of the coin - **/ - struct TALER_BlindedCoinHashP blinded_coin_h; - -}; - - -/** - * Closure for a call to /csr-withdraw, contains data that is needed to process - * the result. - */ -struct CSRClosure -{ - /* Points to the actual candidate in CoinData.coin_candidates, to continue - * to build its contents based on the results from /csr-withdraw */ - struct CoinCandidate *candidate; - - /* The planchet to finally generate. Points to the corresponding candidate - * in CoindData.planchet_details */ - struct TALER_PlanchetDetail *planchet; - - /* Handler to the originating call to /age-withdraw, needed to either - * cancel the running age-withdraw request (on failure of the current call - * to /csr-withdraw), or to eventually perform the protocol, once all - * csr-withdraw requests have successfully finished. */ - struct TALER_EXCHANGE_AgeWithdrawHandle *age_withdraw_handle; - - /* Denomination information, needed for CS coins for the - * step after /csr-withdraw */ - const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; - - /* Handler for the CS R request */ - struct TALER_EXCHANGE_CsRWithdrawHandle *csr_withdraw_handle; -}; - -/** - * Data we keep per coin in the batch. - */ -struct CoinData -{ - /** - * The denomination of the coin. Must support age restriction, i.e - * its .keys.age_mask MUST not be 0 - */ - struct TALER_EXCHANGE_DenomPublicKey denom_pub; - - /** - * The Candidates for the coin - */ - struct CoinCandidate coin_candidates[TALER_CNC_KAPPA]; - - /** - * Details of the planchet(s). - */ - struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA]; - - /** - * Closure for each candidate of type CS for the preflight request to - * /csr-withdraw - */ - struct CSRClosure csr_cls[TALER_CNC_KAPPA]; -}; - -/** - * A /reserves/$RESERVE_PUB/age-withdraw request-handle for calls with - * pre-blinded planchets. Returned by TALER_EXCHANGE_age_withdraw_blinded. - */ -struct TALER_EXCHANGE_AgeWithdrawBlindedHandle -{ - - /** - * Reserve private key. - */ - const struct TALER_ReservePrivateKeyP *reserve_priv; - - /** - * Reserve public key, calculated - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * Signature of the reserve for the request, calculated after all - * parameters for the coins are collected. - */ - struct TALER_ReserveSignatureP reserve_sig; - - /* - * The denomination keys of the exchange - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The age mask, extacted from the denominations. - * MUST be the same for all denominations - * - */ - struct TALER_AgeMask age_mask; - - /** - * Maximum age to commit to. - */ - uint8_t max_age; - - /** - * The commitment calculated as SHA512 hash over all blinded_coin_h - */ - struct TALER_AgeWithdrawCommitmentHashP h_commitment; - - /** - * Total amount requested (value plus withdraw fee). - */ - struct TALER_Amount amount_with_fee; - - /** - * Length of the @e blinded_input Array - */ - size_t num_input; - - /** - * The blinded planchet input for the call to /age-withdraw via - * TALER_EXCHANGE_age_withdraw_blinded - */ - const struct TALER_EXCHANGE_AgeWithdrawBlindedInput *blinded_input; - - /** - * The url for this request. - */ - char *request_url; - - /** - * Context for curl. - */ - struct GNUNET_CURL_Context *curl_ctx; - - /** - * CURL handle for the request job. - */ - struct GNUNET_CURL_Job *job; - - /** - * Post Context - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Function to call with age-withdraw response results. - */ - TALER_EXCHANGE_AgeWithdrawBlindedCallback callback; - - /** - * Closure for @e blinded_callback - */ - void *callback_cls; -}; - -/** - * A /reserves/$RESERVE_PUB/age-withdraw request-handle for calls from - * a wallet, i. e. when blinding data is available. - */ -struct TALER_EXCHANGE_AgeWithdrawHandle -{ - - /** - * Length of the @e coin_data Array - */ - size_t num_coins; - - /** - * The base-URL of the exchange. - */ - const char *exchange_url; - - /** - * Reserve private key. - */ - const struct TALER_ReservePrivateKeyP *reserve_priv; - - /** - * Reserve public key, calculated - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * Signature of the reserve for the request, calculated after all - * parameters for the coins are collected. - */ - struct TALER_ReserveSignatureP reserve_sig; - - /* - * The denomination keys of the exchange - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The age mask, extacted from the denominations. - * MUST be the same for all denominations - * - */ - struct TALER_AgeMask age_mask; - - /** - * Maximum age to commit to. - */ - uint8_t max_age; - - /** - * Array of per-coin data - */ - struct CoinData *coin_data; - - /** - * Context for curl. - */ - struct GNUNET_CURL_Context *curl_ctx; - - struct - { - /** - * Number of /csr-withdraw requests still pending. - */ - unsigned int pending; - - /** - * CURL handle for the request job. - */ - struct GNUNET_CURL_Job *job; - } csr; - - - /** - * Function to call with age-withdraw response results. - */ - TALER_EXCHANGE_AgeWithdrawCallback callback; - - /** - * Closure for @e age_withdraw_cb - */ - void *callback_cls; - - /* The Handler for the actual call to the exchange */ - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *procotol_handle; -}; - -/** - * We got a 200 OK response for the /reserves/$RESERVE_PUB/age-withdraw operation. - * Extract the noreveal_index and return it to the caller. - * - * @param awbh operation handle - * @param j_response reply from the exchange - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -reserve_age_withdraw_ok ( - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh, - const json_t *j_response) -{ - struct TALER_EXCHANGE_AgeWithdrawBlindedResponse response = { - .hr.reply = j_response, - .hr.http_status = MHD_HTTP_OK, - .details.ok.h_commitment = awbh->h_commitment - }; - struct TALER_ExchangeSignatureP exchange_sig; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint8 ("noreveal_index", - &response.details.ok.noreveal_index), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &response.details.ok.exchange_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK!= - GNUNET_JSON_parse (j_response, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - TALER_exchange_online_age_withdraw_confirmation_verify ( - &awbh->h_commitment, - response.details.ok.noreveal_index, - &response.details.ok.exchange_pub, - &exchange_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - - } - - awbh->callback (awbh->callback_cls, - &response); - /* make sure the callback isn't called again */ - awbh->callback = NULL; - - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /reserves/$RESERVE_PUB/age-withdraw request. - * - * @param cls the `struct TALER_EXCHANGE_AgeWithdrawHandle` - * @param response_code The HTTP response code - * @param response response data - */ -static void -handle_reserve_age_withdraw_blinded_finished ( - void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh = cls; - const json_t *j_response = response; - struct TALER_EXCHANGE_AgeWithdrawBlindedResponse awbr = { - .hr.reply = j_response, - .hr.http_status = (unsigned int) response_code - }; - - awbh->job = NULL; - switch (response_code) - { - case 0: - awbr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - reserve_age_withdraw_ok (awbh, - j_response)) - { - GNUNET_break_op (0); - awbr.hr.http_status = 0; - awbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - GNUNET_assert (NULL == awbh->callback); - TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); - return; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_FORBIDDEN: - GNUNET_break_op (0); - /* Nothing really to verify, exchange says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the exchange basically just says - that it doesn't know this reserve. Can happen if we - query before the wire transfer went through. - We should simply pass the JSON reply to the application. */ - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_CONFLICT: - /* The age requirements might not have been met */ - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_GONE: - /* could happen if denomination was revoked */ - /* Note: one might want to check /keys for revocation - signature here, alas tricky in case our /keys - is outdated => left to clients */ - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - /* only validate reply is well-formed */ - { - uint64_t ptu; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("requirement_row", - &ptu), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j_response, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - awbr.hr.http_status = 0; - awbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - awbr.hr.ec = TALER_JSON_get_error_code (j_response); - awbr.hr.hint = TALER_JSON_get_error_hint (j_response); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange age-withdraw\n", - (unsigned int) response_code, - (int) awbr.hr.ec); - break; - } - awbh->callback (awbh->callback_cls, - &awbr); - TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); -} - - -/** - * Runs the actual age-withdraw operation with the blinded planchets. - * - * @param[in,out] awbh age withdraw handler - */ -static void -perform_protocol ( - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh) -{ -#define FAIL_IF(cond) \ - do { \ - if ((cond)) \ - { \ - GNUNET_break (! (cond)); \ - goto ERROR; \ - } \ - } while(0) - - struct GNUNET_HashContext *coins_hctx; - json_t *j_denoms = NULL; - json_t *j_array_candidates = NULL; - json_t *j_request_body = NULL; - CURL *curlh = NULL; - - GNUNET_assert (0 < awbh->num_input); - awbh->age_mask = awbh->blinded_input[0].denom_pub->key.age_mask; - - FAIL_IF (GNUNET_OK != - TALER_amount_set_zero (awbh->keys->currency, - &awbh->amount_with_fee)); - /* Accumulate total value with fees */ - for (size_t i = 0; i < awbh->num_input; i++) - { - struct TALER_Amount coin_total; - const struct TALER_EXCHANGE_DenomPublicKey *dpub = - awbh->blinded_input[i].denom_pub; - - FAIL_IF (0 > - TALER_amount_add (&coin_total, - &dpub->fees.withdraw, - &dpub->value)); - FAIL_IF (0 > - TALER_amount_add (&awbh->amount_with_fee, - &awbh->amount_with_fee, - &coin_total)); - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Attempting to age-withdraw from reserve %s with maximum age %d\n", - TALER_B2S (&awbh->reserve_pub), - awbh->max_age); - - coins_hctx = GNUNET_CRYPTO_hash_context_start (); - FAIL_IF (NULL == coins_hctx); - - - j_denoms = json_array (); - j_array_candidates = json_array (); - FAIL_IF ((NULL == j_denoms) || - (NULL == j_array_candidates)); - - for (size_t i = 0; i< awbh->num_input; i++) - { - /* Build the denomination array */ - { - const struct TALER_EXCHANGE_DenomPublicKey *denom_pub = - awbh->blinded_input[i].denom_pub; - const struct TALER_DenominationHashP *denom_h = &denom_pub->h_key; - json_t *jdenom; - - /* The mask must be the same for all coins */ - FAIL_IF (awbh->age_mask.bits != denom_pub->key.age_mask.bits); - - jdenom = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto (NULL, - denom_h)); - FAIL_IF (NULL == jdenom); - FAIL_IF (0 < json_array_append_new (j_denoms, - jdenom)); - - /* Build the candidate array */ - { - json_t *j_can = json_array (); - FAIL_IF (NULL == j_can); - - for (size_t k = 0; k < TALER_CNC_KAPPA; k++) - { - struct TALER_BlindedCoinHashP bch; - const struct TALER_PlanchetDetail *planchet = - &awbh->blinded_input[i].planchet_details[k]; - json_t *jc = GNUNET_JSON_PACK ( - TALER_JSON_pack_blinded_planchet ( - NULL, - &planchet->blinded_planchet)); - - FAIL_IF (NULL == jc); - FAIL_IF (0 < json_array_append_new (j_can, - jc)); - - TALER_coin_ev_hash (&planchet->blinded_planchet, - &planchet->denom_pub_hash, - &bch); - - GNUNET_CRYPTO_hash_context_read (coins_hctx, - &bch, - sizeof(bch)); - } - - FAIL_IF (0 < json_array_append_new (j_array_candidates, - j_can)); - } - } - } - - /* Build the hash of the commitment */ - GNUNET_CRYPTO_hash_context_finish (coins_hctx, - &awbh->h_commitment.hash); - - /* Sign the request */ - TALER_wallet_age_withdraw_sign (&awbh->h_commitment, - &awbh->amount_with_fee, - &awbh->age_mask, - awbh->max_age, - awbh->reserve_priv, - &awbh->reserve_sig); - - /* Initiate the POST-request */ - j_request_body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("denom_hs", j_denoms), - GNUNET_JSON_pack_array_steal ("blinded_coin_evs", j_array_candidates), - GNUNET_JSON_pack_uint64 ("max_age", awbh->max_age), - GNUNET_JSON_pack_data_auto ("reserve_sig", &awbh->reserve_sig)); - FAIL_IF (NULL == j_request_body); - - curlh = TALER_EXCHANGE_curl_easy_get_ (awbh->request_url); - FAIL_IF (NULL == curlh); - FAIL_IF (GNUNET_OK != - TALER_curl_easy_post (&awbh->post_ctx, - curlh, - j_request_body)); - json_decref (j_request_body); - j_request_body = NULL; - - awbh->job = GNUNET_CURL_job_add2 ( - awbh->curl_ctx, - curlh, - awbh->post_ctx.headers, - &handle_reserve_age_withdraw_blinded_finished, - awbh); - FAIL_IF (NULL == awbh->job); - - /* No errors, return */ - return; - -ERROR: - if (NULL != j_denoms) - json_decref (j_denoms); - if (NULL != j_array_candidates) - json_decref (j_array_candidates); - if (NULL != j_request_body) - json_decref (j_request_body); - if (NULL != curlh) - curl_easy_cleanup (curlh); - TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); - return; -#undef FAIL_IF -} - - -/** - * @brief Callback to copy the results from the call to TALER_age_withdraw_blinded - * to the result for the originating call from TALER_age_withdraw. - * - * @param cls struct TALER_AgeWithdrawHandle - * @param awbr The response - */ -static void -copy_results ( - void *cls, - const struct TALER_EXCHANGE_AgeWithdrawBlindedResponse *awbr) -{ - struct TALER_EXCHANGE_AgeWithdrawHandle *awh = cls; - uint8_t k = awbr->details.ok.noreveal_index; - struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details[awh->num_coins]; - struct TALER_BlindedCoinHashP blinded_coin_hs[awh->num_coins]; - struct TALER_EXCHANGE_AgeWithdrawResponse resp = { - .hr = awbr->hr, - .details = { - .ok = { .noreveal_index = awbr->details.ok.noreveal_index, - .h_commitment = awbr->details.ok.h_commitment, - .exchange_pub = awbr->details.ok.exchange_pub, - .num_coins = awh->num_coins, - .coin_details = details, - .blinded_coin_hs = blinded_coin_hs}, - }, - }; - - for (size_t n = 0; n< awh->num_coins; n++) - { - details[n] = awh->coin_data[n].coin_candidates[k].details; - details[n].planchet = awh->coin_data[n].planchet_details[k]; - blinded_coin_hs[n] = awh->coin_data[n].coin_candidates[k].blinded_coin_h; - } - - awh->callback (awh->callback_cls, - &resp); - - awh->callback = NULL; -} - - -/** - * @brief Prepares and executes TALER_EXCHANGE_age_withdraw_blinded. - * If there were CS-denominations involved, started once the all calls - * to /csr-withdraw are done. - */ -static void -call_age_withdraw_blinded ( - struct TALER_EXCHANGE_AgeWithdrawHandle *awh) -{ - struct TALER_EXCHANGE_AgeWithdrawBlindedInput blinded_input[awh->num_coins]; - - /* Prepare the blinded planchets as input */ - for (size_t n = 0; n < awh->num_coins; n++) - { - blinded_input[n].denom_pub = &awh->coin_data[n].denom_pub; - for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) - blinded_input[n].planchet_details[k] = - awh->coin_data[n].planchet_details[k]; - } - - awh->procotol_handle = - TALER_EXCHANGE_age_withdraw_blinded ( - awh->curl_ctx, - awh->keys, - awh->exchange_url, - awh->reserve_priv, - awh->max_age, - awh->num_coins, - blinded_input, - copy_results, - awh); -} - - -/** - * Prepares the request URL for the age-withdraw request - * - * @param awbh The handler - * @param exchange_url The base-URL to the exchange - */ -static -enum GNUNET_GenericReturnValue -prepare_url ( - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh, - const char *exchange_url) -{ - char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32]; - char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &awbh->reserve_pub, - sizeof (awbh->reserve_pub), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "reserves/%s/age-withdraw", - pub_str); - - awbh->request_url = TALER_url_join (exchange_url, - arg_str, - NULL); - if (NULL == awbh->request_url) - { - GNUNET_break (0); - TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); - return GNUNET_SYSERR; - } - - return GNUNET_OK; -} - - -/** - * @brief Function called when CSR withdraw retrieval is finished - * - * @param cls the `struct CSRClosure *` - * @param csrr replies from the /csr-withdraw request - */ -static void -csr_withdraw_done ( - void *cls, - const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr) -{ - struct CSRClosure *csr = cls; - struct CoinCandidate *can; - struct TALER_PlanchetDetail *planchet; - struct TALER_EXCHANGE_AgeWithdrawHandle *awh; - - GNUNET_assert (NULL != csr); - awh = csr->age_withdraw_handle; - planchet = csr->planchet; - can = csr->candidate; - - GNUNET_assert (NULL != can); - GNUNET_assert (NULL != planchet); - GNUNET_assert (NULL != awh); - - csr->csr_withdraw_handle = NULL; - - switch (csrr->hr.http_status) - { - case MHD_HTTP_OK: - { - bool success = false; - /* Complete the initialization of the coin with CS denomination */ - can->details.alg_values = csrr->details.ok.alg_values; - GNUNET_assert (can->details.alg_values.cipher - == TALER_DENOMINATION_CS); - TALER_planchet_setup_coin_priv (&can->secret, - &can->details.alg_values, - &can->details.coin_priv); - TALER_planchet_blinding_secret_create (&can->secret, - &can->details.alg_values, - &can->details.blinding_key); - /* This initializes the 2nd half of the - can->planchet_detail.blinded_planchet! */ - do { - if (GNUNET_OK != - TALER_planchet_prepare (&csr->denom_pub->key, - &can->details.alg_values, - &can->details.blinding_key, - &can->details.coin_priv, - &can->details.h_age_commitment, - &can->details.h_coin_pub, - planchet)) - { - GNUNET_break (0); - TALER_EXCHANGE_age_withdraw_cancel (awh); - break; - } - - if (GNUNET_OK != - TALER_coin_ev_hash (&planchet->blinded_planchet, - &planchet->denom_pub_hash, - &can->blinded_coin_h)) - { - GNUNET_break (0); - TALER_EXCHANGE_age_withdraw_cancel (awh); - break; - } - success = true; - } while(0); - - awh->csr.pending--; - - /* No more pending requests to /csr-withdraw, we can now perform the - * actual age-withdraw operation */ - if (0 == awh->csr.pending && success) - call_age_withdraw_blinded (awh); - return; - } - default: - break; - } - - TALER_EXCHANGE_age_withdraw_cancel (awh); -} - - -/** - * @brief Prepare the coins for the call to age-withdraw and calculates - * the total amount with fees. - * - * For denomination with CS as cipher, initiates the preflight to retrieve the - * csr-parameter via /csr-withdraw. - * - * @param awh The handler to the age-withdraw - * @param num_coins The number of coins in @e coin_inputs - * @param coin_inputs The input for the individual coin(-candidates) - * @return GNUNET_OK on success, GNUNET_SYSERR on failure - */ -static -enum GNUNET_GenericReturnValue -prepare_coins ( - struct TALER_EXCHANGE_AgeWithdrawHandle *awh, - size_t num_coins, - const struct TALER_EXCHANGE_AgeWithdrawCoinInput coin_inputs[ - static num_coins]) -{ -#define FAIL_IF(cond) \ - do { \ - if ((cond)) \ - { \ - GNUNET_break (! (cond)); \ - goto ERROR; \ - } \ - } while(0) - - GNUNET_assert (0 < num_coins); - awh->age_mask = coin_inputs[0].denom_pub->key.age_mask; - - awh->coin_data = GNUNET_new_array (awh->num_coins, - struct CoinData); - - for (size_t i = 0; i < num_coins; i++) - { - struct CoinData *cd = &awh->coin_data[i]; - const struct TALER_EXCHANGE_AgeWithdrawCoinInput *input = &coin_inputs[i]; - cd->denom_pub = *input->denom_pub; - - /* The mask must be the same for all coins */ - FAIL_IF (awh->age_mask.bits != input->denom_pub->key.age_mask.bits); - - TALER_denom_pub_deep_copy (&cd->denom_pub.key, - &input->denom_pub->key); - - for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) - { - struct CoinCandidate *can = &cd->coin_candidates[k]; - struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; - - can->secret = input->secrets[k]; - /* Derive the age restriction from the given secret and - * the maximum age */ - TALER_age_restriction_from_secret ( - &can->secret, - &input->denom_pub->key.age_mask, - awh->max_age, - &can->details.age_commitment_proof); - - TALER_age_commitment_hash (&can->details.age_commitment_proof.commitment, - &can->details.h_age_commitment); - - switch (input->denom_pub->key.cipher) - { - case TALER_DENOMINATION_RSA: - { - can->details.alg_values.cipher = TALER_DENOMINATION_RSA; - TALER_planchet_setup_coin_priv (&can->secret, - &can->details.alg_values, - &can->details.coin_priv); - TALER_planchet_blinding_secret_create (&can->secret, - &can->details.alg_values, - &can->details.blinding_key); - FAIL_IF (GNUNET_OK != - TALER_planchet_prepare (&cd->denom_pub.key, - &can->details.alg_values, - &can->details.blinding_key, - &can->details.coin_priv, - &can->details.h_age_commitment, - &can->details.h_coin_pub, - planchet)); - FAIL_IF (GNUNET_OK != - TALER_coin_ev_hash (&planchet->blinded_planchet, - &planchet->denom_pub_hash, - &can->blinded_coin_h)); - break; - } - case TALER_DENOMINATION_CS: - { - can->details.alg_values.cipher = TALER_DENOMINATION_CS; - - struct CSRClosure *cls = &cd->csr_cls[k]; - /** - * Save the handler and the denomination for the callback - * after the call to csr-withdraw */ - cls->age_withdraw_handle = awh; - cls->candidate = can; - cls->planchet = planchet; - cls->denom_pub = &cd->denom_pub; - - TALER_cs_withdraw_nonce_derive ( - &can->secret, - &planchet->blinded_planchet.details.cs_blinded_planchet.nonce); - - /* Note that we only initialize the first half - of the blinded_planchet here; the other part - will be done after the /csr-withdraw request! */ - planchet->blinded_planchet.cipher = TALER_DENOMINATION_CS; - cls->csr_withdraw_handle = - TALER_EXCHANGE_csr_withdraw ( - awh->curl_ctx, - awh->exchange_url, - &cd->denom_pub, - &planchet->blinded_planchet.details.cs_blinded_planchet.nonce, - &csr_withdraw_done, - cls); - FAIL_IF (NULL == cls->csr_withdraw_handle); - - awh->csr.pending++; - break; - } - default: - FAIL_IF (1); - } - } - } - return GNUNET_OK; - -ERROR: - TALER_EXCHANGE_age_withdraw_cancel (awh); - return GNUNET_SYSERR; -#undef FAIL_IF -}; - -struct TALER_EXCHANGE_AgeWithdrawHandle * -TALER_EXCHANGE_age_withdraw ( - struct GNUNET_CURL_Context *curl_ctx, - struct TALER_EXCHANGE_Keys *keys, - const char *exchange_url, - const struct TALER_ReservePrivateKeyP *reserve_priv, - size_t num_coins, - const struct TALER_EXCHANGE_AgeWithdrawCoinInput coin_inputs[const static - num_coins], - uint8_t max_age, - TALER_EXCHANGE_AgeWithdrawCallback res_cb, - void *res_cb_cls) -{ - struct TALER_EXCHANGE_AgeWithdrawHandle *awh; - - awh = GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawHandle); - awh->exchange_url = exchange_url; - awh->keys = TALER_EXCHANGE_keys_incref (keys); - awh->curl_ctx = curl_ctx; - awh->reserve_priv = reserve_priv; - awh->callback = res_cb; - awh->callback_cls = res_cb_cls; - awh->num_coins = num_coins; - awh->max_age = max_age; - - - if (GNUNET_OK != prepare_coins (awh, - num_coins, - coin_inputs)) - return NULL; - - /* If there were no CS denominations, we can now perform the actual - * age-withdraw protocol. Otherwise, there are calls to /csr-withdraw - * in flight and once they finish, the age-withdraw-protocol will be - * called from within the csr_withdraw_done-function. - */ - if (0 == awh->csr.pending) - call_age_withdraw_blinded (awh); - - return awh; -} - - -void -TALER_EXCHANGE_age_withdraw_cancel ( - struct TALER_EXCHANGE_AgeWithdrawHandle *awh) -{ - /* Cleanup coin data */ - for (unsigned int i = 0; inum_coins; i++) - { - struct CoinData *cd = &awh->coin_data[i]; - - for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) - { - struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; - struct CSRClosure *cls = &cd->csr_cls[k]; - - if (NULL != cls->csr_withdraw_handle) - { - TALER_EXCHANGE_csr_withdraw_cancel (cls->csr_withdraw_handle); - cls->csr_withdraw_handle = NULL; - } - TALER_blinded_planchet_free (&planchet->blinded_planchet); - } - TALER_denom_pub_free (&cd->denom_pub.key); - } - GNUNET_free (awh->coin_data); - TALER_EXCHANGE_keys_decref (awh->keys); - TALER_EXCHANGE_age_withdraw_blinded_cancel (awh->procotol_handle); - awh->procotol_handle = NULL; - GNUNET_free (awh); -} - - -struct TALER_EXCHANGE_AgeWithdrawBlindedHandle * -TALER_EXCHANGE_age_withdraw_blinded ( - struct GNUNET_CURL_Context *curl_ctx, - struct TALER_EXCHANGE_Keys *keys, - const char *exchange_url, - const struct TALER_ReservePrivateKeyP *reserve_priv, - uint8_t max_age, - unsigned int num_input, - const struct TALER_EXCHANGE_AgeWithdrawBlindedInput blinded_input[static - num_input], - TALER_EXCHANGE_AgeWithdrawBlindedCallback res_cb, - void *res_cb_cls) -{ - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh = - GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawBlindedHandle); - - awbh->num_input = num_input; - awbh->blinded_input = blinded_input; - awbh->keys = TALER_EXCHANGE_keys_incref (keys); - awbh->curl_ctx = curl_ctx; - awbh->reserve_priv = reserve_priv; - awbh->callback = res_cb; - awbh->callback_cls = res_cb_cls; - awbh->max_age = max_age; - - GNUNET_CRYPTO_eddsa_key_get_public (&awbh->reserve_priv->eddsa_priv, - &awbh->reserve_pub.eddsa_pub); - - if (GNUNET_OK != prepare_url (awbh, - exchange_url)) - return NULL; - - perform_protocol (awbh); - return awbh; -} - - -void -TALER_EXCHANGE_age_withdraw_blinded_cancel ( - struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh) -{ - if (NULL == awbh) - return; - - if (NULL != awbh->job) - { - GNUNET_CURL_job_cancel (awbh->job); - awbh->job = NULL; - } - GNUNET_free (awbh->request_url); - TALER_EXCHANGE_keys_decref (awbh->keys); - TALER_curl_easy_post_finished (&awbh->post_ctx); - GNUNET_free (awbh); -} - - -/* exchange_api_age_withdraw.c */ diff --git a/src/lib/exchange_api_age_withdraw_reveal.c b/src/lib/exchange_api_age_withdraw_reveal.c deleted file mode 100644 index a448d109d..000000000 --- a/src/lib/exchange_api_age_withdraw_reveal.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_age_withdraw_reveal.c - * @brief Implementation of /age-withdraw/$ACH/reveal requests - * @author Özgür Kesim - */ - -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_curl_lib.h" -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_common.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - -/** - * Handler for a running age-withdraw-reveal request - */ -struct TALER_EXCHANGE_AgeWithdrawRevealHandle -{ - - /* The index not to be disclosed */ - uint8_t noreveal_index; - - /* The age-withdraw commitment */ - struct TALER_AgeWithdrawCommitmentHashP h_commitment; - - /* The reserve's public key */ - const struct TALER_ReservePublicKeyP *reserve_pub; - - /* Number of coins */ - size_t num_coins; - - /* The @e num_coins * kappa coin secrets from the age-withdraw commitment */ - const struct TALER_EXCHANGE_AgeWithdrawCoinInput *coins_input; - - /* The url for the reveal request */ - const char *request_url; - - /** - * CURL handle for the request job. - */ - struct GNUNET_CURL_Job *job; - - /** - * Post Context - */ - struct TALER_CURL_PostContext post_ctx; - - /* Callback */ - TALER_EXCHANGE_AgeWithdrawRevealCallback callback; - - /* Reveal */ - void *callback_cls; -}; - - -/** - * We got a 200 OK response for the /age-withdraw/$ACH/reveal operation. - * Extract the signed blindedcoins and return it to the caller. - * - * @param awrh operation handle - * @param j_response reply from the exchange - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -age_withdraw_reveal_ok ( - struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh, - const json_t *j_response) -{ - struct TALER_EXCHANGE_AgeWithdrawRevealResponse response = { - .hr.reply = j_response, - .hr.http_status = MHD_HTTP_OK, - }; - const json_t *j_sigs; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("ev_sigs", - &j_sigs), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != GNUNET_JSON_parse (j_response, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (awrh->num_coins != json_array_size (j_sigs)) - { - /* Number of coins generated does not match our expectation */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - { - struct TALER_BlindedDenominationSignature denom_sigs[awrh->num_coins]; - json_t *j_sig; - size_t n; - - /* Reconstruct the coins and unblind the signatures */ - json_array_foreach (j_sigs, n, j_sig) - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_blinded_denom_sig (NULL, - &denom_sigs[n]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != GNUNET_JSON_parse (j_sig, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - } - - response.details.ok.num_sigs = awrh->num_coins; - response.details.ok.blinded_denom_sigs = denom_sigs; - awrh->callback (awrh->callback_cls, - &response); - /* Make sure the callback isn't called again */ - awrh->callback = NULL; - } - - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /age-withdraw/$ACH/reveal request. - * - * @param cls the `struct TALER_EXCHANGE_AgeWithdrawRevealHandle` - * @param response_code The HTTP response code - * @param response response data - */ -static void -handle_age_withdraw_reveal_finished ( - void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh = cls; - const json_t *j_response = response; - struct TALER_EXCHANGE_AgeWithdrawRevealResponse awr = { - .hr.reply = j_response, - .hr.http_status = (unsigned int) response_code - }; - - awrh->job = NULL; - /* FIXME[oec]: Only handle response-codes that are in the spec */ - switch (response_code) - { - case 0: - awr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - enum GNUNET_GenericReturnValue ret; - - ret = age_withdraw_reveal_ok (awrh, - j_response); - if (GNUNET_OK != ret) - { - GNUNET_break_op (0); - awr.hr.http_status = 0; - awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - GNUNET_assert (NULL == awrh->callback); - TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); - return; - } - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - /* only validate reply is well-formed */ - { - uint64_t ptu; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("legitimization_uuid", - &ptu), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j_response, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - awr.hr.http_status = 0; - awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_FORBIDDEN: - GNUNET_break_op (0); - /** - * This should never happen, as we don't sent any signatures - * to the exchange to verify. We should simply pass the JSON reply - * to the application - **/ - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the exchange basically just says - that it doesn't know this age-withdraw commitment. */ - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_CONFLICT: - /* An age commitment for one of the coins did not fulfill - * the required maximum age requirement of the corresponding - * reserve. - * Error code: TALER_EC_EXCHANGE_GENERIC_COIN_AGE_REQUIREMENT_FAILURE. - */ - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_GONE: - /* could happen if denomination was revoked */ - /* Note: one might want to check /keys for revocation - signature here, alas tricky in case our /keys - is outdated => left to clients */ - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - awr.hr.ec = TALER_JSON_get_error_code (j_response); - awr.hr.hint = TALER_JSON_get_error_hint (j_response); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange age-withdraw\n", - (unsigned int) response_code, - (int) awr.hr.ec); - break; - } - awrh->callback (awrh->callback_cls, - &awr); - TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); -} - - -/** - * Prepares the request URL for the age-withdraw-reveal request - * - * @param exchange_url The base-URL to the exchange - * @param[in,out] awrh The handler - * @return GNUNET_OK on success, GNUNET_SYSERR otherwise - */ -static -enum GNUNET_GenericReturnValue -prepare_url ( - const char *exchange_url, - struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh) -{ - char arg_str[sizeof (struct TALER_AgeWithdrawCommitmentHashP) * 2 + 32]; - char pub_str[sizeof (struct TALER_AgeWithdrawCommitmentHashP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string (&awrh->h_commitment, - sizeof (awrh->h_commitment), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "age-withdraw/%s/reveal", - pub_str); - - awrh->request_url = TALER_url_join (exchange_url, - arg_str, - NULL); - if (NULL == awrh->request_url) - { - GNUNET_break (0); - TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); - return GNUNET_SYSERR; - } - - return GNUNET_OK; -} - - -/** - * Call /age-withdraw/$ACH/reveal - * - * @param curl_ctx The context for CURL - * @param awrh The handler - */ -static -void -perform_protocol ( - struct GNUNET_CURL_Context *curl_ctx, - struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh) -{ - CURL *curlh = NULL; - json_t *j_request_body = NULL; - json_t *j_array_of_secrets = NULL; - json_t *j_secrets = NULL; - json_t *j_sec = NULL; - -#define FAIL_IF(cond) \ - do { \ - if ((cond)) \ - { \ - GNUNET_break (! (cond)); \ - goto ERROR; \ - } \ - } while(0) - - j_array_of_secrets = json_array (); - FAIL_IF (NULL == j_array_of_secrets); - - for (size_t n = 0; n < awrh->num_coins; n++) - { - const struct TALER_PlanchetMasterSecretP *secrets = - awrh->coins_input[n].secrets; - - j_secrets = json_array (); - FAIL_IF (NULL == j_secrets); - - for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) - { - const struct TALER_PlanchetMasterSecretP *secret = &secrets[k]; - if (awrh->noreveal_index == k) - continue; - - j_sec = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto (NULL, secret)); - - FAIL_IF (NULL == j_sec); - FAIL_IF (0 < json_array_append_new (j_secrets, - j_sec)); - } - - FAIL_IF (0 < json_array_append_new (j_array_of_secrets, - j_secrets)); - } - j_request_body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("reserve_pub", - awrh->reserve_pub), - GNUNET_JSON_pack_array_steal ("disclosed_coin_secrets", - j_array_of_secrets)); - FAIL_IF (NULL == j_request_body); - - curlh = TALER_EXCHANGE_curl_easy_get_ (awrh->request_url); - FAIL_IF (NULL == curlh); - FAIL_IF (GNUNET_OK != - TALER_curl_easy_post (&awrh->post_ctx, - curlh, - j_request_body)); - json_decref (j_request_body); - j_request_body = NULL; - - awrh->job = GNUNET_CURL_job_add2 (curl_ctx, - curlh, - awrh->post_ctx.headers, - &handle_age_withdraw_reveal_finished, - awrh); - FAIL_IF (NULL == awrh->job); - - /* No error, return */ - return; - -ERROR: - if (NULL != j_sec) - json_decref (j_sec); - if (NULL != j_secrets) - json_decref (j_secrets); - if (NULL != j_array_of_secrets) - json_decref (j_array_of_secrets); - if (NULL != j_request_body) - json_decref (j_request_body); - if (NULL != curlh) - curl_easy_cleanup (curlh); - TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); - return; -#undef FAIL_IF -} - - -struct TALER_EXCHANGE_AgeWithdrawRevealHandle * -TALER_EXCHANGE_age_withdraw_reveal ( - struct GNUNET_CURL_Context *curl_ctx, - const char *exchange_url, - size_t num_coins, - const struct TALER_EXCHANGE_AgeWithdrawCoinInput coins_input[static - num_coins], - uint8_t noreveal_index, - const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_EXCHANGE_AgeWithdrawRevealCallback reveal_cb, - void *reveal_cb_cls) -{ - struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh = - GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawRevealHandle); - awrh->noreveal_index = noreveal_index; - awrh->h_commitment = *h_commitment; - awrh->num_coins = num_coins; - awrh->coins_input = coins_input; - awrh->callback = reveal_cb; - awrh->callback_cls = reveal_cb_cls; - awrh->reserve_pub = reserve_pub; - - if (GNUNET_OK != - prepare_url (exchange_url, - awrh)) - return NULL; - - perform_protocol (curl_ctx, awrh); - - return awrh; -} - - -void -TALER_EXCHANGE_age_withdraw_reveal_cancel ( - struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh) -{ - if (NULL != awrh->job) - { - GNUNET_CURL_job_cancel (awrh->job); - awrh->job = NULL; - } - TALER_curl_easy_post_finished (&awrh->post_ctx); - /* FIXME[oec]: anything else left to cleanup!? */ - GNUNET_free (awrh); -} - - -/* exchange_api_age_withdraw_reveal.c */ diff --git a/src/lib/exchange_api_auditor_add_denomination.c b/src/lib/exchange_api_auditor_add_denomination.c deleted file mode 100644 index 89de0d7f1..000000000 --- a/src/lib/exchange_api_auditor_add_denomination.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_auditor_add_denomination.c - * @brief functions for the auditor to add its signature for denomination at the exchange - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "auditor_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_AuditorAddDenominationHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_AuditorAddDenominationCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /auditor/$AUDITOR_PUB/$H_DENOM_PUB request. - * - * @param cls the `struct TALER_EXCHANGE_AuditorAddDenominationHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_auditor_add_denomination_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah = cls; - const json_t *json = response; - struct TALER_EXCHANGE_AuditorAddDenominationResponse adr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ah->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_GONE: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_PRECONDITION_FAILED: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - if (NULL != json) - { - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange auditor-add-denomination at URL `%s'\n", - (unsigned int) response_code, - (int) adr.hr.ec, - ah->url); - } - else - { - adr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - adr.hr.hint = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected HTTP response code %u (no JSON returned) at URL `%s'\n", - (unsigned int) response_code, - ah->url); - } - break; - } - if (NULL != ah->cb) - { - ah->cb (ah->cb_cls, - &adr); - ah->cb = NULL; - } - TALER_EXCHANGE_add_auditor_denomination_cancel (ah); -} - - -struct TALER_EXCHANGE_AuditorAddDenominationHandle * -TALER_EXCHANGE_add_auditor_denomination ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_AuditorPublicKeyP *auditor_pub, - const struct TALER_AuditorSignatureP *auditor_sig, - TALER_EXCHANGE_AuditorAddDenominationCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah; - CURL *eh; - json_t *body; - - ah = GNUNET_new (struct TALER_EXCHANGE_AuditorAddDenominationHandle); - ah->cb = cb; - ah->cb_cls = cb_cls; - ah->ctx = ctx; - { - char apub_str[sizeof (*auditor_pub) * 2]; - char denom_str[sizeof (*h_denom_pub) * 2]; - char arg_str[sizeof (apub_str) + sizeof (denom_str) + 32]; - char *end; - - end = GNUNET_STRINGS_data_to_string (auditor_pub, - sizeof (*auditor_pub), - apub_str, - sizeof (apub_str)); - *end = '\0'; - end = GNUNET_STRINGS_data_to_string (h_denom_pub, - sizeof (*h_denom_pub), - denom_str, - sizeof (denom_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "auditors/%s/%s", - apub_str, - denom_str); - ah->url = TALER_url_join (url, - arg_str, - NULL); - } - if (NULL == ah->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ah); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("auditor_sig", - auditor_sig)); - eh = TALER_AUDITOR_curl_easy_get_ (ah->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ah->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (ah->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ah->url); - ah->job = GNUNET_CURL_job_add2 (ctx, - eh, - ah->post_ctx.headers, - &handle_auditor_add_denomination_finished, - ah); - if (NULL == ah->job) - { - TALER_EXCHANGE_add_auditor_denomination_cancel (ah); - return NULL; - } - return ah; -} - - -void -TALER_EXCHANGE_add_auditor_denomination_cancel ( - struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah) -{ - if (NULL != ah->job) - { - GNUNET_CURL_job_cancel (ah->job); - ah->job = NULL; - } - TALER_curl_easy_post_finished (&ah->post_ctx); - GNUNET_free (ah->url); - GNUNET_free (ah); -} diff --git a/src/lib/exchange_api_csr_melt.c b/src/lib/exchange_api_csr_melt.c deleted file mode 100644 index f59995af3..000000000 --- a/src/lib/exchange_api_csr_melt.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_csr_melt.c - * @brief Implementation of /csr-melt requests (get R in exchange used for Clause Schnorr refresh) - * @author Lucien Heuzeveldt - * @author Gian Demarmels - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A Clause Schnorr R Handle - */ -struct TALER_EXCHANGE_CsRMeltHandle -{ - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_CsRMeltCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext post_ctx; -}; - - -/** - * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation. - * Extract the coin's signature and return it to the caller. The signature we - * get from the exchange is for the blinded value. Thus, we first must - * unblind it and then should verify its validity against our coin's hash. - * - * If everything checks out, we return the unblinded signature - * to the application via the callback. - * - * @param csrh operation handle - * @param arr reply from the exchange - * @param hr http response details - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -csr_ok (struct TALER_EXCHANGE_CsRMeltHandle *csrh, - const json_t *arr, - struct TALER_EXCHANGE_HttpResponse *hr) -{ - unsigned int alen = json_array_size (arr); - struct TALER_ExchangeWithdrawValues alg_values[GNUNET_NZL (alen)]; - struct TALER_EXCHANGE_CsRMeltResponse csrr = { - .hr = *hr, - .details.ok.alg_values_len = alen, - .details.ok.alg_values = alg_values - }; - - for (unsigned int i = 0; icb (csrh->cb_cls, - &csrr); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the HTTP /csr request. - * - * @param cls the `struct TALER_EXCHANGE_CsRMeltHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_csr_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_CsRMeltHandle *csrh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = (unsigned int) response_code - }; - struct TALER_EXCHANGE_CsRMeltResponse csrr = { - .hr = hr - }; - - csrh->job = NULL; - switch (response_code) - { - case 0: - csrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - json_t *arr; - - arr = json_object_get (j, - "ewvs"); - if ( (NULL == arr) || - (0 == json_array_size (arr)) || - (GNUNET_OK != - csr_ok (csrh, - arr, - &hr)) ) - { - GNUNET_break_op (0); - csrr.hr.http_status = 0; - csrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - TALER_EXCHANGE_csr_melt_cancel (csrh); - return; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the exchange basically just says - that it doesn't know the /csr endpoint or denomination. - Can happen if the exchange doesn't support Clause Schnorr. - We should simply pass the JSON reply to the application. */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* could happen if denomination was revoked */ - /* Note: one might want to check /keys for revocation - signature here, alas tricky in case our /keys - is outdated => left to clients */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - csrr.hr.ec = TALER_JSON_get_error_code (j); - csrr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for CS R request\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - csrh->cb (csrh->cb_cls, - &csrr); - csrh->cb = NULL; - TALER_EXCHANGE_csr_melt_cancel (csrh); -} - - -struct TALER_EXCHANGE_CsRMeltHandle * -TALER_EXCHANGE_csr_melt ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_RefreshMasterSecretP *rms, - unsigned int nks_len, - struct TALER_EXCHANGE_NonceKey nks[static nks_len], - TALER_EXCHANGE_CsRMeltCallback res_cb, - void *res_cb_cls) -{ - struct TALER_EXCHANGE_CsRMeltHandle *csrh; - json_t *csr_arr; - - if (0 == nks_len) - { - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; ikey.cipher) - { - GNUNET_break (0); - return NULL; - } - csrh = GNUNET_new (struct TALER_EXCHANGE_CsRMeltHandle); - csrh->cb = res_cb; - csrh->cb_cls = res_cb_cls; - csr_arr = json_array (); - GNUNET_assert (NULL != csr_arr); - for (unsigned int i = 0; icnc_num), - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &nk->pk->h_key)); - GNUNET_assert (NULL != csr_obj); - GNUNET_assert (0 == - json_array_append_new (csr_arr, - csr_obj)); - } - csrh->url = TALER_url_join (url, - "csr-melt", - NULL); - if (NULL == csrh->url) - { - json_decref (csr_arr); - GNUNET_free (csrh); - return NULL; - } - { - CURL *eh; - json_t *req; - - req = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("rms", - rms), - GNUNET_JSON_pack_array_steal ("nks", - csr_arr)); - eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&csrh->post_ctx, - eh, - req)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (csrh->url); - GNUNET_free (csrh); - return NULL; - } - json_decref (req); - csrh->job = GNUNET_CURL_job_add2 (ctx, - eh, - csrh->post_ctx.headers, - &handle_csr_finished, - csrh); - } - return csrh; -} - - -void -TALER_EXCHANGE_csr_melt_cancel (struct TALER_EXCHANGE_CsRMeltHandle *csrh) -{ - if (NULL != csrh->job) - { - GNUNET_CURL_job_cancel (csrh->job); - csrh->job = NULL; - } - GNUNET_free (csrh->url); - TALER_curl_easy_post_finished (&csrh->post_ctx); - GNUNET_free (csrh); -} diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c deleted file mode 100644 index 373dd89a7..000000000 --- a/src/lib/exchange_api_kyc_check.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2021-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_kyc_check.c - * @brief Implementation of the /kyc-check request - * @author Christian Grothoff - */ -#include "platform.h" -#include /* just for HTTP check codes */ -#include -#include -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A ``/kyc-check`` handle - */ -struct TALER_EXCHANGE_KycCheckHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Keys of the exchange. - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_KycStatusCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Hash of the payto:// URL that is being KYC'ed. - */ - struct TALER_PaytoHashP h_payto; - -}; - - -/** - * Function called when we're done processing the - * HTTP /kyc-check request. - * - * @param cls the `struct TALER_EXCHANGE_KycCheckHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_kyc_check_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_KycCheckHandle *kch = cls; - const json_t *j = response; - struct TALER_EXCHANGE_KycStatus ks = { - .http_status = (unsigned int) response_code - }; - - kch->job = NULL; - switch (response_code) - { - case 0: - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - const json_t *kyc_details; - uint32_t status; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &ks.details.ok.exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &ks.details.ok.exchange_pub), - GNUNET_JSON_spec_timestamp ("now", - &ks.details.ok.timestamp), - GNUNET_JSON_spec_object_const ("kyc_details", - &kyc_details), - GNUNET_JSON_spec_uint32 ("aml_status", - &status), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - ks.details.ok.kyc_details = kyc_details; - ks.details.ok.aml_status - = (enum TALER_AmlDecisionState) status; - if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (kch->keys, - &ks.details.ok.exchange_pub)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); - break; - } - - if (GNUNET_OK != - TALER_exchange_online_account_setup_success_verify ( - &kch->h_payto, - ks.details.ok.kyc_details, - ks.details.ok.timestamp, - &ks.details.ok.exchange_pub, - &ks.details.ok.exchange_sig)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); - break; - } - kch->cb (kch->cb_cls, - &ks); - GNUNET_JSON_parse_free (spec); - TALER_EXCHANGE_kyc_check_cancel (kch); - return; - } - case MHD_HTTP_ACCEPTED: - { - uint32_t status; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("kyc_url", - &ks.details.accepted.kyc_url), - GNUNET_JSON_spec_uint32 ("aml_status", - &status), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - ks.details.accepted.aml_status - = (enum TALER_AmlDecisionState) status; - kch->cb (kch->cb_cls, - &ks); - GNUNET_JSON_parse_free (spec); - TALER_EXCHANGE_kyc_check_cancel (kch); - return; - } - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - ks.ec = TALER_JSON_get_error_code (j); - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - ks.ec = TALER_JSON_get_error_code (j); - break; - case MHD_HTTP_NOT_FOUND: - ks.ec = TALER_JSON_get_error_code (j); - break; - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - { - uint32_t status; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint32 ("aml_status", - &status), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - ks.details.unavailable_for_legal_reasons.aml_status - = (enum TALER_AmlDecisionState) status; - kch->cb (kch->cb_cls, - &ks); - GNUNET_JSON_parse_free (spec); - TALER_EXCHANGE_kyc_check_cancel (kch); - return; - } - case MHD_HTTP_INTERNAL_SERVER_ERROR: - ks.ec = TALER_JSON_get_error_code (j); - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - ks.ec = TALER_JSON_get_error_code (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange kyc_check\n", - (unsigned int) response_code, - (int) ks.ec); - break; - } - kch->cb (kch->cb_cls, - &ks); - TALER_EXCHANGE_kyc_check_cancel (kch); -} - - -struct TALER_EXCHANGE_KycCheckHandle * -TALER_EXCHANGE_kyc_check ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - uint64_t requirement_row, - const struct TALER_PaytoHashP *h_payto, - enum TALER_KYCLOGIC_KycUserType ut, - struct GNUNET_TIME_Relative timeout, - TALER_EXCHANGE_KycStatusCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_KycCheckHandle *kch; - CURL *eh; - char *arg_str; - - { - char payto_str[sizeof (*h_payto) * 2]; - char *end; - unsigned long long timeout_ms; - - end = GNUNET_STRINGS_data_to_string ( - h_payto, - sizeof (*h_payto), - payto_str, - sizeof (payto_str) - 1); - *end = '\0'; - timeout_ms = timeout.rel_value_us - / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; - GNUNET_asprintf (&arg_str, - "kyc-check/%llu/%s/%s?timeout_ms=%llu", - (unsigned long long) requirement_row, - payto_str, - TALER_KYCLOGIC_kyc_user_type2s (ut), - timeout_ms); - } - kch = GNUNET_new (struct TALER_EXCHANGE_KycCheckHandle); - kch->h_payto = *h_payto; - kch->cb = cb; - kch->cb_cls = cb_cls; - kch->url = TALER_url_join (url, - arg_str, - NULL); - GNUNET_free (arg_str); - if (NULL == kch->url) - { - GNUNET_free (kch); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (kch->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (kch->url); - GNUNET_free (kch); - return NULL; - } - kch->keys = TALER_EXCHANGE_keys_incref (keys); - kch->job = GNUNET_CURL_job_add_with_ct_json (ctx, - eh, - &handle_kyc_check_finished, - kch); - return kch; -} - - -void -TALER_EXCHANGE_kyc_check_cancel (struct TALER_EXCHANGE_KycCheckHandle *kch) -{ - if (NULL != kch->job) - { - GNUNET_CURL_job_cancel (kch->job); - kch->job = NULL; - } - TALER_EXCHANGE_keys_decref (kch->keys); - GNUNET_free (kch->url); - GNUNET_free (kch); -} - - -/* end of exchange_api_kyc_check.c */ diff --git a/src/lib/exchange_api_kyc_proof.c b/src/lib/exchange_api_kyc_proof.c deleted file mode 100644 index e7cc9c4cf..000000000 --- a/src/lib/exchange_api_kyc_proof.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2021, 2022 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_kyc_proof.c - * @brief Implementation of the /kyc-proof request - * @author Christian Grothoff - */ -#include "platform.h" -#include /* just for HTTP proof codes */ -#include -#include -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A ``/kyc-proof`` handle - */ -struct TALER_EXCHANGE_KycProofHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle to our CURL request. - */ - CURL *eh; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_KycProofCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - -}; - - -/** - * Function called when we're done processing the - * HTTP /kyc-proof request. - * - * @param cls the `struct TALER_EXCHANGE_KycProofHandle` - * @param response_code HTTP response code, 0 on error - * @param body response body - * @param body_size number of bytes in @a body - */ -static void -handle_kyc_proof_finished (void *cls, - long response_code, - const void *body, - size_t body_size) -{ - struct TALER_EXCHANGE_KycProofHandle *kph = cls; - struct TALER_EXCHANGE_KycProofResponse kpr = { - .http_status = (unsigned int) response_code - }; - - (void) body; - (void) body_size; - kph->job = NULL; - switch (response_code) - { - case 0: - break; - case MHD_HTTP_SEE_OTHER: - { - char *redirect_url; - - GNUNET_assert (CURLE_OK == - curl_easy_getinfo (kph->eh, - CURLINFO_REDIRECT_URL, - &redirect_url)); - kpr.details.found.redirect_url = redirect_url; - break; - } - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_UNAUTHORIZED: - break; - case MHD_HTTP_FORBIDDEN: - break; - case MHD_HTTP_NOT_FOUND: - break; - case MHD_HTTP_BAD_GATEWAY: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - case MHD_HTTP_GATEWAY_TIMEOUT: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u for exchange kyc_proof\n", - (unsigned int) response_code); - break; - } - kph->cb (kph->cb_cls, - &kpr); - TALER_EXCHANGE_kyc_proof_cancel (kph); -} - - -struct TALER_EXCHANGE_KycProofHandle * -TALER_EXCHANGE_kyc_proof ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_PaytoHashP *h_payto, - const char *logic, - const char *args, - TALER_EXCHANGE_KycProofCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_KycProofHandle *kph; - char *arg_str; - - if (NULL == args) - args = ""; - else - GNUNET_assert (args[0] == '&'); - { - char hstr[sizeof (struct TALER_PaytoHashP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string (h_payto, - sizeof (*h_payto), - hstr, - sizeof (hstr)); - *end = '\0'; - GNUNET_asprintf (&arg_str, - "kyc-proof/%s?state=%s%s", - logic, - hstr, - args); - } - kph = GNUNET_new (struct TALER_EXCHANGE_KycProofHandle); - kph->cb = cb; - kph->cb_cls = cb_cls; - kph->url = TALER_url_join (url, - arg_str, - NULL); - GNUNET_free (arg_str); - if (NULL == kph->url) - { - GNUNET_free (kph); - return NULL; - } - kph->eh = TALER_EXCHANGE_curl_easy_get_ (kph->url); - if (NULL == kph->eh) - { - GNUNET_break (0); - GNUNET_free (kph->url); - GNUNET_free (kph); - return NULL; - } - /* disable location following, we want to learn the - result of a 303 redirect! */ - GNUNET_assert (CURLE_OK == - curl_easy_setopt (kph->eh, - CURLOPT_FOLLOWLOCATION, - 0L)); - kph->job = GNUNET_CURL_job_add_raw (ctx, - kph->eh, - NULL, - &handle_kyc_proof_finished, - kph); - return kph; -} - - -void -TALER_EXCHANGE_kyc_proof_cancel (struct TALER_EXCHANGE_KycProofHandle *kph) -{ - if (NULL != kph->job) - { - GNUNET_CURL_job_cancel (kph->job); - kph->job = NULL; - } - GNUNET_free (kph->url); - GNUNET_free (kph); -} - - -/* end of exchange_api_kyc_proof.c */ diff --git a/src/lib/exchange_api_kyc_wallet.c b/src/lib/exchange_api_kyc_wallet.c deleted file mode 100644 index 7197694ae..000000000 --- a/src/lib/exchange_api_kyc_wallet.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2021 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_kyc_wallet.c - * @brief Implementation of the /kyc-wallet request - * @author Christian Grothoff - */ -#include "platform.h" -#include /* just for HTTP wallet codes */ -#include -#include -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A ``/kyc-wallet`` handle - */ -struct TALER_EXCHANGE_KycWalletHandle -{ - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_KycWalletCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - -}; - - -/** - * Function called when we're done processing the - * HTTP /kyc-wallet request. - * - * @param cls the `struct TALER_EXCHANGE_KycWalletHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_kyc_wallet_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_KycWalletHandle *kwh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_WalletKycResponse ks = { - .http_status = (unsigned int) response_code - }; - - kwh->job = NULL; - switch (response_code) - { - case 0: - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - ks.ec = TALER_JSON_get_error_code (j); - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - ks.ec = TALER_JSON_get_error_code (j); - break; - case MHD_HTTP_NOT_FOUND: - ks.ec = TALER_JSON_get_error_code (j); - break; - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ( - "h_payto", - &ks.details.unavailable_for_legal_reasons.h_payto), - GNUNET_JSON_spec_uint64 ( - "requirement_row", - &ks.details.unavailable_for_legal_reasons.requirement_row), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - break; - } - case MHD_HTTP_INTERNAL_SERVER_ERROR: - ks.ec = TALER_JSON_get_error_code (j); - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - ks.ec = TALER_JSON_get_error_code (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange /kyc-wallet\n", - (unsigned int) response_code, - (int) ks.ec); - break; - } - kwh->cb (kwh->cb_cls, - &ks); - TALER_EXCHANGE_kyc_wallet_cancel (kwh); -} - - -struct TALER_EXCHANGE_KycWalletHandle * -TALER_EXCHANGE_kyc_wallet ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_Amount *balance, - TALER_EXCHANGE_KycWalletCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_KycWalletHandle *kwh; - CURL *eh; - json_t *req; - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_ReserveSignatureP reserve_sig; - - GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, - &reserve_pub.eddsa_pub); - TALER_wallet_account_setup_sign (reserve_priv, - balance, - &reserve_sig); - req = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("balance", - balance), - GNUNET_JSON_pack_data_auto ("reserve_pub", - &reserve_pub), - GNUNET_JSON_pack_data_auto ("reserve_sig", - &reserve_sig)); - GNUNET_assert (NULL != req); - kwh = GNUNET_new (struct TALER_EXCHANGE_KycWalletHandle); - kwh->cb = cb; - kwh->cb_cls = cb_cls; - kwh->url = TALER_url_join (url, - "kyc-wallet", - NULL); - if (NULL == kwh->url) - { - json_decref (req); - GNUNET_free (kwh); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (kwh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&kwh->ctx, - eh, - req)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (req); - GNUNET_free (kwh->url); - GNUNET_free (kwh); - return NULL; - } - json_decref (req); - kwh->job = GNUNET_CURL_job_add2 (ctx, - eh, - kwh->ctx.headers, - &handle_kyc_wallet_finished, - kwh); - return kwh; -} - - -void -TALER_EXCHANGE_kyc_wallet_cancel (struct TALER_EXCHANGE_KycWalletHandle *kwh) -{ - if (NULL != kwh->job) - { - GNUNET_CURL_job_cancel (kwh->job); - kwh->job = NULL; - } - GNUNET_free (kwh->url); - TALER_curl_easy_post_finished (&kwh->ctx); - GNUNET_free (kwh); -} - - -/* end of exchange_api_kyc_wallet.c */ diff --git a/src/lib/exchange_api_lookup_aml_decision.c b/src/lib/exchange_api_lookup_aml_decision.c deleted file mode 100644 index 01e98213b..000000000 --- a/src/lib/exchange_api_lookup_aml_decision.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_lookup_aml_decision.c - * @brief Implementation of the /aml/$OFFICER_PUB/decision request - * @author Christian Grothoff - */ -#include "platform.h" -#include /* just for HTTP status codes */ -#include -#include -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A /coins/$COIN_PUB/link Handle - */ -struct TALER_EXCHANGE_LookupAmlDecision -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_LookupAmlDecisionCallback decision_cb; - - /** - * Closure for @e cb. - */ - void *decision_cb_cls; - - /** - * HTTP headers for the job. - */ - struct curl_slist *job_headers; -}; - - -/** - * Parse AML decision history. - * - * @param aml_history JSON array with AML history - * @param[out] aml_history_ar where to write the result - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_aml_history (const json_t *aml_history, - struct TALER_EXCHANGE_AmlDecisionDetail *aml_history_ar) -{ - json_t *obj; - size_t idx; - - json_array_foreach (aml_history, idx, obj) - { - struct TALER_EXCHANGE_AmlDecisionDetail *aml = &aml_history_ar[idx]; - uint32_t state32; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("decision_time", - &aml->decision_time), - GNUNET_JSON_spec_string ("justification", - &aml->justification), - TALER_JSON_spec_amount_any ("new_threshold", - &aml->new_threshold), - GNUNET_JSON_spec_uint32 ("new_state", - &state32), - GNUNET_JSON_spec_fixed_auto ("decider_pub", - &aml->decider_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (obj, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - aml->new_state = (enum TALER_AmlDecisionState) state32; - } - return GNUNET_OK; -} - - -/** - * Parse KYC response array. - * - * @param kyc_attributes JSON array with KYC details - * @param[out] kyc_attributes_ar where to write the result - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_kyc_attributes (const json_t *kyc_attributes, - struct TALER_EXCHANGE_KycHistoryDetail *kyc_attributes_ar) -{ - json_t *obj; - size_t idx; - - json_array_foreach (kyc_attributes, idx, obj) - { - struct TALER_EXCHANGE_KycHistoryDetail *kyc = &kyc_attributes_ar[idx]; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("collection_time", - &kyc->collection_time), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("attributes", - &kyc->attributes), - NULL), - GNUNET_JSON_spec_string ("provider_section", - &kyc->provider_section), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (obj, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * Parse the provided decision data from the "200 OK" response. - * - * @param[in,out] lh handle (callback may be zero'ed out) - * @param json json reply with the data for one coin - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -parse_decision_ok (struct TALER_EXCHANGE_LookupAmlDecision *lh, - const json_t *json) -{ - struct TALER_EXCHANGE_AmlDecisionResponse lr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_OK - }; - const json_t *aml_history; - const json_t *kyc_attributes; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("aml_history", - &aml_history), - GNUNET_JSON_spec_array_const ("kyc_attributes", - &kyc_attributes), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - lr.details.ok.aml_history_length = json_array_size (aml_history); - lr.details.ok.kyc_attributes_length = json_array_size (kyc_attributes); - { - struct TALER_EXCHANGE_AmlDecisionDetail aml_history_ar[ - GNUNET_NZL (lr.details.ok.aml_history_length)]; - struct TALER_EXCHANGE_KycHistoryDetail kyc_attributes_ar[ - GNUNET_NZL (lr.details.ok.kyc_attributes_length)]; - enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; - - memset (aml_history_ar, - 0, - sizeof (aml_history_ar)); - memset (kyc_attributes_ar, - 0, - sizeof (kyc_attributes_ar)); - lr.details.ok.aml_history = aml_history_ar; - lr.details.ok.kyc_attributes = kyc_attributes_ar; - ret = parse_aml_history (aml_history, - aml_history_ar); - if (GNUNET_OK == ret) - ret = parse_kyc_attributes (kyc_attributes, - kyc_attributes_ar); - if (GNUNET_OK == ret) - { - lh->decision_cb (lh->decision_cb_cls, - &lr); - lh->decision_cb = NULL; - } - return ret; - } -} - - -/** - * Function called when we're done processing the - * HTTP /aml/$OFFICER_PUB/decision request. - * - * @param cls the `struct TALER_EXCHANGE_LookupAmlDecision` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_lookup_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_LookupAmlDecision *lh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_AmlDecisionResponse lr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - lh->job = NULL; - switch (response_code) - { - case 0: - lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - parse_decision_ok (lh, - j)) - { - GNUNET_break_op (0); - lr.hr.http_status = 0; - lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - GNUNET_assert (NULL == lh->decision_cb); - TALER_EXCHANGE_lookup_aml_decision_cancel (lh); - return; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* Nothing really to verify, exchange says this coin was not melted; we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* Nothing really to verify, exchange says this coin was not melted; we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange lookup AML decision\n", - (unsigned int) response_code, - (int) lr.hr.ec); - break; - } - if (NULL != lh->decision_cb) - lh->decision_cb (lh->decision_cb_cls, - &lr); - TALER_EXCHANGE_lookup_aml_decision_cancel (lh); -} - - -struct TALER_EXCHANGE_LookupAmlDecision * -TALER_EXCHANGE_lookup_aml_decision ( - struct GNUNET_CURL_Context *ctx, - const char *exchange_url, - const struct TALER_PaytoHashP *h_payto, - const struct TALER_AmlOfficerPrivateKeyP *officer_priv, - bool history, - TALER_EXCHANGE_LookupAmlDecisionCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_LookupAmlDecision *lh; - CURL *eh; - struct TALER_AmlOfficerPublicKeyP officer_pub; - struct TALER_AmlOfficerSignatureP officer_sig; - char arg_str[sizeof (officer_pub) * 2 - + sizeof (*h_payto) * 2 + 32]; - - GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, - &officer_pub.eddsa_pub); - TALER_officer_aml_query_sign (officer_priv, - &officer_sig); - { - char pub_str[sizeof (officer_pub) * 2]; - char pt_str[sizeof (*h_payto) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &officer_pub, - sizeof (officer_pub), - pub_str, - sizeof (pub_str)); - *end = '\0'; - end = GNUNET_STRINGS_data_to_string ( - h_payto, - sizeof (*h_payto), - pt_str, - sizeof (pt_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "aml/%s/decision/%s", - pub_str, - pt_str); - } - lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecision); - lh->decision_cb = cb; - lh->decision_cb_cls = cb_cls; - lh->url = TALER_url_join (exchange_url, - arg_str, - "history", - history - ? "true" - : NULL, - NULL); - if (NULL == lh->url) - { - GNUNET_free (lh); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (lh->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (lh->url); - GNUNET_free (lh); - return NULL; - } - { - char *hdr; - char sig_str[sizeof (officer_sig) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &officer_sig, - sizeof (officer_sig), - sig_str, - sizeof (sig_str)); - *end = '\0'; - - GNUNET_asprintf (&hdr, - "%s: %s", - TALER_AML_OFFICER_SIGNATURE_HEADER, - sig_str); - lh->job_headers = curl_slist_append (NULL, - hdr); - GNUNET_free (hdr); - lh->job_headers = curl_slist_append (lh->job_headers, - "Content-type: application/json"); - lh->job = GNUNET_CURL_job_add2 (ctx, - eh, - lh->job_headers, - &handle_lookup_finished, - lh); - } - return lh; -} - - -void -TALER_EXCHANGE_lookup_aml_decision_cancel ( - struct TALER_EXCHANGE_LookupAmlDecision *lh) -{ - if (NULL != lh->job) - { - GNUNET_CURL_job_cancel (lh->job); - lh->job = NULL; - } - curl_slist_free_all (lh->job_headers); - GNUNET_free (lh->url); - GNUNET_free (lh); -} - - -/* end of exchange_api_lookup_aml_decision.c */ diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c deleted file mode 100644 index 22222b1e4..000000000 --- a/src/lib/exchange_api_lookup_aml_decisions.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_lookup_aml_decisions.c - * @brief Implementation of the /aml/$OFFICER_PUB/decisions request - * @author Christian Grothoff - */ -#include "platform.h" -#include /* just for HTTP status codes */ -#include -#include -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A /coins/$COIN_PUB/link Handle - */ -struct TALER_EXCHANGE_LookupAmlDecisions -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_LookupAmlDecisionsCallback decisions_cb; - - /** - * Closure for @e cb. - */ - void *decisions_cb_cls; - - /** - * HTTP headers for the job. - */ - struct curl_slist *job_headers; -}; - - -/** - * Parse AML decision summary array. - * - * @param decisions JSON array with AML decision summaries - * @param[out] decision_ar where to write the result - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_aml_decisions (const json_t *decisions, - struct TALER_EXCHANGE_AmlDecisionSummary *decision_ar) -{ - json_t *obj; - size_t idx; - - json_array_foreach (decisions, idx, obj) - { - struct TALER_EXCHANGE_AmlDecisionSummary *decision = &decision_ar[idx]; - uint32_t state32; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("h_payto", - &decision->h_payto), - GNUNET_JSON_spec_uint32 ("current_state", - &state32), - TALER_JSON_spec_amount_any ("threshold", - &decision->threshold), - GNUNET_JSON_spec_uint64 ("rowid", - &decision->rowid), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (obj, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - decision->current_state = (enum TALER_AmlDecisionState) state32; - } - return GNUNET_OK; -} - - -/** - * Parse the provided decision data from the "200 OK" response. - * - * @param[in,out] lh handle (callback may be zero'ed out) - * @param json json reply with the data for one coin - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh, - const json_t *json) -{ - struct TALER_EXCHANGE_AmlDecisionsResponse lr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_OK - }; - const json_t *records; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("records", - &records), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - lr.details.ok.decisions_length = json_array_size (records); - { - struct TALER_EXCHANGE_AmlDecisionSummary decisions[ - GNUNET_NZL (lr.details.ok.decisions_length)]; - enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; - - lr.details.ok.decisions = decisions; - ret = parse_aml_decisions (records, - decisions); - if (GNUNET_OK == ret) - { - lh->decisions_cb (lh->decisions_cb_cls, - &lr); - lh->decisions_cb = NULL; - } - return ret; - } -} - - -/** - * Function called when we're done processing the - * HTTP /aml/$OFFICER_PUB/decisions request. - * - * @param cls the `struct TALER_EXCHANGE_LookupAmlDecisions` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_lookup_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_LookupAmlDecisions *lh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_AmlDecisionsResponse lr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - lh->job = NULL; - switch (response_code) - { - case 0: - lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - parse_decisions_ok (lh, - j)) - { - GNUNET_break_op (0); - lr.hr.http_status = 0; - lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - GNUNET_assert (NULL == lh->decisions_cb); - TALER_EXCHANGE_lookup_aml_decisions_cancel (lh); - return; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_BAD_REQUEST: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* Nothing really to verify, exchange says this coin was not melted; we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* Nothing really to verify, exchange says this coin was not melted; we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - lr.hr.ec = TALER_JSON_get_error_code (j); - lr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for lookup AML decisions\n", - (unsigned int) response_code, - (int) lr.hr.ec); - break; - } - if (NULL != lh->decisions_cb) - lh->decisions_cb (lh->decisions_cb_cls, - &lr); - TALER_EXCHANGE_lookup_aml_decisions_cancel (lh); -} - - -struct TALER_EXCHANGE_LookupAmlDecisions * -TALER_EXCHANGE_lookup_aml_decisions ( - struct GNUNET_CURL_Context *ctx, - const char *exchange_url, - uint64_t start, - int delta, - enum TALER_AmlDecisionState state, - const struct TALER_AmlOfficerPrivateKeyP *officer_priv, - TALER_EXCHANGE_LookupAmlDecisionsCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_LookupAmlDecisions *lh; - CURL *eh; - struct TALER_AmlOfficerPublicKeyP officer_pub; - struct TALER_AmlOfficerSignatureP officer_sig; - char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32]; - const char *state_str = NULL; - - switch (state) - { - case TALER_AML_NORMAL: - state_str = "normal"; - break; - case TALER_AML_PENDING: - state_str = "pending"; - break; - case TALER_AML_FROZEN: - state_str = "frozen"; - break; - } - GNUNET_assert (NULL != state_str); - GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, - &officer_pub.eddsa_pub); - TALER_officer_aml_query_sign (officer_priv, - &officer_sig); - { - char pub_str[sizeof (officer_pub) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &officer_pub, - sizeof (officer_pub), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "aml/%s/decisions/%s", - pub_str, - state_str); - } - lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecisions); - lh->decisions_cb = cb; - lh->decisions_cb_cls = cb_cls; - { - char delta_s[24]; - char start_s[24]; - - GNUNET_snprintf (delta_s, - sizeof (delta_s), - "%d", - delta); - GNUNET_snprintf (start_s, - sizeof (start_s), - "%llu", - (unsigned long long) start); - lh->url = TALER_url_join (exchange_url, - arg_str, - "delta", - delta_s, - "start", - start_s, - NULL); - } - if (NULL == lh->url) - { - GNUNET_free (lh); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (lh->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (lh->url); - GNUNET_free (lh); - return NULL; - } - { - char *hdr; - char sig_str[sizeof (officer_sig) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &officer_sig, - sizeof (officer_sig), - sig_str, - sizeof (sig_str)); - *end = '\0'; - - GNUNET_asprintf (&hdr, - "%s: %s", - TALER_AML_OFFICER_SIGNATURE_HEADER, - sig_str); - lh->job_headers = curl_slist_append (NULL, - hdr); - GNUNET_free (hdr); - lh->job_headers = curl_slist_append (lh->job_headers, - "Content-type: application/json"); - lh->job = GNUNET_CURL_job_add2 (ctx, - eh, - lh->job_headers, - &handle_lookup_finished, - lh); - } - return lh; -} - - -void -TALER_EXCHANGE_lookup_aml_decisions_cancel ( - struct TALER_EXCHANGE_LookupAmlDecisions *lh) -{ - if (NULL != lh->job) - { - GNUNET_CURL_job_cancel (lh->job); - lh->job = NULL; - } - curl_slist_free_all (lh->job_headers); - GNUNET_free (lh->url); - GNUNET_free (lh); -} - - -/* end of exchange_api_lookup_aml_decisions.c */ diff --git a/src/lib/exchange_api_management_add_partner.c b/src/lib/exchange_api_management_add_partner.c deleted file mode 100644 index fec66c567..000000000 --- a/src/lib/exchange_api_management_add_partner.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_add_partner.c - * @brief functions to add an partner by an AML officer - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementAddPartner -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementAddPartnerCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /management/partners request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAddPartner *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_add_partner_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementAddPartner *wh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementAddPartnerResponse apr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - wh->job = NULL; - switch (response_code) - { - case 0: - /* no reply */ - apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - apr.hr.hint = "server offline?"; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - apr.hr.ec = TALER_JSON_get_error_code (json); - apr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for adding exchange partner\n", - (unsigned int) response_code, - (int) apr.hr.ec); - break; - } - if (NULL != wh->cb) - { - wh->cb (wh->cb_cls, - &apr); - wh->cb = NULL; - } - TALER_EXCHANGE_management_add_partner_cancel (wh); -} - - -struct TALER_EXCHANGE_ManagementAddPartner * -TALER_EXCHANGE_management_add_partner ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_MasterPublicKeyP *partner_pub, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp end_date, - struct GNUNET_TIME_Relative wad_frequency, - const struct TALER_Amount *wad_fee, - const char *partner_base_url, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementAddPartnerCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementAddPartner *wh; - CURL *eh; - json_t *body; - - wh = GNUNET_new (struct TALER_EXCHANGE_ManagementAddPartner); - wh->cb = cb; - wh->cb_cls = cb_cls; - wh->ctx = ctx; - wh->url = TALER_url_join (url, - "management/partners", - NULL); - if (NULL == wh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("partner_base_url", - partner_base_url), - GNUNET_JSON_pack_timestamp ("start_date", - start_date), - GNUNET_JSON_pack_timestamp ("end_date", - end_date), - GNUNET_JSON_pack_time_rel ("wad_frequency", - wad_frequency), - GNUNET_JSON_pack_data_auto ("partner_pub", - &partner_pub), - GNUNET_JSON_pack_data_auto ("master_sig", - &master_sig), - TALER_JSON_pack_amount ("wad_fee", - wad_fee) - ); - eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&wh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (wh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wh->url); - wh->job = GNUNET_CURL_job_add2 (ctx, - eh, - wh->post_ctx.headers, - &handle_add_partner_finished, - wh); - if (NULL == wh->job) - { - TALER_EXCHANGE_management_add_partner_cancel (wh); - return NULL; - } - return wh; -} - - -void -TALER_EXCHANGE_management_add_partner_cancel ( - struct TALER_EXCHANGE_ManagementAddPartner *wh) -{ - if (NULL != wh->job) - { - GNUNET_CURL_job_cancel (wh->job); - wh->job = NULL; - } - TALER_curl_easy_post_finished (&wh->post_ctx); - GNUNET_free (wh->url); - GNUNET_free (wh); -} diff --git a/src/lib/exchange_api_management_auditor_disable.c b/src/lib/exchange_api_management_auditor_disable.c deleted file mode 100644 index 8bce7f74f..000000000 --- a/src/lib/exchange_api_management_auditor_disable.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_auditor_disable.c - * @brief functions to disable an auditor - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - -/** - * @brief Handle for a POST /management/auditors/$AUDITOR_PUB/disable request. - */ -struct TALER_EXCHANGE_ManagementAuditorDisableHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementAuditorDisableCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/auditors/%s/disable request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorDisableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_auditor_disable_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementAuditorDisableHandle *ah = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementAuditorDisableResponse adr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ah->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - adr.hr.ec = TALER_JSON_get_error_code (json); - adr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management auditor disable\n", - (unsigned int) response_code, - (int) adr.hr.ec); - break; - } - if (NULL != ah->cb) - { - ah->cb (ah->cb_cls, - &adr); - ah->cb = NULL; - } - TALER_EXCHANGE_management_disable_auditor_cancel (ah); -} - - -struct TALER_EXCHANGE_ManagementAuditorDisableHandle * -TALER_EXCHANGE_management_disable_auditor ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_AuditorPublicKeyP *auditor_pub, - struct GNUNET_TIME_Timestamp validity_end, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementAuditorDisableCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementAuditorDisableHandle *ah; - CURL *eh; - json_t *body; - - ah = GNUNET_new (struct TALER_EXCHANGE_ManagementAuditorDisableHandle); - ah->cb = cb; - ah->cb_cls = cb_cls; - ah->ctx = ctx; - { - char epub_str[sizeof (*auditor_pub) * 2]; - char arg_str[sizeof (epub_str) + 64]; - char *end; - - end = GNUNET_STRINGS_data_to_string (auditor_pub, - sizeof (*auditor_pub), - epub_str, - sizeof (epub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "management/auditors/%s/disable", - epub_str); - ah->url = TALER_url_join (url, - arg_str, - NULL); - } - if (NULL == ah->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ah); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig), - GNUNET_JSON_pack_timestamp ("validity_end", - validity_end)); - eh = TALER_EXCHANGE_curl_easy_get_ (ah->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ah->post_ctx, - eh, - body))) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (ah->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ah->url); - ah->job = GNUNET_CURL_job_add2 (ctx, - eh, - ah->post_ctx.headers, - &handle_auditor_disable_finished, - ah); - if (NULL == ah->job) - { - TALER_EXCHANGE_management_disable_auditor_cancel (ah); - return NULL; - } - return ah; -} - - -void -TALER_EXCHANGE_management_disable_auditor_cancel ( - struct TALER_EXCHANGE_ManagementAuditorDisableHandle *ah) -{ - if (NULL != ah->job) - { - GNUNET_CURL_job_cancel (ah->job); - ah->job = NULL; - } - TALER_curl_easy_post_finished (&ah->post_ctx); - GNUNET_free (ah->url); - GNUNET_free (ah); -} diff --git a/src/lib/exchange_api_management_auditor_enable.c b/src/lib/exchange_api_management_auditor_enable.c deleted file mode 100644 index 41c5049c2..000000000 --- a/src/lib/exchange_api_management_auditor_enable.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_auditor_enable.c - * @brief functions to enable an auditor - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -/** - * @brief Handle for a POST /management/auditors request. - */ -struct TALER_EXCHANGE_ManagementAuditorEnableHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementAuditorEnableCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /management/auditors request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_auditor_enable_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementAuditorEnableHandle *ah = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementAuditorEnableResponse aer = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ah->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - aer.hr.ec = TALER_JSON_get_error_code (json); - aer.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - ah->url); - if (NULL != json) - { - aer.hr.ec = TALER_JSON_get_error_code (json); - aer.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - aer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - aer.hr.hint = TALER_ErrorCode_get_hint (aer.hr.ec); - } - break; - case MHD_HTTP_CONFLICT: - aer.hr.ec = TALER_JSON_get_error_code (json); - aer.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - aer.hr.ec = TALER_JSON_get_error_code (json); - aer.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management auditor enable\n", - (unsigned int) response_code, - (int) aer.hr.ec); - break; - } - if (NULL != ah->cb) - { - ah->cb (ah->cb_cls, - &aer); - ah->cb = NULL; - } - TALER_EXCHANGE_management_enable_auditor_cancel (ah); -} - - -struct TALER_EXCHANGE_ManagementAuditorEnableHandle * -TALER_EXCHANGE_management_enable_auditor ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_AuditorPublicKeyP *auditor_pub, - const char *auditor_url, - const char *auditor_name, - struct GNUNET_TIME_Timestamp validity_start, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementAuditorEnableCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementAuditorEnableHandle *ah; - CURL *eh; - json_t *body; - - ah = GNUNET_new (struct TALER_EXCHANGE_ManagementAuditorEnableHandle); - ah->cb = cb; - ah->cb_cls = cb_cls; - ah->ctx = ctx; - ah->url = TALER_url_join (url, - "management/auditors", - NULL); - if (NULL == ah->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ah); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("auditor_url", - auditor_url), - GNUNET_JSON_pack_string ("auditor_name", - auditor_name), - GNUNET_JSON_pack_data_auto ("auditor_pub", - auditor_pub), - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig), - GNUNET_JSON_pack_timestamp ("validity_start", - validity_start)); - eh = TALER_EXCHANGE_curl_easy_get_ (ah->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ah->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - json_decref (body); - if (NULL != eh) - curl_easy_cleanup (eh); - GNUNET_free (ah->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ah->url); - ah->job = GNUNET_CURL_job_add2 (ctx, - eh, - ah->post_ctx.headers, - &handle_auditor_enable_finished, - ah); - if (NULL == ah->job) - { - TALER_EXCHANGE_management_enable_auditor_cancel (ah); - return NULL; - } - return ah; -} - - -void -TALER_EXCHANGE_management_enable_auditor_cancel ( - struct TALER_EXCHANGE_ManagementAuditorEnableHandle *ah) -{ - if (NULL != ah->job) - { - GNUNET_CURL_job_cancel (ah->job); - ah->job = NULL; - } - TALER_curl_easy_post_finished (&ah->post_ctx); - GNUNET_free (ah->url); - GNUNET_free (ah); -} diff --git a/src/lib/exchange_api_management_drain_profits.c b/src/lib/exchange_api_management_drain_profits.c deleted file mode 100644 index bc7232b87..000000000 --- a/src/lib/exchange_api_management_drain_profits.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_drain_profits.c - * @brief functions to set wire fees at an exchange - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "exchange_api_curl_defaults.h" -#include "taler_exchange_service.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementDrainProfitsHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementDrainProfitsCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/drain request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementDrainProfitsHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_drain_profits_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementDrainResponse dr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - dp->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - dr.hr.ec = TALER_JSON_get_error_code (json); - dr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - dr.hr.ec = TALER_JSON_get_error_code (json); - dr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_PRECONDITION_FAILED: - dr.hr.ec = TALER_JSON_get_error_code (json); - dr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - dr.hr.ec = TALER_JSON_get_error_code (json); - dr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management drain profits\n", - (unsigned int) response_code, - (int) dr.hr.ec); - break; - } - if (NULL != dp->cb) - { - dp->cb (dp->cb_cls, - &dr); - dp->cb = NULL; - } - TALER_EXCHANGE_management_drain_profits_cancel (dp); -} - - -struct TALER_EXCHANGE_ManagementDrainProfitsHandle * -TALER_EXCHANGE_management_drain_profits ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_WireTransferIdentifierRawP *wtid, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Timestamp date, - const char *account_section, - const char *payto_uri, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementDrainProfitsCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp; - CURL *eh; - json_t *body; - - dp = GNUNET_new (struct TALER_EXCHANGE_ManagementDrainProfitsHandle); - dp->cb = cb; - dp->cb_cls = cb_cls; - dp->ctx = ctx; - dp->url = TALER_url_join (url, - "management/drain", - NULL); - if (NULL == dp->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (dp); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("debit_account_section", - account_section), - GNUNET_JSON_pack_string ("credit_payto_uri", - payto_uri), - GNUNET_JSON_pack_data_auto ("wtid", - wtid), - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig), - GNUNET_JSON_pack_timestamp ("date", - date), - TALER_JSON_pack_amount ("amount", - amount)); - eh = TALER_EXCHANGE_curl_easy_get_ (dp->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&dp->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (dp->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - dp->url); - dp->job = GNUNET_CURL_job_add2 (ctx, - eh, - dp->post_ctx.headers, - &handle_drain_profits_finished, - dp); - if (NULL == dp->job) - { - TALER_EXCHANGE_management_drain_profits_cancel (dp); - return NULL; - } - return dp; -} - - -void -TALER_EXCHANGE_management_drain_profits_cancel ( - struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp) -{ - if (NULL != dp->job) - { - GNUNET_CURL_job_cancel (dp->job); - dp->job = NULL; - } - TALER_curl_easy_post_finished (&dp->post_ctx); - GNUNET_free (dp->url); - GNUNET_free (dp); -} diff --git a/src/lib/exchange_api_management_post_extensions.c b/src/lib/exchange_api_management_post_extensions.c deleted file mode 100644 index 00d1c5e3f..000000000 --- a/src/lib/exchange_api_management_post_extensions.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2023 Taler Systems SA - - 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, see - - */ -/** - * @file lib/exchange_api_management_post_extensions.c - * @brief functions to handle the settings for extensions (p2p and age restriction) - * @author Özgür Kesim - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_extensions.h" -#include "exchange_api_curl_defaults.h" -#include "taler_exchange_service.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -/** - * @brief Handle for a POST /management/extensions request. - */ -struct TALER_EXCHANGE_ManagementPostExtensionsHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementPostExtensionsCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /management/extensions request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementPostExtensionsHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_extensions_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementPostExtensionsResponse per = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ph->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - per.hr.ec = TALER_JSON_get_error_code (json); - per.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - ph->url); - if (NULL != json) - { - per.hr.ec = TALER_JSON_get_error_code (json); - per.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - per.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - per.hr.hint = TALER_ErrorCode_get_hint (per.hr.ec); - } - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - per.hr.ec = TALER_JSON_get_error_code (json); - per.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management post extensions\n", - (unsigned int) response_code, - (int) per.hr.ec); - break; - } - if (NULL != ph->cb) - { - ph->cb (ph->cb_cls, - &per); - ph->cb = NULL; - } - TALER_EXCHANGE_management_post_extensions_cancel (ph); -} - - -struct TALER_EXCHANGE_ManagementPostExtensionsHandle * -TALER_EXCHANGE_management_post_extensions ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_EXCHANGE_ManagementPostExtensionsData *ped, - TALER_EXCHANGE_ManagementPostExtensionsCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph; - CURL *eh = NULL; - json_t *body = NULL; - - ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostExtensionsHandle); - ph->cb = cb; - ph->cb_cls = cb_cls; - ph->ctx = ctx; - ph->url = TALER_url_join (url, - "management/extensions", - NULL); - if (NULL == ph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ph); - return NULL; - } - - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_object_steal ("extensions", - (json_t *) ped->extensions), - GNUNET_JSON_pack_data_auto ("extensions_sig", - &ped->extensions_sig)); - - eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ph->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (ph->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting URL '%s'\n", - ph->url); - ph->job = GNUNET_CURL_job_add2 (ctx, - eh, - ph->post_ctx.headers, - &handle_post_extensions_finished, - ph); - if (NULL == ph->job) - { - TALER_EXCHANGE_management_post_extensions_cancel (ph); - return NULL; - } - return ph; -} - - -void -TALER_EXCHANGE_management_post_extensions_cancel ( - struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph) -{ - if (NULL != ph->job) - { - GNUNET_CURL_job_cancel (ph->job); - ph->job = NULL; - } - TALER_curl_easy_post_finished (&ph->post_ctx); - GNUNET_free (ph->url); - GNUNET_free (ph); -} diff --git a/src/lib/exchange_api_management_post_keys.c b/src/lib/exchange_api_management_post_keys.c deleted file mode 100644 index a46124d90..000000000 --- a/src/lib/exchange_api_management_post_keys.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_post_keys.c - * @brief functions to affirm the validity of exchange keys using the master private key - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -/** - * @brief Handle for a POST /management/keys request. - */ -struct TALER_EXCHANGE_ManagementPostKeysHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementPostKeysCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP POST /management/keys request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementPostKeysHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_post_keys_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementPostKeysHandle *ph = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementPostKeysResponse pkr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - ph->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - pkr.hr.ec = TALER_JSON_get_error_code (json); - pkr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - pkr.hr.ec = TALER_JSON_get_error_code (json); - pkr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: - pkr.hr.ec = TALER_JSON_get_error_code (json); - pkr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - pkr.hr.ec = TALER_JSON_get_error_code (json); - pkr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management post keys\n", - (unsigned int) response_code, - (int) pkr.hr.ec); - break; - } - if (NULL != ph->cb) - { - ph->cb (ph->cb_cls, - &pkr); - ph->cb = NULL; - } - TALER_EXCHANGE_post_management_keys_cancel (ph); -} - - -struct TALER_EXCHANGE_ManagementPostKeysHandle * -TALER_EXCHANGE_post_management_keys ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_EXCHANGE_ManagementPostKeysData *pkd, - TALER_EXCHANGE_ManagementPostKeysCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementPostKeysHandle *ph; - CURL *eh; - json_t *body; - json_t *denom_sigs; - json_t *signkey_sigs; - - ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostKeysHandle); - ph->cb = cb; - ph->cb_cls = cb_cls; - ph->ctx = ctx; - ph->url = TALER_url_join (url, - "management/keys", - NULL); - if (NULL == ph->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (ph); - return NULL; - } - denom_sigs = json_array (); - GNUNET_assert (NULL != denom_sigs); - for (unsigned int i = 0; inum_denom_sigs; i++) - { - const struct TALER_EXCHANGE_DenominationKeySignature *dks - = &pkd->denom_sigs[i]; - - GNUNET_assert (0 == - json_array_append_new ( - denom_sigs, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("h_denom_pub", - &dks->h_denom_pub), - GNUNET_JSON_pack_data_auto ("master_sig", - &dks->master_sig)))); - } - signkey_sigs = json_array (); - GNUNET_assert (NULL != signkey_sigs); - for (unsigned int i = 0; inum_sign_sigs; i++) - { - const struct TALER_EXCHANGE_SigningKeySignature *sks - = &pkd->sign_sigs[i]; - - GNUNET_assert (0 == - json_array_append_new ( - signkey_sigs, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("exchange_pub", - &sks->exchange_pub), - GNUNET_JSON_pack_data_auto ("master_sig", - &sks->master_sig)))); - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("denom_sigs", - denom_sigs), - GNUNET_JSON_pack_array_steal ("signkey_sigs", - signkey_sigs)); - eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ph->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (ph->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - ph->url); - ph->job = GNUNET_CURL_job_add2 (ctx, - eh, - ph->post_ctx.headers, - &handle_post_keys_finished, - ph); - if (NULL == ph->job) - { - TALER_EXCHANGE_post_management_keys_cancel (ph); - return NULL; - } - return ph; -} - - -void -TALER_EXCHANGE_post_management_keys_cancel ( - struct TALER_EXCHANGE_ManagementPostKeysHandle *ph) -{ - if (NULL != ph->job) - { - GNUNET_CURL_job_cancel (ph->job); - ph->job = NULL; - } - TALER_curl_easy_post_finished (&ph->post_ctx); - GNUNET_free (ph->url); - GNUNET_free (ph); -} diff --git a/src/lib/exchange_api_management_revoke_denomination_key.c b/src/lib/exchange_api_management_revoke_denomination_key.c deleted file mode 100644 index aa4d527a7..000000000 --- a/src/lib/exchange_api_management_revoke_denomination_key.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2020 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_revoke_denomination_key.c - * @brief functions to revoke an exchange denomination key - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -/** - * @brief Handle for a POST /management/denominations/$H_DENOM_PUB/revoke request. - */ -struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementRevokeDenominationKeyCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/denominations/$H_DENOM_PUB/revoke request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_revoke_denomination_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementRevokeDenominationResponse rdr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - rh->job = NULL; - switch (response_code) - { - case 0: - /* no reply */ - rdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - rdr.hr.hint = "server offline?"; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - rdr.hr.ec = TALER_JSON_get_error_code (json); - rdr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - rdr.hr.ec = TALER_JSON_get_error_code (json); - rdr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management revoke denomination\n", - (unsigned int) response_code, - (int) rdr.hr.ec); - break; - } - if (NULL != rh->cb) - { - rh->cb (rh->cb_cls, - &rdr); - rh->cb = NULL; - } - TALER_EXCHANGE_management_revoke_denomination_key_cancel (rh); -} - - -struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle * -TALER_EXCHANGE_management_revoke_denomination_key ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementRevokeDenominationKeyCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh; - CURL *eh; - json_t *body; - - rh = GNUNET_new (struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle); - rh->cb = cb; - rh->cb_cls = cb_cls; - rh->ctx = ctx; - { - char epub_str[sizeof (*h_denom_pub) * 2]; - char arg_str[sizeof (epub_str) + 64]; - char *end; - - end = GNUNET_STRINGS_data_to_string (h_denom_pub, - sizeof (*h_denom_pub), - epub_str, - sizeof (epub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "management/denominations/%s/revoke", - epub_str); - rh->url = TALER_url_join (url, - arg_str, - NULL); - } - if (NULL == rh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (rh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig)); - if (NULL == body) - { - GNUNET_break (0); - GNUNET_free (rh->url); - GNUNET_free (rh); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (rh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&rh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (rh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - rh->url); - rh->job = GNUNET_CURL_job_add2 (ctx, - eh, - rh->post_ctx.headers, - &handle_revoke_denomination_finished, - rh); - if (NULL == rh->job) - { - TALER_EXCHANGE_management_revoke_denomination_key_cancel (rh); - return NULL; - } - return rh; -} - - -void -TALER_EXCHANGE_management_revoke_denomination_key_cancel ( - struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh) -{ - if (NULL != rh->job) - { - GNUNET_CURL_job_cancel (rh->job); - rh->job = NULL; - } - TALER_curl_easy_post_finished (&rh->post_ctx); - GNUNET_free (rh->url); - GNUNET_free (rh); -} diff --git a/src/lib/exchange_api_management_revoke_signing_key.c b/src/lib/exchange_api_management_revoke_signing_key.c deleted file mode 100644 index c4d634248..000000000 --- a/src/lib/exchange_api_management_revoke_signing_key.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_revoke_signing_key.c - * @brief functions to revoke an exchange online signing key - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementRevokeSigningKeyCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/signkeys/%s/revoke request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_revoke_signing_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse rsr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - rh->job = NULL; - switch (response_code) - { - case 0: - /* no reply */ - rsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - rsr.hr.hint = "server offline?"; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - rsr.hr.ec = TALER_JSON_get_error_code (json); - rsr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - rsr.hr.ec = TALER_JSON_get_error_code (json); - rsr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management revoke signkey\n", - (unsigned int) response_code, - (int) rsr.hr.ec); - break; - } - if (NULL != rh->cb) - { - rh->cb (rh->cb_cls, - &rsr); - rh->cb = NULL; - } - TALER_EXCHANGE_management_revoke_signing_key_cancel (rh); -} - - -struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle * -TALER_EXCHANGE_management_revoke_signing_key ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementRevokeSigningKeyCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh; - CURL *eh; - json_t *body; - - rh = GNUNET_new (struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle); - rh->cb = cb; - rh->cb_cls = cb_cls; - rh->ctx = ctx; - { - char epub_str[sizeof (*exchange_pub) * 2]; - char arg_str[sizeof (epub_str) + 64]; - char *end; - - end = GNUNET_STRINGS_data_to_string (exchange_pub, - sizeof (*exchange_pub), - epub_str, - sizeof (epub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "management/signkeys/%s/revoke", - epub_str); - rh->url = TALER_url_join (url, - arg_str, - NULL); - } - if (NULL == rh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (rh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig)); - eh = TALER_EXCHANGE_curl_easy_get_ (rh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&rh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (rh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - rh->url); - rh->job = GNUNET_CURL_job_add2 (ctx, - eh, - rh->post_ctx.headers, - &handle_revoke_signing_finished, - rh); - if (NULL == rh->job) - { - TALER_EXCHANGE_management_revoke_signing_key_cancel (rh); - return NULL; - } - return rh; -} - - -void -TALER_EXCHANGE_management_revoke_signing_key_cancel ( - struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh) -{ - if (NULL != rh->job) - { - GNUNET_CURL_job_cancel (rh->job); - rh->job = NULL; - } - TALER_curl_easy_post_finished (&rh->post_ctx); - GNUNET_free (rh->url); - GNUNET_free (rh); -} diff --git a/src/lib/exchange_api_management_set_global_fee.c b/src/lib/exchange_api_management_set_global_fee.c deleted file mode 100644 index 54c37fd64..000000000 --- a/src/lib/exchange_api_management_set_global_fee.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2022 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_set_global_fee.c - * @brief functions to set global fees at an exchange - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "exchange_api_curl_defaults.h" -#include "taler_exchange_service.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementSetGlobalFeeCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/global request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_set_global_fee_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *sgfh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse sfr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - sgfh->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - sfr.hr.ec = TALER_JSON_get_error_code (json); - sfr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - sgfh->url); - if (NULL != json) - { - sfr.hr.ec = TALER_JSON_get_error_code (json); - sfr.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - sfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - sfr.hr.hint = TALER_ErrorCode_get_hint (sfr.hr.ec); - } - break; - case MHD_HTTP_CONFLICT: - sfr.hr.ec = TALER_JSON_get_error_code (json); - sfr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_PRECONDITION_FAILED: - sfr.hr.ec = TALER_JSON_get_error_code (json); - sfr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - sfr.hr.ec = TALER_JSON_get_error_code (json); - sfr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management set global fee\n", - (unsigned int) response_code, - (int) sfr.hr.ec); - break; - } - if (NULL != sgfh->cb) - { - sgfh->cb (sgfh->cb_cls, - &sfr); - sgfh->cb = NULL; - } - TALER_EXCHANGE_management_set_global_fees_cancel (sgfh); -} - - -struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle * -TALER_EXCHANGE_management_set_global_fees ( - struct GNUNET_CURL_Context *ctx, - const char *exchange_base_url, - struct GNUNET_TIME_Timestamp validity_start, - struct GNUNET_TIME_Timestamp validity_end, - const struct TALER_GlobalFeeSet *fees, - struct GNUNET_TIME_Relative purse_timeout, - struct GNUNET_TIME_Relative history_expiration, - uint32_t purse_account_limit, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementSetGlobalFeeCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *sgfh; - CURL *eh; - json_t *body; - - sgfh = GNUNET_new (struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle); - sgfh->cb = cb; - sgfh->cb_cls = cb_cls; - sgfh->ctx = ctx; - sgfh->url = TALER_url_join (exchange_base_url, - "management/global-fee", - NULL); - if (NULL == sgfh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (sgfh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig), - GNUNET_JSON_pack_timestamp ("fee_start", - validity_start), - GNUNET_JSON_pack_timestamp ("fee_end", - validity_end), - TALER_JSON_pack_amount ("history_fee", - &fees->history), - TALER_JSON_pack_amount ("account_fee", - &fees->account), - TALER_JSON_pack_amount ("purse_fee", - &fees->purse), - GNUNET_JSON_pack_time_rel ("purse_timeout", - purse_timeout), - GNUNET_JSON_pack_time_rel ("history_expiration", - history_expiration), - GNUNET_JSON_pack_uint64 ("purse_account_limit", - purse_account_limit)); - eh = TALER_EXCHANGE_curl_easy_get_ (sgfh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&sgfh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (sgfh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - sgfh->url); - sgfh->job = GNUNET_CURL_job_add2 (ctx, - eh, - sgfh->post_ctx.headers, - &handle_set_global_fee_finished, - sgfh); - if (NULL == sgfh->job) - { - TALER_EXCHANGE_management_set_global_fees_cancel (sgfh); - return NULL; - } - return sgfh; -} - - -void -TALER_EXCHANGE_management_set_global_fees_cancel ( - struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *sgfh) -{ - if (NULL != sgfh->job) - { - GNUNET_CURL_job_cancel (sgfh->job); - sgfh->job = NULL; - } - TALER_curl_easy_post_finished (&sgfh->post_ctx); - GNUNET_free (sgfh->url); - GNUNET_free (sgfh); -} diff --git a/src/lib/exchange_api_management_set_wire_fee.c b/src/lib/exchange_api_management_set_wire_fee.c deleted file mode 100644 index 03cab8c99..000000000 --- a/src/lib/exchange_api_management_set_wire_fee.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020-2022 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_set_wire_fee.c - * @brief functions to set wire fees at an exchange - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "exchange_api_curl_defaults.h" -#include "taler_exchange_service.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementSetWireFeeHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementSetWireFeeCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/wire request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_set_wire_fee_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementSetWireFeeHandle *swfh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementSetWireFeeResponse swr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - swfh->job = NULL; - switch (response_code) - { - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - swr.hr.ec = TALER_JSON_get_error_code (json); - swr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - swfh->url); - if (NULL != json) - { - swr.hr.ec = TALER_JSON_get_error_code (json); - swr.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - swr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - swr.hr.hint = TALER_ErrorCode_get_hint (swr.hr.ec); - } - break; - case MHD_HTTP_CONFLICT: - swr.hr.ec = TALER_JSON_get_error_code (json); - swr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_PRECONDITION_FAILED: - swr.hr.ec = TALER_JSON_get_error_code (json); - swr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - swr.hr.ec = TALER_JSON_get_error_code (json); - swr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management set wire fee\n", - (unsigned int) response_code, - (int) swr.hr.ec); - break; - } - if (NULL != swfh->cb) - { - swfh->cb (swfh->cb_cls, - &swr); - swfh->cb = NULL; - } - TALER_EXCHANGE_management_set_wire_fees_cancel (swfh); -} - - -struct TALER_EXCHANGE_ManagementSetWireFeeHandle * -TALER_EXCHANGE_management_set_wire_fees ( - struct GNUNET_CURL_Context *ctx, - const char *exchange_base_url, - const char *wire_method, - struct GNUNET_TIME_Timestamp validity_start, - struct GNUNET_TIME_Timestamp validity_end, - const struct TALER_WireFeeSet *fees, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementSetWireFeeCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementSetWireFeeHandle *swfh; - CURL *eh; - json_t *body; - - swfh = GNUNET_new (struct TALER_EXCHANGE_ManagementSetWireFeeHandle); - swfh->cb = cb; - swfh->cb_cls = cb_cls; - swfh->ctx = ctx; - swfh->url = TALER_url_join (exchange_base_url, - "management/wire-fee", - NULL); - if (NULL == swfh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (swfh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("wire_method", - wire_method), - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig), - GNUNET_JSON_pack_timestamp ("fee_start", - validity_start), - GNUNET_JSON_pack_timestamp ("fee_end", - validity_end), - TALER_JSON_pack_amount ("closing_fee", - &fees->closing), - TALER_JSON_pack_amount ("wire_fee", - &fees->wire)); - eh = TALER_EXCHANGE_curl_easy_get_ (swfh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&swfh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (swfh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - swfh->url); - swfh->job = GNUNET_CURL_job_add2 (ctx, - eh, - swfh->post_ctx.headers, - &handle_set_wire_fee_finished, - swfh); - if (NULL == swfh->job) - { - TALER_EXCHANGE_management_set_wire_fees_cancel (swfh); - return NULL; - } - return swfh; -} - - -void -TALER_EXCHANGE_management_set_wire_fees_cancel ( - struct TALER_EXCHANGE_ManagementSetWireFeeHandle *swfh) -{ - if (NULL != swfh->job) - { - GNUNET_CURL_job_cancel (swfh->job); - swfh->job = NULL; - } - TALER_curl_easy_post_finished (&swfh->post_ctx); - GNUNET_free (swfh->url); - GNUNET_free (swfh); -} diff --git a/src/lib/exchange_api_management_update_aml_officer.c b/src/lib/exchange_api_management_update_aml_officer.c deleted file mode 100644 index 76cbc7e8d..000000000 --- a/src/lib/exchange_api_management_update_aml_officer.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_update_aml_officer.c - * @brief functions to update AML officer status - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementUpdateAmlOfficer -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementUpdateAmlOfficerCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/wire request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_update_aml_officer_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse uar = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - wh->job = NULL; - switch (response_code) - { - case 0: - /* no reply */ - uar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - uar.hr.hint = "server offline?"; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - uar.hr.ec = TALER_JSON_get_error_code (json); - uar.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - wh->url); - if (NULL != json) - { - uar.hr.ec = TALER_JSON_get_error_code (json); - uar.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - uar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - uar.hr.hint = TALER_ErrorCode_get_hint (uar.hr.ec); - } - break; - case MHD_HTTP_CONFLICT: - uar.hr.ec = TALER_JSON_get_error_code (json); - uar.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - uar.hr.ec = TALER_JSON_get_error_code (json); - uar.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management update AML officer\n", - (unsigned int) response_code, - (int) uar.hr.ec); - break; - } - if (NULL != wh->cb) - { - wh->cb (wh->cb_cls, - &uar); - wh->cb = NULL; - } - TALER_EXCHANGE_management_update_aml_officer_cancel (wh); -} - - -struct TALER_EXCHANGE_ManagementUpdateAmlOfficer * -TALER_EXCHANGE_management_update_aml_officer ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_AmlOfficerPublicKeyP *officer_pub, - const char *officer_name, - struct GNUNET_TIME_Timestamp change_date, - bool is_active, - bool read_only, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementUpdateAmlOfficerCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh; - CURL *eh; - json_t *body; - - wh = GNUNET_new (struct TALER_EXCHANGE_ManagementUpdateAmlOfficer); - wh->cb = cb; - wh->cb_cls = cb_cls; - wh->ctx = ctx; - wh->url = TALER_url_join (url, - "management/aml-officers", - NULL); - if (NULL == wh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("officer_name", - officer_name), - GNUNET_JSON_pack_data_auto ("officer_pub", - officer_pub), - GNUNET_JSON_pack_data_auto ("master_sig", - master_sig), - GNUNET_JSON_pack_bool ("is_active", - is_active), - GNUNET_JSON_pack_bool ("read_only", - read_only), - GNUNET_JSON_pack_timestamp ("change_date", - change_date)); - eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&wh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (wh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wh->url); - wh->job = GNUNET_CURL_job_add2 (ctx, - eh, - wh->post_ctx.headers, - &handle_update_aml_officer_finished, - wh); - if (NULL == wh->job) - { - TALER_EXCHANGE_management_update_aml_officer_cancel (wh); - return NULL; - } - return wh; -} - - -void -TALER_EXCHANGE_management_update_aml_officer_cancel ( - struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh) -{ - if (NULL != wh->job) - { - GNUNET_CURL_job_cancel (wh->job); - wh->job = NULL; - } - TALER_curl_easy_post_finished (&wh->post_ctx); - GNUNET_free (wh->url); - GNUNET_free (wh); -} diff --git a/src/lib/exchange_api_management_wire_disable.c b/src/lib/exchange_api_management_wire_disable.c deleted file mode 100644 index 30749b0e4..000000000 --- a/src/lib/exchange_api_management_wire_disable.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_wire_disable.c - * @brief functions to disable an exchange wire method / bank account - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementWireDisableHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementWireDisableCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/wire/disable request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorDisableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_auditor_disable_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementWireDisableHandle *wh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementWireDisableResponse wdr = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - wh->job = NULL; - switch (response_code) - { - case 0: - /* no reply */ - wdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - wdr.hr.hint = "server offline?"; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - wdr.hr.ec = TALER_JSON_get_error_code (json); - wdr.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - wh->url); - if (NULL != json) - { - wdr.hr.ec = TALER_JSON_get_error_code (json); - wdr.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - wdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - wdr.hr.hint = TALER_ErrorCode_get_hint (wdr.hr.ec); - } - break; - case MHD_HTTP_CONFLICT: - wdr.hr.ec = TALER_JSON_get_error_code (json); - wdr.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - wdr.hr.ec = TALER_JSON_get_error_code (json); - wdr.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d exchange management disable wire\n", - (unsigned int) response_code, - (int) wdr.hr.ec); - break; - } - if (NULL != wh->cb) - { - wh->cb (wh->cb_cls, - &wdr); - wh->cb = NULL; - } - TALER_EXCHANGE_management_disable_wire_cancel (wh); -} - - -struct TALER_EXCHANGE_ManagementWireDisableHandle * -TALER_EXCHANGE_management_disable_wire ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const char *payto_uri, - struct GNUNET_TIME_Timestamp validity_end, - const struct TALER_MasterSignatureP *master_sig, - TALER_EXCHANGE_ManagementWireDisableCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementWireDisableHandle *wh; - CURL *eh; - json_t *body; - - wh = GNUNET_new (struct TALER_EXCHANGE_ManagementWireDisableHandle); - wh->cb = cb; - wh->cb_cls = cb_cls; - wh->ctx = ctx; - wh->url = TALER_url_join (url, - "management/wire/disable", - NULL); - if (NULL == wh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("payto_uri", - payto_uri), - GNUNET_JSON_pack_data_auto ("master_sig_del", - master_sig), - GNUNET_JSON_pack_timestamp ("validity_end", - validity_end)); - eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&wh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (wh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wh->url); - wh->job = GNUNET_CURL_job_add2 (ctx, - eh, - wh->post_ctx.headers, - &handle_auditor_disable_finished, - wh); - if (NULL == wh->job) - { - TALER_EXCHANGE_management_disable_wire_cancel (wh); - return NULL; - } - return wh; -} - - -void -TALER_EXCHANGE_management_disable_wire_cancel ( - struct TALER_EXCHANGE_ManagementWireDisableHandle *wh) -{ - if (NULL != wh->job) - { - GNUNET_CURL_job_cancel (wh->job); - wh->job = NULL; - } - TALER_curl_easy_post_finished (&wh->post_ctx); - GNUNET_free (wh->url); - GNUNET_free (wh); -} diff --git a/src/lib/exchange_api_management_wire_enable.c b/src/lib/exchange_api_management_wire_enable.c deleted file mode 100644 index 5add3e0b0..000000000 --- a/src/lib/exchange_api_management_wire_enable.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_management_wire_enable.c - * @brief functions to enable an exchange wire method / bank account - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "exchange_api_curl_defaults.h" -#include "taler_signatures.h" -#include "taler_curl_lib.h" -#include "taler_json_lib.h" - - -struct TALER_EXCHANGE_ManagementWireEnableHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Minor context that holds body and headers. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_ManagementWireEnableCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reference to the execution context. - */ - struct GNUNET_CURL_Context *ctx; -}; - - -/** - * Function called when we're done processing the - * HTTP /management/wire request. - * - * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` - * @param response_code HTTP response code, 0 on error - * @param response response body, NULL if not in JSON - */ -static void -handle_auditor_enable_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_ManagementWireEnableHandle *wh = cls; - const json_t *json = response; - struct TALER_EXCHANGE_ManagementWireEnableResponse wer = { - .hr.http_status = (unsigned int) response_code, - .hr.reply = json - }; - - wh->job = NULL; - switch (response_code) - { - case 0: - /* no reply */ - wer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - wer.hr.hint = "server offline?"; - break; - case MHD_HTTP_NO_CONTENT: - break; - case MHD_HTTP_FORBIDDEN: - wer.hr.ec = TALER_JSON_get_error_code (json); - wer.hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", - wh->url); - if (NULL != json) - { - wer.hr.ec = TALER_JSON_get_error_code (json); - wer.hr.hint = TALER_JSON_get_error_hint (json); - } - else - { - wer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - wer.hr.hint = TALER_ErrorCode_get_hint (wer.hr.ec); - } - break; - case MHD_HTTP_CONFLICT: - wer.hr.ec = TALER_JSON_get_error_code (json); - wer.hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - wer.hr.ec = TALER_JSON_get_error_code (json); - wer.hr.hint = TALER_JSON_get_error_hint (json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange management enable wire\n", - (unsigned int) response_code, - (int) wer.hr.ec); - break; - } - if (NULL != wh->cb) - { - wh->cb (wh->cb_cls, - &wer); - wh->cb = NULL; - } - TALER_EXCHANGE_management_enable_wire_cancel (wh); -} - - -struct TALER_EXCHANGE_ManagementWireEnableHandle * -TALER_EXCHANGE_management_enable_wire ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const char *payto_uri, - const char *conversion_url, - const json_t *debit_restrictions, - const json_t *credit_restrictions, - struct GNUNET_TIME_Timestamp validity_start, - const struct TALER_MasterSignatureP *master_sig1, - const struct TALER_MasterSignatureP *master_sig2, - TALER_EXCHANGE_ManagementWireEnableCallback cb, - void *cb_cls) -{ - struct TALER_EXCHANGE_ManagementWireEnableHandle *wh; - CURL *eh; - json_t *body; - - { - char *msg = TALER_payto_validate (payto_uri); - - if (NULL != msg) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "payto URI is malformed: %s\n", - msg); - GNUNET_free (msg); - return NULL; - } - } - wh = GNUNET_new (struct TALER_EXCHANGE_ManagementWireEnableHandle); - wh->cb = cb; - wh->cb_cls = cb_cls; - wh->ctx = ctx; - wh->url = TALER_url_join (url, - "management/wire", - NULL); - if (NULL == wh->url) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - GNUNET_free (wh); - return NULL; - } - body = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("payto_uri", - payto_uri), - GNUNET_JSON_pack_array_incref ("debit_restrictions", - (json_t *) debit_restrictions), - GNUNET_JSON_pack_array_incref ("credit_restrictions", - (json_t *) credit_restrictions), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("conversion_url", - conversion_url)), - GNUNET_JSON_pack_data_auto ("master_sig_add", - master_sig1), - GNUNET_JSON_pack_data_auto ("master_sig_wire", - master_sig2), - GNUNET_JSON_pack_timestamp ("validity_start", - validity_start)); - eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&wh->post_ctx, - eh, - body)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (body); - GNUNET_free (wh->url); - return NULL; - } - json_decref (body); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - wh->url); - wh->job = GNUNET_CURL_job_add2 (ctx, - eh, - wh->post_ctx.headers, - &handle_auditor_enable_finished, - wh); - if (NULL == wh->job) - { - TALER_EXCHANGE_management_enable_wire_cancel (wh); - return NULL; - } - return wh; -} - - -void -TALER_EXCHANGE_management_enable_wire_cancel ( - struct TALER_EXCHANGE_ManagementWireEnableHandle *wh) -{ - if (NULL != wh->job) - { - GNUNET_CURL_job_cancel (wh->job); - wh->job = NULL; - } - TALER_curl_easy_post_finished (&wh->post_ctx); - GNUNET_free (wh->url); - GNUNET_free (wh); -} diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c deleted file mode 100644 index ba4241dab..000000000 --- a/src/lib/exchange_api_melt.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_melt.c - * @brief Implementation of the /coins/$COIN_PUB/melt request - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_common.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" -#include "exchange_api_refresh_common.h" - - -/** - * @brief A /coins/$COIN_PUB/melt Handle - */ -struct TALER_EXCHANGE_MeltHandle -{ - - /** - * The keys of the this request handle will use - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The url for this request. - */ - char *url; - - /** - * The exchange base url. - */ - char *exchange_url; - - /** - * Curl context. - */ - struct GNUNET_CURL_Context *cctx; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with refresh melt failure results. - */ - TALER_EXCHANGE_MeltCallback melt_cb; - - /** - * Closure for @e result_cb and @e melt_failure_cb. - */ - void *melt_cb_cls; - - /** - * Actual information about the melt operation. - */ - struct MeltData md; - - /** - * The secret the entire melt operation is seeded from. - */ - struct TALER_RefreshMasterSecretP rms; - - /** - * Details about the characteristics of the requested melt operation. - */ - const struct TALER_EXCHANGE_RefreshData *rd; - - /** - * Array of `num_fresh_coins` per-coin values - * returned from melt operation. - */ - struct TALER_EXCHANGE_MeltBlindingDetail *mbds; - - /** - * Handle for the preflight request, or NULL. - */ - struct TALER_EXCHANGE_CsRMeltHandle *csr; - - /** - * Public key of the coin being melted. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Signature affirming the melt. - */ - struct TALER_CoinSpendSignatureP coin_sig; - - /** - * @brief Public information about the coin's denomination key - */ - const struct TALER_EXCHANGE_DenomPublicKey *dki; - - /** - * Gamma value chosen by the exchange during melt. - */ - uint32_t noreveal_index; - - /** - * True if we need to include @e rms in our melt request. - */ - bool send_rms; -}; - - -/** - * Verify that the signature on the "200 OK" response - * from the exchange is valid. - * - * @param[in,out] mh melt handle - * @param json json reply with the signature - * @param[out] exchange_pub public key of the exchange used for the signature - * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh, - const json_t *json, - struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct TALER_ExchangeSignatureP exchange_sig; - const struct TALER_EXCHANGE_Keys *key_state; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - exchange_pub), - GNUNET_JSON_spec_uint32 ("noreveal_index", - &mh->noreveal_index), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* check that exchange signing key is permitted */ - key_state = mh->keys; - if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (key_state, - exchange_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - /* check that noreveal index is in permitted range */ - if (TALER_CNC_KAPPA <= mh->noreveal_index) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - TALER_exchange_online_melt_confirmation_verify ( - &mh->md.rc, - mh->noreveal_index, - exchange_pub, - &exchange_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /coins/$COIN_PUB/melt request. - * - * @param cls the `struct TALER_EXCHANGE_MeltHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_melt_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_MeltHandle *mh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_MeltResponse mr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - mh->job = NULL; - switch (response_code) - { - case 0: - mr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - verify_melt_signature_ok (mh, - j, - &mr.details.ok.sign_key)) - { - GNUNET_break_op (0); - mr.hr.http_status = 0; - mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; - break; - } - mr.details.ok.noreveal_index = mh->noreveal_index; - mr.details.ok.num_mbds = mh->rd->fresh_pks_len; - mr.details.ok.mbds = mh->mbds; - mh->melt_cb (mh->melt_cb_cls, - &mr); - mh->melt_cb = NULL; - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_FORBIDDEN: - /* Nothing really to verify, exchange says one of the signatures is - invalid; assuming we checked them, this should never happen, we - should pass the JSON reply to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - mr.hr.ec = TALER_JSON_get_error_code (j); - mr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange melt\n", - (unsigned int) response_code, - mr.hr.ec); - GNUNET_break_op (0); - break; - } - if (NULL != mh->melt_cb) - mh->melt_cb (mh->melt_cb_cls, - &mr); - TALER_EXCHANGE_melt_cancel (mh); -} - - -/** - * Start the actual melt operation, now that we have - * the exchange's input values. - * - * @param[in,out] mh melt operation to run - * @return #GNUNET_OK if we could start the operation - */ -static enum GNUNET_GenericReturnValue -start_melt (struct TALER_EXCHANGE_MeltHandle *mh) -{ - const struct TALER_EXCHANGE_Keys *key_state; - json_t *melt_obj; - CURL *eh; - char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; - struct TALER_DenominationHashP h_denom_pub; - struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len]; - - for (unsigned int i = 0; ird->fresh_pks_len; i++) - alg_values[i] = mh->mbds[i].alg_value; - if (GNUNET_OK != - TALER_EXCHANGE_get_melt_data_ (&mh->rms, - mh->rd, - alg_values, - &mh->md)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_denom_pub_hash (&mh->md.melted_coin.pub_key, - &h_denom_pub); - TALER_wallet_melt_sign ( - &mh->md.melted_coin.melt_amount_with_fee, - &mh->md.melted_coin.fee_melt, - &mh->md.rc, - &h_denom_pub, - mh->md.melted_coin.h_age_commitment, - &mh->md.melted_coin.coin_priv, - &mh->coin_sig); - GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv, - &mh->coin_pub.eddsa_pub); - melt_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &h_denom_pub), - TALER_JSON_pack_denom_sig ("denom_sig", - &mh->md.melted_coin.sig), - GNUNET_JSON_pack_data_auto ("confirm_sig", - &mh->coin_sig), - TALER_JSON_pack_amount ("value_with_fee", - &mh->md.melted_coin.melt_amount_with_fee), - GNUNET_JSON_pack_data_auto ("rc", - &mh->md.rc), - GNUNET_JSON_pack_allow_null ( - (NULL != mh->md.melted_coin.h_age_commitment) - ? GNUNET_JSON_pack_data_auto ("age_commitment_hash", - mh->md.melted_coin.h_age_commitment) - : GNUNET_JSON_pack_string ("age_commitment_hash", - NULL)), - GNUNET_JSON_pack_allow_null ( - mh->send_rms - ? GNUNET_JSON_pack_data_auto ("rms", - &mh->rms) - : GNUNET_JSON_pack_string ("rms", - NULL))); - { - char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &mh->coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "coins/%s/melt", - pub_str); - } - - key_state = mh->keys; - mh->dki = TALER_EXCHANGE_get_denomination_key (key_state, - &mh->md.melted_coin.pub_key); - - /* and now we can at last begin the actual request handling */ - - mh->url = TALER_url_join (mh->exchange_url, - arg_str, - NULL); - if (NULL == mh->url) - { - json_decref (melt_obj); - return GNUNET_SYSERR; - } - eh = TALER_EXCHANGE_curl_easy_get_ (mh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&mh->ctx, - eh, - melt_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (melt_obj); - return GNUNET_SYSERR; - } - json_decref (melt_obj); - mh->job = GNUNET_CURL_job_add2 (mh->cctx, - eh, - mh->ctx.headers, - &handle_melt_finished, - mh); - return GNUNET_OK; -} - - -/** - * The melt request @a mh failed, return an error to - * the application and cancel the operation. - * - * @param[in] mh melt request that failed - * @param ec error code to fail with - */ -static void -fail_mh (struct TALER_EXCHANGE_MeltHandle *mh, - enum TALER_ErrorCode ec) -{ - struct TALER_EXCHANGE_MeltResponse mr = { - .hr.ec = ec - }; - - mh->melt_cb (mh->melt_cb_cls, - &mr); - TALER_EXCHANGE_melt_cancel (mh); -} - - -/** - * Callbacks of this type are used to serve the result of submitting a - * CS R request to a exchange. - * - * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` - * @param csrr response details - */ -static void -csr_cb (void *cls, - const struct TALER_EXCHANGE_CsRMeltResponse *csrr) -{ - struct TALER_EXCHANGE_MeltHandle *mh = cls; - unsigned int nks_off = 0; - - mh->csr = NULL; - if (MHD_HTTP_OK != csrr->hr.http_status) - { - struct TALER_EXCHANGE_MeltResponse mr = { - .hr = csrr->hr - }; - - mr.hr.hint = "/csr-melt failed"; - mh->melt_cb (mh->melt_cb_cls, - &mr); - TALER_EXCHANGE_melt_cancel (mh); - return; - } - for (unsigned int i = 0; ird->fresh_pks_len; i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = - &mh->rd->fresh_pks[i]; - struct TALER_ExchangeWithdrawValues *wv = &mh->mbds[i].alg_value; - - switch (fresh_pk->key.cipher) - { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - fail_mh (mh, - TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); - return; - case TALER_DENOMINATION_RSA: - GNUNET_assert (TALER_DENOMINATION_RSA == wv->cipher); - break; - case TALER_DENOMINATION_CS: - GNUNET_assert (TALER_DENOMINATION_CS == wv->cipher); - *wv = csrr->details.ok.alg_values[nks_off]; - nks_off++; - break; - } - } - mh->send_rms = true; - if (GNUNET_OK != - start_melt (mh)) - { - GNUNET_break (0); - fail_mh (mh, - TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); - return; - } -} - - -struct TALER_EXCHANGE_MeltHandle * -TALER_EXCHANGE_melt ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - TALER_EXCHANGE_MeltCallback melt_cb, - void *melt_cb_cls) -{ - struct TALER_EXCHANGE_NonceKey nks[GNUNET_NZL (rd->fresh_pks_len)]; - unsigned int nks_off = 0; - struct TALER_EXCHANGE_MeltHandle *mh; - - if (0 == rd->fresh_pks_len) - { - GNUNET_break (0); - return NULL; - } - mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle); - mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */ - mh->cctx = ctx; - mh->exchange_url = GNUNET_strdup (url); - mh->rd = rd; - mh->rms = *rms; - mh->melt_cb = melt_cb; - mh->melt_cb_cls = melt_cb_cls; - mh->mbds = GNUNET_new_array (rd->fresh_pks_len, - struct TALER_EXCHANGE_MeltBlindingDetail); - for (unsigned int i = 0; ifresh_pks_len; i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = &rd->fresh_pks[i]; - struct TALER_ExchangeWithdrawValues *wv = &mh->mbds[i].alg_value; - - switch (fresh_pk->key.cipher) - { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - GNUNET_free (mh->mbds); - GNUNET_free (mh); - return NULL; - case TALER_DENOMINATION_RSA: - wv->cipher = TALER_DENOMINATION_RSA; - break; - case TALER_DENOMINATION_CS: - wv->cipher = TALER_DENOMINATION_CS; - nks[nks_off].pk = fresh_pk; - nks[nks_off].cnc_num = nks_off; - nks_off++; - break; - } - } - mh->keys = TALER_EXCHANGE_keys_incref (keys); - if (0 != nks_off) - { - mh->csr = TALER_EXCHANGE_csr_melt (ctx, - url, - rms, - nks_off, - nks, - &csr_cb, - mh); - if (NULL == mh->csr) - { - GNUNET_break (0); - TALER_EXCHANGE_melt_cancel (mh); - return NULL; - } - return mh; - } - if (GNUNET_OK != - start_melt (mh)) - { - GNUNET_break (0); - TALER_EXCHANGE_melt_cancel (mh); - return NULL; - } - return mh; -} - - -void -TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh) -{ - if (NULL != mh->job) - { - GNUNET_CURL_job_cancel (mh->job); - mh->job = NULL; - } - if (NULL != mh->csr) - { - TALER_EXCHANGE_csr_melt_cancel (mh->csr); - mh->csr = NULL; - } - TALER_EXCHANGE_free_melt_data_ (&mh->md); /* does not free 'md' itself */ - GNUNET_free (mh->mbds); - GNUNET_free (mh->url); - GNUNET_free (mh->exchange_url); - TALER_curl_easy_post_finished (&mh->ctx); - TALER_EXCHANGE_keys_decref (mh->keys); - GNUNET_free (mh); -} - - -/* end of exchange_api_melt.c */ diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c deleted file mode 100644 index cfd265f04..000000000 --- a/src/lib/exchange_api_recoup.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2017-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_recoup.c - * @brief Implementation of the /recoup request of the exchange's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_common.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A Recoup Handle - */ -struct TALER_EXCHANGE_RecoupHandle -{ - - /** - * The keys of the exchange this request handle will use - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Denomination key of the coin. - */ - struct TALER_EXCHANGE_DenomPublicKey pk; - - /** - * Our signature requesting the recoup. - */ - struct TALER_CoinSpendSignatureP coin_sig; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_RecoupResultCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Public key of the coin we are trying to get paid back. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - -}; - - -/** - * Parse a recoup response. If it is valid, call the callback. - * - * @param ph recoup handle - * @param json json reply with the signature - * @return #GNUNET_OK if the signature is valid and we called the callback; - * #GNUNET_SYSERR if not (callback must still be called) - */ -static enum GNUNET_GenericReturnValue -process_recoup_response (const struct TALER_EXCHANGE_RecoupHandle *ph, - const json_t *json) -{ - struct TALER_EXCHANGE_RecoupResponse rr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_OK - }; - struct GNUNET_JSON_Specification spec_withdraw[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &rr.details.ok.reserve_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec_withdraw, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - ph->cb (ph->cb_cls, - &rr); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /recoup request. - * - * @param cls the `struct TALER_EXCHANGE_RecoupHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_recoup_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_RecoupHandle *ph = cls; - const json_t *j = response; - struct TALER_EXCHANGE_RecoupResponse rr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - ph->job = NULL; - switch (response_code) - { - case 0: - rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - process_recoup_response (ph, - j)) - { - GNUNET_break_op (0); - rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - rr.hr.http_status = 0; - break; - } - TALER_EXCHANGE_recoup_cancel (ph); - return; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - { - struct TALER_Amount min_key; - - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - if (GNUNET_OK != - TALER_EXCHANGE_get_min_denomination_ (ph->keys, - &min_key)) - { - GNUNET_break (0); - rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - rr.hr.http_status = 0; - break; - } - break; - } - case MHD_HTTP_FORBIDDEN: - /* Nothing really to verify, exchange says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* Kind of normal: the money was already sent to the merchant - (it was too late for the refund). */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange recoup\n", - (unsigned int) response_code, - (int) rr.hr.ec); - GNUNET_break (0); - break; - } - ph->cb (ph->cb_cls, - &rr); - TALER_EXCHANGE_recoup_cancel (ph); -} - - -struct TALER_EXCHANGE_RecoupHandle * -TALER_EXCHANGE_recoup ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_EXCHANGE_DenomPublicKey *pk, - const struct TALER_DenominationSignature *denom_sig, - const struct TALER_ExchangeWithdrawValues *exchange_vals, - const struct TALER_PlanchetMasterSecretP *ps, - TALER_EXCHANGE_RecoupResultCallback recoup_cb, - void *recoup_cb_cls) -{ - struct TALER_EXCHANGE_RecoupHandle *ph; - struct TALER_DenominationHashP h_denom_pub; - json_t *recoup_obj; - CURL *eh; - char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; - struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; - - ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle); - TALER_planchet_setup_coin_priv (ps, - exchange_vals, - &coin_priv); - TALER_planchet_blinding_secret_create (ps, - exchange_vals, - &bks); - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, - &ph->coin_pub.eddsa_pub); - TALER_denom_pub_hash (&pk->key, - &h_denom_pub); - TALER_wallet_recoup_sign (&h_denom_pub, - &bks, - &coin_priv, - &ph->coin_sig); - recoup_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &h_denom_pub), - TALER_JSON_pack_denom_sig ("denom_sig", - denom_sig), - TALER_JSON_pack_exchange_withdraw_values ("ewv", - exchange_vals), - GNUNET_JSON_pack_data_auto ("coin_sig", - &ph->coin_sig), - GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", - &bks)); - if (TALER_DENOMINATION_CS == denom_sig->cipher) - { - struct TALER_CsNonce nonce; - - /* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash() - it is not strictly clear that the nonce is needed. Best case would be - to find a way to include it more 'naturally' somehow, for example with - the variant union version of bks! */ - TALER_cs_withdraw_nonce_derive (ps, - &nonce); - GNUNET_assert ( - 0 == - json_object_set_new (recoup_obj, - "cs_nonce", - GNUNET_JSON_from_data_auto ( - &nonce))); - } - - { - char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &ph->coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "coins/%s/recoup", - pub_str); - } - - ph->pk = *pk; - memset (&ph->pk.key, - 0, - sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */ - ph->cb = recoup_cb; - ph->cb_cls = recoup_cb_cls; - ph->url = TALER_url_join (url, - arg_str, - NULL); - if (NULL == ph->url) - { - json_decref (recoup_obj); - GNUNET_free (ph); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ph->ctx, - eh, - recoup_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (recoup_obj); - GNUNET_free (ph->url); - GNUNET_free (ph); - return NULL; - } - json_decref (recoup_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "URL for recoup: `%s'\n", - ph->url); - ph->keys = TALER_EXCHANGE_keys_incref (keys); - ph->job = GNUNET_CURL_job_add2 (ctx, - eh, - ph->ctx.headers, - &handle_recoup_finished, - ph); - return ph; -} - - -void -TALER_EXCHANGE_recoup_cancel (struct TALER_EXCHANGE_RecoupHandle *ph) -{ - if (NULL != ph->job) - { - GNUNET_CURL_job_cancel (ph->job); - ph->job = NULL; - } - GNUNET_free (ph->url); - TALER_curl_easy_post_finished (&ph->ctx); - TALER_EXCHANGE_keys_decref (ph->keys); - GNUNET_free (ph); -} - - -/* end of exchange_api_recoup.c */ diff --git a/src/lib/exchange_api_recoup_refresh.c b/src/lib/exchange_api_recoup_refresh.c deleted file mode 100644 index 0bcd44dec..000000000 --- a/src/lib/exchange_api_recoup_refresh.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2017-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_recoup_refresh.c - * @brief Implementation of the /recoup-refresh request of the exchange's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_common.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A Recoup Handle - */ -struct TALER_EXCHANGE_RecoupRefreshHandle -{ - - /** - * The keys of the exchange this request handle will use - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Denomination key of the coin. - */ - struct TALER_EXCHANGE_DenomPublicKey pk; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_RecoupRefreshResultCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Public key of the coin we are trying to get paid back. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Signature affirming the recoup-refresh operation. - */ - struct TALER_CoinSpendSignatureP coin_sig; - -}; - - -/** - * Parse a recoup-refresh response. If it is valid, call the callback. - * - * @param ph recoup handle - * @param json json reply with the signature - * @return #GNUNET_OK if the signature is valid and we called the callback; - * #GNUNET_SYSERR if not (callback must still be called) - */ -static enum GNUNET_GenericReturnValue -process_recoup_response ( - const struct TALER_EXCHANGE_RecoupRefreshHandle *ph, - const json_t *json) -{ - struct TALER_EXCHANGE_RecoupRefreshResponse rrr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_OK - }; - struct GNUNET_JSON_Specification spec_refresh[] = { - GNUNET_JSON_spec_fixed_auto ("old_coin_pub", - &rrr.details.ok.old_coin_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec_refresh, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - ph->cb (ph->cb_cls, - &rrr); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /recoup-refresh request. - * - * @param cls the `struct TALER_EXCHANGE_RecoupRefreshHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_recoup_refresh_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_RecoupRefreshHandle *ph = cls; - const json_t *j = response; - struct TALER_EXCHANGE_RecoupRefreshResponse rrr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - ph->job = NULL; - switch (response_code) - { - case 0: - rrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - process_recoup_response (ph, - j)) - { - GNUNET_break_op (0); - rrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - rrr.hr.http_status = 0; - break; - } - TALER_EXCHANGE_recoup_refresh_cancel (ph); - return; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_FORBIDDEN: - /* Nothing really to verify, exchange says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* Kind of normal: the money was already sent to the merchant - (it was too late for the refund). */ - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - rrr.hr.ec = TALER_JSON_get_error_code (j); - rrr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange recoup\n", - (unsigned int) response_code, - (int) rrr.hr.ec); - GNUNET_break (0); - break; - } - ph->cb (ph->cb_cls, - &rrr); - TALER_EXCHANGE_recoup_refresh_cancel (ph); -} - - -struct TALER_EXCHANGE_RecoupRefreshHandle * -TALER_EXCHANGE_recoup_refresh ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_EXCHANGE_DenomPublicKey *pk, - const struct TALER_DenominationSignature *denom_sig, - const struct TALER_ExchangeWithdrawValues *exchange_vals, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_PlanchetMasterSecretP *ps, - unsigned int idx, - TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb, - void *recoup_cb_cls) -{ - struct TALER_EXCHANGE_RecoupRefreshHandle *ph; - struct TALER_DenominationHashP h_denom_pub; - json_t *recoup_obj; - CURL *eh; - char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; - struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; - - GNUNET_assert (NULL != recoup_cb); - ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle); - ph->pk = *pk; - memset (&ph->pk.key, - 0, - sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */ - ph->cb = recoup_cb; - ph->cb_cls = recoup_cb_cls; - TALER_planchet_setup_coin_priv (ps, - exchange_vals, - &coin_priv); - TALER_planchet_blinding_secret_create (ps, - exchange_vals, - &bks); - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, - &ph->coin_pub.eddsa_pub); - TALER_denom_pub_hash (&pk->key, - &h_denom_pub); - TALER_wallet_recoup_refresh_sign (&h_denom_pub, - &bks, - &coin_priv, - &ph->coin_sig); - recoup_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &h_denom_pub), - TALER_JSON_pack_denom_sig ("denom_sig", - denom_sig), - TALER_JSON_pack_exchange_withdraw_values ("ewv", - exchange_vals), - GNUNET_JSON_pack_data_auto ("coin_sig", - &ph->coin_sig), - GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", - &bks)); - - if (TALER_DENOMINATION_CS == denom_sig->cipher) - { - struct TALER_CsNonce nonce; - - /* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash() - it is not strictly clear that the nonce is needed. Best case would be - to find a way to include it more 'naturally' somehow, for example with - the variant union version of bks! */ - TALER_cs_refresh_nonce_derive (rms, - idx, - &nonce); - GNUNET_assert ( - 0 == - json_object_set_new (recoup_obj, - "cs_nonce", - GNUNET_JSON_from_data_auto ( - &nonce))); - } - - { - char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &ph->coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "coins/%s/recoup-refresh", - pub_str); - } - - ph->url = TALER_url_join (url, - arg_str, - NULL); - if (NULL == ph->url) - { - json_decref (recoup_obj); - GNUNET_free (ph); - return NULL; - } - eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&ph->ctx, - eh, - recoup_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (recoup_obj); - GNUNET_free (ph->url); - GNUNET_free (ph); - return NULL; - } - json_decref (recoup_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "URL for recoup-refresh: `%s'\n", - ph->url); - ph->keys = TALER_EXCHANGE_keys_incref (keys); - ph->job = GNUNET_CURL_job_add2 (ctx, - eh, - ph->ctx.headers, - &handle_recoup_refresh_finished, - ph); - return ph; -} - - -void -TALER_EXCHANGE_recoup_refresh_cancel ( - struct TALER_EXCHANGE_RecoupRefreshHandle *ph) -{ - if (NULL != ph->job) - { - GNUNET_CURL_job_cancel (ph->job); - ph->job = NULL; - } - GNUNET_free (ph->url); - TALER_curl_easy_post_finished (&ph->ctx); - TALER_EXCHANGE_keys_decref (ph->keys); - GNUNET_free (ph); -} - - -/* end of exchange_api_recoup_refresh.c */ diff --git a/src/lib/exchange_api_refresh_common.c b/src/lib/exchange_api_refresh_common.c deleted file mode 100644 index 0a6665b55..000000000 --- a/src/lib/exchange_api_refresh_common.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2022 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_refresh_common.c - * @brief Serialization logic shared between melt and reveal steps during refreshing - * @author Christian Grothoff - */ -#include "platform.h" -#include "exchange_api_refresh_common.h" - - -void -TALER_EXCHANGE_free_melt_data_ (struct MeltData *md) -{ - for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) - { - struct TALER_RefreshCoinData *rcds = md->rcd[i]; - - if (NULL == rcds) - continue; - for (unsigned int j = 0; j < md->num_fresh_coins; j++) - TALER_blinded_planchet_free (&rcds[j].blinded_planchet); - GNUNET_free (rcds); - } - TALER_denom_pub_free (&md->melted_coin.pub_key); - TALER_denom_sig_free (&md->melted_coin.sig); - if (NULL != md->fcds) - { - for (unsigned int j = 0; jnum_fresh_coins; j++) - { - struct FreshCoinData *fcd = &md->fcds[j]; - - TALER_denom_pub_free (&fcd->fresh_pk); - for (size_t i = 0; i < TALER_CNC_KAPPA; i++) - { - TALER_age_commitment_proof_free (fcd->age_commitment_proofs[i]); - GNUNET_free (fcd->age_commitment_proofs[i]); - } - } - GNUNET_free (md->fcds); - } - /* Finally, clean up a bit... */ - GNUNET_CRYPTO_zero_keys (md, - sizeof (struct MeltData)); -} - - -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_get_melt_data_ ( - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - const struct TALER_ExchangeWithdrawValues *alg_values, - struct MeltData *md) -{ - struct TALER_Amount total; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_CsNonce nonces[rd->fresh_pks_len]; - bool uses_cs = false; - - GNUNET_CRYPTO_eddsa_key_get_public (&rd->melt_priv.eddsa_priv, - &coin_pub.eddsa_pub); - /* build up melt data structure */ - memset (md, - 0, - sizeof (*md)); - md->num_fresh_coins = rd->fresh_pks_len; - md->melted_coin.coin_priv = rd->melt_priv; - md->melted_coin.melt_amount_with_fee = rd->melt_amount; - md->melted_coin.fee_melt = rd->melt_pk.fees.refresh; - md->melted_coin.original_value = rd->melt_pk.value; - md->melted_coin.expire_deposit = rd->melt_pk.expire_deposit; - md->melted_coin.age_commitment_proof = rd->melt_age_commitment_proof; - md->melted_coin.h_age_commitment = rd->melt_h_age_commitment; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (rd->melt_amount.currency, - &total)); - TALER_denom_pub_deep_copy (&md->melted_coin.pub_key, - &rd->melt_pk.key); - TALER_denom_sig_deep_copy (&md->melted_coin.sig, - &rd->melt_sig); - md->fcds = GNUNET_new_array (md->num_fresh_coins, - struct FreshCoinData); - for (unsigned int j = 0; jfresh_pks_len; j++) - { - struct FreshCoinData *fcd = &md->fcds[j]; - - if (alg_values[j].cipher != rd->fresh_pks[j].key.cipher) - { - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - if (TALER_DENOMINATION_CS == alg_values[j].cipher) - { - uses_cs = true; - TALER_cs_refresh_nonce_derive ( - rms, - j, - &nonces[j]); - } - TALER_denom_pub_deep_copy (&fcd->fresh_pk, - &rd->fresh_pks[j].key); - if ( (0 > - TALER_amount_add (&total, - &total, - &rd->fresh_pks[j].value)) || - (0 > - TALER_amount_add (&total, - &total, - &rd->fresh_pks[j].fees.withdraw)) ) - { - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - } - - /* verify that melt_amount is above total cost */ - if (1 == - TALER_amount_cmp (&total, - &rd->melt_amount) ) - { - /* Eh, this operation is more expensive than the - @a melt_amount. This is not OK. */ - GNUNET_break (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - - /* build up coins */ - for (unsigned int i = 0; imelt_priv, - i, - &md->transfer_priv[i]); - - GNUNET_CRYPTO_ecdhe_key_get_public ( - &md->transfer_priv[i].ecdhe_priv, - &md->transfer_pub[i].ecdhe_pub); - - TALER_link_derive_transfer_secret (&rd->melt_priv, - &md->transfer_priv[i], - &trans_sec); - - md->rcd[i] = GNUNET_new_array (rd->fresh_pks_len, - struct TALER_RefreshCoinData); - - for (unsigned int j = 0; jfresh_pks_len; j++) - { - struct FreshCoinData *fcd = &md->fcds[j]; - struct TALER_CoinSpendPrivateKeyP *coin_priv = &fcd->coin_priv; - struct TALER_PlanchetMasterSecretP *ps = &fcd->ps[i]; - struct TALER_RefreshCoinData *rcd = &md->rcd[i][j]; - union TALER_DenominationBlindingKeyP *bks = &fcd->bks[i]; - struct TALER_PlanchetDetail pd; - struct TALER_CoinPubHashP c_hash; - struct TALER_AgeCommitmentHash ach; - struct TALER_AgeCommitmentHash *pah = NULL; - - TALER_transfer_secret_to_planchet_secret (&trans_sec, - j, - ps); - - TALER_planchet_setup_coin_priv (ps, - &alg_values[j], - coin_priv); - - TALER_planchet_blinding_secret_create (ps, - &alg_values[j], - bks); - - if (NULL != rd->melt_age_commitment_proof) - { - fcd->age_commitment_proofs[i] = GNUNET_new (struct - TALER_AgeCommitmentProof); - - GNUNET_assert (GNUNET_OK == - TALER_age_commitment_derive ( - md->melted_coin.age_commitment_proof, - &trans_sec.key, - fcd->age_commitment_proofs[i])); - - TALER_age_commitment_hash ( - &fcd->age_commitment_proofs[i]->commitment, - &ach); - pah = &ach; - } - - if (TALER_DENOMINATION_CS == alg_values[j].cipher) - pd.blinded_planchet.details.cs_blinded_planchet.nonce = nonces[j]; - - if (GNUNET_OK != - TALER_planchet_prepare (&fcd->fresh_pk, - &alg_values[j], - bks, - coin_priv, - pah, - &c_hash, - &pd)) - { - GNUNET_break_op (0); - TALER_EXCHANGE_free_melt_data_ (md); - return GNUNET_SYSERR; - } - rcd->blinded_planchet = pd.blinded_planchet; - rcd->dk = &fcd->fresh_pk; - } - } - - /* Finally, compute refresh commitment */ - { - struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; - - for (unsigned int i = 0; itransfer_pub[i]; - rce[i].new_coins = md->rcd[i]; - } - TALER_refresh_get_commitment (&md->rc, - TALER_CNC_KAPPA, - uses_cs - ? rms - : NULL, - rd->fresh_pks_len, - rce, - &coin_pub, - &rd->melt_amount); - } - return GNUNET_OK; -} diff --git a/src/lib/exchange_api_refresh_common.h b/src/lib/exchange_api_refresh_common.h deleted file mode 100644 index 0cb80f17e..000000000 --- a/src/lib/exchange_api_refresh_common.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2022 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_refresh_common.h - * @brief shared (serialization) logic for refresh protocol - * @author Christian Grothoff - */ -#ifndef REFRESH_COMMON_H -#define REFRESH_COMMON_H -#include -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "taler_signatures.h" - - -/** - * Information about a coin we are melting. - */ -struct MeltedCoin -{ - /** - * Private key of the coin. - */ - struct TALER_CoinSpendPrivateKeyP coin_priv; - - /** - * Amount this coin contributes to the melt, including fee. - */ - struct TALER_Amount melt_amount_with_fee; - - /** - * The applicable fee for melting a coin of this denomination - */ - struct TALER_Amount fee_melt; - - /** - * The original value of the coin. - */ - struct TALER_Amount original_value; - - /** - * The original age commitment, its proof and its hash. MUST be NULL if no - * age commitment was set. - */ - const struct TALER_AgeCommitmentProof *age_commitment_proof; - const struct TALER_AgeCommitmentHash *h_age_commitment; - - /** - * Timestamp indicating when coins of this denomination become invalid. - */ - struct GNUNET_TIME_Timestamp expire_deposit; - - /** - * Denomination key of the original coin. - */ - struct TALER_DenominationPublicKey pub_key; - - /** - * Exchange's signature over the coin. - */ - struct TALER_DenominationSignature sig; - -}; - - -/** - * Data we keep for each fresh coin created in the - * melt process. - */ -struct FreshCoinData -{ - /** - * Denomination public key of the coin. - */ - struct TALER_DenominationPublicKey fresh_pk; - - /** - * Array of planchet secrets for the coins, depending - * on the cut-and-choose. - */ - struct TALER_PlanchetMasterSecretP ps[TALER_CNC_KAPPA]; - - /** - * Private key of the coin. - */ - struct TALER_CoinSpendPrivateKeyP coin_priv; - - /** - * Arrays of age commitments and proofs to be created, one for each - * cut-and-choose dimension. NULL if age restriction is not applicable. - */ - struct TALER_AgeCommitmentProof *age_commitment_proofs[TALER_CNC_KAPPA]; - - /** - * Blinding key secrets for the coins, depending on the - * cut-and-choose. - */ - union TALER_DenominationBlindingKeyP bks[TALER_CNC_KAPPA]; - -}; - - -/** - * Melt data in non-serialized format for convenient processing. - */ -struct MeltData -{ - - /** - * Hash over the committed data during refresh operation. - */ - struct TALER_RefreshCommitmentP rc; - - /** - * Information about the melted coin. - */ - struct MeltedCoin melted_coin; - - /** - * Array of length @e num_fresh_coins with information - * about each fresh coin. - */ - struct FreshCoinData *fcds; - - /** - * Transfer secrets, one per cut and choose. - */ - struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA]; - - /** - * Transfer private keys for each cut-and-choose dimension. - */ - struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA]; - - /** - * Transfer public key of this commitment. - */ - struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA]; - - /** - * Transfer secrets, one per cut and choose. - */ - struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; - - /** - * Blinded planchets and denominations of the fresh coins, depending on the cut-and-choose. Array of length - * @e num_fresh_coins. - */ - struct TALER_RefreshCoinData *rcd[TALER_CNC_KAPPA]; - - /** - * Number of coins we are creating - */ - uint16_t num_fresh_coins; - -}; - - -/** - * Compute the melt data from the refresh data and secret. - * - * @param rms secret internals of the refresh-reveal operation - * @param rd refresh data with the characteristics of the operation - * @param alg_values contributions from the exchange into the melt - * @param[out] md where to write the derived melt data - */ -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_get_melt_data_ ( - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - const struct TALER_ExchangeWithdrawValues *alg_values, - struct MeltData *md); - - -/** - * Free all information associated with a melting session. Note - * that we allow the melting session to be only partially initialized, - * as we use this function also when freeing melt data that was not - * fully initialized. - * - * @param[in] md melting data to release, the pointer itself is NOT - * freed (as it is typically not allocated by itself) - */ -void -TALER_EXCHANGE_free_melt_data_ (struct MeltData *md); - -#endif diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c deleted file mode 100644 index 220682992..000000000 --- a/src/lib/exchange_api_refreshes_reveal.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_refreshes_reveal.c - * @brief Implementation of the /refreshes/$RCH/reveal requests - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" -#include "exchange_api_refresh_common.h" - - -/** - * @brief A /refreshes/$RCH/reveal Handle - */ -struct TALER_EXCHANGE_RefreshesRevealHandle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Exchange-contributed values to the operation. - */ - struct TALER_ExchangeWithdrawValues *alg_values; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_RefreshesRevealCallback reveal_cb; - - /** - * Closure for @e reveal_cb. - */ - void *reveal_cb_cls; - - /** - * Actual information about the melt operation. - */ - struct MeltData md; - - /** - * The index selected by the exchange in cut-and-choose to not be revealed. - */ - uint16_t noreveal_index; - -}; - - -/** - * We got a 200 OK response for the /refreshes/$RCH/reveal operation. Extract - * the coin signatures and return them to the caller. The signatures we get - * from the exchange is for the blinded value. Thus, we first must unblind - * them and then should verify their validity. - * - * If everything checks out, we return the unblinded signatures - * to the application via the callback. - * - * @param rrh operation handle - * @param json reply from the exchange - * @param[out] rcis array of length `num_fresh_coins`, initialized to contain the coin data - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, - const json_t *json, - struct TALER_EXCHANGE_RevealedCoinInfo *rcis) -{ - const json_t *jsona; - struct GNUNET_JSON_Specification outer_spec[] = { - GNUNET_JSON_spec_array_const ("ev_sigs", - &jsona), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - outer_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (rrh->md.num_fresh_coins != json_array_size (jsona)) - { - /* Number of coins generated does not match our expectation */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 0; imd.num_fresh_coins; i++) - { - struct TALER_EXCHANGE_RevealedCoinInfo *rci = &rcis[i]; - const struct FreshCoinData *fcd = &rrh->md.fcds[i]; - const struct TALER_DenominationPublicKey *pk; - json_t *jsonai; - struct TALER_BlindedDenominationSignature blind_sig; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_CoinPubHashP coin_hash; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_blinded_denom_sig ("ev_sig", - &blind_sig), - GNUNET_JSON_spec_end () - }; - struct TALER_FreshCoin coin; - union TALER_DenominationBlindingKeyP bks; - const struct TALER_AgeCommitmentHash *pah = NULL; - - rci->ps = fcd->ps[rrh->noreveal_index]; - rci->bks = fcd->bks[rrh->noreveal_index]; - rci->age_commitment_proof = NULL; - - pk = &fcd->fresh_pk; - jsonai = json_array_get (jsona, i); - - GNUNET_assert (NULL != jsonai); - - if (NULL != rrh->md.melted_coin.age_commitment_proof) - { - rci->age_commitment_proof = - fcd->age_commitment_proofs[rrh->noreveal_index]; - - TALER_age_commitment_hash (&rci->age_commitment_proof->commitment, - &rci->h_age_commitment); - pah = &rci->h_age_commitment; - } - - if (GNUNET_OK != - GNUNET_JSON_parse (jsonai, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - TALER_planchet_setup_coin_priv (&rci->ps, - &rrh->alg_values[i], - &rci->coin_priv); - TALER_planchet_blinding_secret_create (&rci->ps, - &rrh->alg_values[i], - &bks); - /* needed to verify the signature, and we didn't store it earlier, - hence recomputing it here... */ - GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - TALER_coin_pub_hash ( - &coin_pub, - pah, - &coin_hash); - if (GNUNET_OK != - TALER_planchet_to_coin ( - pk, - &blind_sig, - &bks, - &rci->coin_priv, - pah, - &coin_hash, - &rrh->alg_values[i], - &coin)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - rci->sig = coin.sig; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /refreshes/$RCH/reveal request. - * - * @param cls the `struct TALER_EXCHANGE_RefreshHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_refresh_reveal_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_RevealResult rr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - rrh->job = NULL; - switch (response_code) - { - case 0: - rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - struct TALER_EXCHANGE_RevealedCoinInfo rcis[rrh->md.num_fresh_coins]; - enum GNUNET_GenericReturnValue ret; - - memset (rcis, - 0, - sizeof (rcis)); - ret = refresh_reveal_ok (rrh, - j, - rcis); - if (GNUNET_OK != ret) - { - rr.hr.http_status = 0; - rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - else - { - GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA); - rr.details.ok.num_coins = rrh->md.num_fresh_coins; - rr.details.ok.coins = rcis; - rrh->reveal_cb (rrh->reveal_cb_cls, - &rr); - rrh->reveal_cb = NULL; - } - for (unsigned int i = 0; imd.num_fresh_coins; i++) - { - TALER_denom_sig_free (&rcis[i].sig); - TALER_age_commitment_proof_free (rcis[i].age_commitment_proof); - } - TALER_EXCHANGE_refreshes_reveal_cancel (rrh); - return; - } - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); just pass JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - /* Nothing really to verify, exchange says our reveal is inconsistent - with our commitment, so either side is buggy; we - should pass the JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* Server claims key expired or has been revoked */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange refreshes reveal\n", - (unsigned int) response_code, - (int) rr.hr.ec); - break; - } - if (NULL != rrh->reveal_cb) - rrh->reveal_cb (rrh->reveal_cb_cls, - &rr); - TALER_EXCHANGE_refreshes_reveal_cancel (rrh); -} - - -struct TALER_EXCHANGE_RefreshesRevealHandle * -TALER_EXCHANGE_refreshes_reveal ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_RefreshMasterSecretP *rms, - const struct TALER_EXCHANGE_RefreshData *rd, - unsigned int num_coins, - const struct TALER_ExchangeWithdrawValues alg_values[static num_coins], - uint32_t noreveal_index, - TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, - void *reveal_cb_cls) -{ - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh; - json_t *transfer_privs; - json_t *new_denoms_h; - json_t *coin_evs; - json_t *reveal_obj; - json_t *link_sigs; - json_t *old_age_commitment = NULL; - CURL *eh; - struct MeltData md; - char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32]; - bool send_rms = false; - - GNUNET_assert (num_coins == rd->fresh_pks_len); - if (noreveal_index >= TALER_CNC_KAPPA) - { - /* We check this here, as it would be really bad to below just - disclose all the transfer keys. Note that this error should - have been caught way earlier when the exchange replied, but maybe - we had some internal corruption that changed the value... */ - GNUNET_break (0); - return NULL; - } - if (GNUNET_OK != - TALER_EXCHANGE_get_melt_data_ (rms, - rd, - alg_values, - &md)) - { - GNUNET_break (0); - return NULL; - } - - /* now new_denoms */ - GNUNET_assert (NULL != (new_denoms_h = json_array ())); - GNUNET_assert (NULL != (coin_evs = json_array ())); - GNUNET_assert (NULL != (link_sigs = json_array ())); - for (unsigned int i = 0; iblinded_planchet)))); - { - struct TALER_CoinSpendSignatureP link_sig; - struct TALER_BlindedCoinHashP bch; - - TALER_coin_ev_hash (&rcd->blinded_planchet, - &denom_hash, - &bch); - TALER_wallet_link_sign ( - &denom_hash, - &md.transfer_pub[noreveal_index], - &bch, - &md.melted_coin.coin_priv, - &link_sig); - GNUNET_assert (0 == - json_array_append_new ( - link_sigs, - GNUNET_JSON_from_data_auto (&link_sig))); - } - } - - /* build array of transfer private keys */ - GNUNET_assert (NULL != (transfer_privs = json_array ())); - for (unsigned int j = 0; jmelt_age_commitment_proof) - { - GNUNET_assert (NULL != rd->melt_h_age_commitment); - GNUNET_assert (NULL != (old_age_commitment = json_array ())); - - for (size_t i = 0; i < rd->melt_age_commitment_proof->commitment.num; i++) - { - enum GNUNET_GenericReturnValue ret; - ret = json_array_append_new ( - old_age_commitment, - GNUNET_JSON_from_data_auto ( - &rd->melt_age_commitment_proof->commitment.keys[i])); - GNUNET_assert (0 == ret); - } - } - - /* build main JSON request */ - reveal_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("transfer_pub", - &md.transfer_pub[noreveal_index]), - GNUNET_JSON_pack_allow_null ( - send_rms - ? GNUNET_JSON_pack_data_auto ("rms", - rms) - : GNUNET_JSON_pack_string ("rms", - NULL)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("old_age_commitment", - old_age_commitment)), - GNUNET_JSON_pack_array_steal ("transfer_privs", - transfer_privs), - GNUNET_JSON_pack_array_steal ("link_sigs", - link_sigs), - GNUNET_JSON_pack_array_steal ("new_denoms_h", - new_denoms_h), - GNUNET_JSON_pack_array_steal ("coin_evs", - coin_evs)); - { - char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string (&md.rc, - sizeof (md.rc), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "refreshes/%s/reveal", - pub_str); - } - /* finally, we can actually issue the request */ - rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle); - rrh->noreveal_index = noreveal_index; - rrh->reveal_cb = reveal_cb; - rrh->reveal_cb_cls = reveal_cb_cls; - rrh->md = md; - rrh->alg_values - = GNUNET_memdup (alg_values, - md.num_fresh_coins - * sizeof (struct TALER_ExchangeWithdrawValues)); - rrh->url = TALER_url_join (url, - arg_str, - NULL); - if (NULL == rrh->url) - { - json_decref (reveal_obj); - TALER_EXCHANGE_free_melt_data_ (&md); - GNUNET_free (rrh->alg_values); - GNUNET_free (rrh); - return NULL; - } - - eh = TALER_EXCHANGE_curl_easy_get_ (rrh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&rrh->ctx, - eh, - reveal_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (reveal_obj); - TALER_EXCHANGE_free_melt_data_ (&md); - GNUNET_free (rrh->alg_values); - GNUNET_free (rrh->url); - GNUNET_free (rrh); - return NULL; - } - json_decref (reveal_obj); - rrh->job = GNUNET_CURL_job_add2 (ctx, - eh, - rrh->ctx.headers, - &handle_refresh_reveal_finished, - rrh); - return rrh; -} - - -void -TALER_EXCHANGE_refreshes_reveal_cancel ( - struct TALER_EXCHANGE_RefreshesRevealHandle *rrh) -{ - if (NULL != rrh->job) - { - GNUNET_CURL_job_cancel (rrh->job); - rrh->job = NULL; - } - GNUNET_free (rrh->alg_values); - GNUNET_free (rrh->url); - TALER_curl_easy_post_finished (&rrh->ctx); - TALER_EXCHANGE_free_melt_data_ (&rrh->md); - GNUNET_free (rrh); -} - - -/* exchange_api_refreshes_reveal.c */ diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c deleted file mode 100644 index 7401bfe4f..000000000 --- a/src/lib/exchange_api_refund.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_refund.c - * @brief Implementation of the /refund request of the exchange's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include /* just for HTTP status codes */ -#include -#include -#include -#include "taler_json_lib.h" -#include "taler_exchange_service.h" -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "exchange_api_curl_defaults.h" - - -/** - * @brief A Refund Handle - */ -struct TALER_EXCHANGE_RefundHandle -{ - - /** - * The keys of the exchange this request handle will use - */ - struct TALER_EXCHANGE_Keys *keys; - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #TEH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - TALER_EXCHANGE_RefundCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Hash over the proposal data to identify the contract - * which is being refunded. - */ - struct TALER_PrivateContractHashP h_contract_terms; - - /** - * The coin's public key. This is the value that must have been - * signed (blindly) by the Exchange. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * The Merchant's public key. Allows the merchant to later refund - * the transaction or to inquire about the wire transfer identifier. - */ - struct TALER_MerchantPublicKeyP merchant; - - /** - * Merchant-generated transaction ID for the refund. - */ - uint64_t rtransaction_id; - - /** - * Amount to be refunded, including refund fee charged by the - * exchange to the customer. - */ - struct TALER_Amount refund_amount; - -}; - - -/** - * Verify that the signature on the "200 OK" response - * from the exchange is valid. - * - * @param[in,out] rh refund handle (refund fee added) - * @param json json reply with the signature - * @param[out] exchange_pub set to the exchange's public key - * @param[out] exchange_sig set to the exchange's signature - * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh, - const json_t *json, - struct TALER_ExchangePublicKeyP *exchange_pub, - struct TALER_ExchangeSignatureP *exchange_sig) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - exchange_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (rh->keys, - exchange_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_exchange_online_refund_confirmation_verify ( - &rh->h_contract_terms, - &rh->coin_pub, - &rh->merchant, - rh->rtransaction_id, - &rh->refund_amount, - exchange_pub, - exchange_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Verify that the information on the "412 Dependency Failed" response - * from the exchange is valid and indeed shows that there is a refund - * transaction ID reuse going on. - * - * @param[in,out] rh refund handle (refund fee added) - * @param json json reply with the signature - * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_failed_dependency_ok (struct TALER_EXCHANGE_RefundHandle *rh, - const json_t *json) -{ - const json_t *h; - json_t *e; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("history", - &h), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (1 != json_array_size (h)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - e = json_array_get (h, 0); - { - struct TALER_Amount amount; - const char *type; - struct TALER_MerchantSignatureP sig; - struct TALER_Amount refund_fee; - struct TALER_PrivateContractHashP h_contract_terms; - uint64_t rtransaction_id; - struct TALER_MerchantPublicKeyP merchant_pub; - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_amount_any ("amount", - &amount), - GNUNET_JSON_spec_string ("type", - &type), - TALER_JSON_spec_amount_any ("refund_fee", - &refund_fee), - GNUNET_JSON_spec_fixed_auto ("merchant_sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &h_contract_terms), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rtransaction_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (e, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_merchant_refund_verify (&rh->coin_pub, - &h_contract_terms, - rtransaction_id, - &amount, - &merchant_pub, - &sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if ( (rtransaction_id != rh->rtransaction_id) || - (0 != GNUNET_memcmp (&rh->h_contract_terms, - &h_contract_terms)) || - (0 != GNUNET_memcmp (&rh->merchant, - &merchant_pub)) || - (0 == TALER_amount_cmp (&rh->refund_amount, - &amount)) ) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /refund request. - * - * @param cls the `struct TALER_EXCHANGE_RefundHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_refund_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_EXCHANGE_RefundHandle *rh = cls; - const json_t *j = response; - struct TALER_EXCHANGE_RefundResponse rr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - rh->job = NULL; - switch (response_code) - { - case 0: - rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - verify_refund_signature_ok (rh, - j, - &rr.details.ok.exchange_pub, - &rr.details.ok.exchange_sig)) - { - GNUNET_break_op (0); - rr.hr.http_status = 0; - rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE; - } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the exchange is buggy - (or API version conflict); also can happen if the currency - differs (which we should obviously never support). - Just pass JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_FORBIDDEN: - /* Nothing really to verify, exchange says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - /* Requested total refunds exceed deposited amount */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_GONE: - /* Kind of normal: the money was already sent to the merchant - (it was too late for the refund). */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_PRECONDITION_FAILED: - if (GNUNET_OK != - verify_failed_dependency_ok (rh, - j)) - { - GNUNET_break (0); - rr.hr.http_status = 0; - rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE; - rr.hr.hint = "failed precondition proof returned by exchange is invalid"; - break; - } - /* Two different refund requests were made about the same deposit, but - carrying identical refund transaction ids. */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - rr.hr.ec = TALER_JSON_get_error_code (j); - rr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange refund\n", - (unsigned int) response_code, - rr.hr.ec); - break; - } - rh->cb (rh->cb_cls, - &rr); - TALER_EXCHANGE_refund_cancel (rh); -} - - -struct TALER_EXCHANGE_RefundHandle * -TALER_EXCHANGE_refund ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *keys, - const struct TALER_Amount *amount, - const struct TALER_PrivateContractHashP *h_contract_terms, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - uint64_t rtransaction_id, - const struct TALER_MerchantPrivateKeyP *merchant_priv, - TALER_EXCHANGE_RefundCallback cb, - void *cb_cls) -{ - struct TALER_MerchantPublicKeyP merchant_pub; - struct TALER_MerchantSignatureP merchant_sig; - struct TALER_EXCHANGE_RefundHandle *rh; - json_t *refund_obj; - CURL *eh; - char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; - - GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, - &merchant_pub.eddsa_pub); - TALER_merchant_refund_sign (coin_pub, - h_contract_terms, - rtransaction_id, - amount, - merchant_priv, - &merchant_sig); - { - char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "coins/%s/refund", - pub_str); - } - refund_obj = GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("refund_amount", - amount), - GNUNET_JSON_pack_data_auto ("h_contract_terms", - h_contract_terms), - GNUNET_JSON_pack_uint64 ("rtransaction_id", - rtransaction_id), - GNUNET_JSON_pack_data_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_pack_data_auto ("merchant_sig", - &merchant_sig)); - rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle); - rh->cb = cb; - rh->cb_cls = cb_cls; - rh->url = TALER_url_join (url, - arg_str, - NULL); - if (NULL == rh->url) - { - json_decref (refund_obj); - GNUNET_free (rh); - return NULL; - } - rh->h_contract_terms = *h_contract_terms; - rh->coin_pub = *coin_pub; - rh->merchant = merchant_pub; - rh->rtransaction_id = rtransaction_id; - rh->refund_amount = *amount; - eh = TALER_EXCHANGE_curl_easy_get_ (rh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&rh->ctx, - eh, - refund_obj)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (refund_obj); - GNUNET_free (rh->url); - GNUNET_free (rh); - return NULL; - } - json_decref (refund_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "URL for refund: `%s'\n", - rh->url); - rh->keys = TALER_EXCHANGE_keys_incref (keys); - rh->job = GNUNET_CURL_job_add2 (ctx, - eh, - rh->ctx.headers, - &handle_refund_finished, - rh); - return rh; -} - - -void -TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund) -{ - if (NULL != refund->job) - { - GNUNET_CURL_job_cancel (refund->job); - refund->job = NULL; - } - GNUNET_free (refund->url); - TALER_curl_easy_post_finished (&refund->ctx); - TALER_EXCHANGE_keys_decref (refund->keys); - GNUNET_free (refund); -} - - -/* end of exchange_api_refund.c */ diff --git a/src/lib/exchange_api_stefan.c b/src/lib/exchange_api_stefan.c deleted file mode 100644 index c3576cd73..000000000 --- a/src/lib/exchange_api_stefan.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/exchange_api_stefan.c - * @brief calculations on the STEFAN curve - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "exchange_api_handle.h" -#include - - -/** - * Determine smallest denomination in @a keys. - * - * @param keys exchange response to evaluate - * @return NULL on error (no denominations) - */ -static const struct TALER_Amount * -get_unit (const struct TALER_EXCHANGE_Keys *keys) -{ - const struct TALER_Amount *min = NULL; - - for (unsigned int i = 0; inum_denom_keys; i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *dk - = &keys->denom_keys[i]; - - if ( (NULL == min) || - (1 == TALER_amount_cmp (min, - /* > */ - &dk->value)) ) - min = &dk->value; - } - GNUNET_break (NULL != min); - return min; -} - - -/** - * Convert amount to double for STEFAN curve evaluation. - * - * @param a input amount - * @return (rounded) amount as a double - */ -static double -amount_to_double (const struct TALER_Amount *a) -{ - double d = (double) a->value; - - d += a->fraction / ((double) TALER_AMOUNT_FRAC_BASE); - return d; -} - - -/** - * Convert double to amount for STEFAN curve evaluation. - * - * @param dv input amount - * @param currency deisred currency - * @param[out] rval (rounded) amount as a double - */ -static void -double_to_amount (double dv, - const char *currency, - struct TALER_Amount *rval) -{ - double rem; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - rval)); - rval->value = floorl (dv); - rem = dv - ((double) rval->value); - if (rem < 0.0) - rem = 0.0; - rem *= TALER_AMOUNT_FRAC_BASE; - rval->fraction = floorl (rem); - if (rval->fraction >= TALER_AMOUNT_FRAC_BASE) - { - /* Strange, multiplication overflowed our range, - round up value instead */ - rval->fraction = 0; - rval->value += 1; - } -} - - -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_keys_stefan_b2n ( - const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_Amount *brut, - struct TALER_Amount *net) -{ - const struct TALER_Amount *min; - double log_d = amount_to_double (&keys->stefan_log); - double lin_d = keys->stefan_lin; - double abs_d = amount_to_double (&keys->stefan_abs); - double bru_d = amount_to_double (brut); - double min_d; - double fee_d; - double net_d; - - if (TALER_amount_is_zero (brut)) - { - *net = *brut; - return GNUNET_NO; - } - min = get_unit (keys); - if (NULL == min) - return GNUNET_SYSERR; - if (1.0f <= keys->stefan_lin) - { - /* This cannot work, linear STEFAN fee estimate always - exceed any gross amount. */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - min_d = amount_to_double (min); - fee_d = abs_d - + log_d * log2 (bru_d / min_d) - + lin_d * bru_d; - if (fee_d > bru_d) - { - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (brut->currency, - net)); - return GNUNET_NO; - } - net_d = bru_d - fee_d; - double_to_amount (net_d, - brut->currency, - net); - return GNUNET_OK; -} - - -/** - * Our function - * f(x) := ne + ab + lo * log2(x/mi) + li * x - x - * for #newton(). - */ -static double -eval_f (double mi, - double ab, - double lo, - double li, - double ne, - double x) -{ - return ne + ab + lo * log2 (x / mi) + li * x - x; -} - - -/** - * Our function - * f'(x) := lo / log(2) / x + li - 1 - * for #newton(). - */ -static double -eval_fp (double mi, - double lo, - double li, - double ne, - double x) -{ - return lo / log (2) / x + li - 1; -} - - -/** - * Use Newton's method to find x where f(x)=0. - * - * @return x where "eval_f(x)==0". - */ -static double -newton (double mi, - double ab, - double lo, - double li, - double ne) -{ - const double eps = 0.00000001; /* max error allowed */ - double min_ab = ne + ab; /* result cannot be smaller than this! */ - /* compute lower bounds by various heuristics */ - double min_ab_li = min_ab + min_ab * li; - double min_ab_li_lo = min_ab_li + log2 (min_ab_li / mi) * lo; - double min_ab_lo = min_ab + log2 (min_ab / mi) * lo; - double min_ab_lo_li = min_ab_lo + min_ab_lo * li; - /* take global lower bound */ - double x_min = GNUNET_MAX (min_ab_lo_li, - min_ab_li_lo); - double x = x_min; /* use lower bound as starting point */ - - /* Objective: invert - ne := br - ab - lo * log2 (br/mi) - li * br - to find 'br'. - Method: use Newton's method to find root of: - f(x) := ne + ab + lo * log2 (x/mi) + li * x - x - using also - f'(x) := lo / log(2) / x + li - 1 - */ - /* Loop to abort in case of divergence; - 100 is already very high, 2-4 is normal! */ - for (unsigned int i = 0; i<100; i++) - { - double fx = eval_f (mi, ab, lo, li, ne, x); - double fxp = eval_fp (mi, lo, li, ne, x); - double x_new = x - fx / fxp; - - if (fabs (x - x_new) <= eps) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Needed %u rounds from %f to result BRUT %f => NET: %f\n", - i, - x_min, - x_new, - x_new - ab - li * x_new - lo * log2 (x / mi)); - return x_new; - } - if (x_new < x_min) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Divergence, obtained very bad estimate %f after %u rounds!\n", - x_new, - i); - return x_min; - } - x = x_new; - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Slow convergence, returning bad estimate %f!\n", - x); - return x; -} - - -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_keys_stefan_n2b ( - const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_Amount *net, - struct TALER_Amount *brut) -{ - const struct TALER_Amount *min; - double lin_d = keys->stefan_lin; - double log_d = amount_to_double (&keys->stefan_log); - double abs_d = amount_to_double (&keys->stefan_abs); - double net_d = amount_to_double (net); - double min_d; - double brut_d; - - if (TALER_amount_is_zero (net)) - { - *brut = *net; - return GNUNET_NO; - } - min = get_unit (keys); - if (NULL == min) - return GNUNET_SYSERR; - if (1.0f <= keys->stefan_lin) - { - /* This cannot work, linear STEFAN fee estimate always - exceed any gross amount. */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - min_d = amount_to_double (min); - brut_d = newton (min_d, - abs_d, - log_d, - lin_d, - net_d); - double_to_amount (brut_d, - net->currency, - brut); - return GNUNET_OK; -} - - -void -TALER_EXCHANGE_keys_stefan_round ( - const struct TALER_EXCHANGE_Keys *keys, - struct TALER_Amount *val) -{ - const struct TALER_Amount *min; - uint32_t mod = 1; - uint32_t frac; - uint32_t rst; - - min = get_unit (keys); - if (NULL == min) - return; - frac = min->fraction; - while (0 == frac % 10) - { - mod *= 10; - frac /= 10; - } - rst = val->fraction % mod; - if (rst < mod / 2) - val->fraction -= rst; - else - val->fraction += mod - rst; -} diff --git a/src/lib/test_stefan.c b/src/lib/test_stefan.c deleted file mode 100644 index 838cca769..000000000 --- a/src/lib/test_stefan.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - 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, see - -*/ -/** - * @file lib/test_stefan.c - * @brief test calculations on the STEFAN curve - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "exchange_api_handle.h" - - -/** - * Check if @a a and @a b are numerically close. - * - * @param a an amount - * @param b an amount - * @return true if both values are quite close - */ -static bool -amount_close (const struct TALER_Amount *a, - const struct TALER_Amount *b) -{ - struct TALER_Amount delta; - - switch (TALER_amount_cmp (a, - b)) - { - case -1: /* a < b */ - GNUNET_assert (0 < - TALER_amount_subtract (&delta, - b, - a)); - break; - case 0: - /* perfect */ - return true; - case 1: /* a > b */ - GNUNET_assert (0 < - TALER_amount_subtract (&delta, - a, - b)); - break; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Rounding error is %s\n", - TALER_amount2s (&delta)); - if (delta.value > 0) - { - GNUNET_break (0); - return false; - } - if (delta.fraction > 5000) - { - GNUNET_break (0); - return false; - } - return true; /* let's consider this a rounding error */ -} - - -int -main (int argc, - char **argv) -{ - struct TALER_EXCHANGE_DenomPublicKey dk; - struct TALER_EXCHANGE_Keys keys = { - .denom_keys = &dk, - .num_denom_keys = 1 - }; - struct TALER_Amount brut; - struct TALER_Amount net; - - (void) argc; - (void) argv; - GNUNET_log_setup ("test-stefan", - "INFO", - NULL); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:0.13", - &dk.value)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:1", - &keys.stefan_abs)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:0.13", - &keys.stefan_log)); - keys.stefan_lin = 1.15; - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:4", - &brut)); - GNUNET_log_skip (1, - GNUNET_NO); - GNUNET_assert (GNUNET_SYSERR == - TALER_EXCHANGE_keys_stefan_b2n (&keys, - &brut, - &net)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:4", - &net)); - GNUNET_log_skip (1, - GNUNET_NO); - GNUNET_assert (GNUNET_SYSERR == - TALER_EXCHANGE_keys_stefan_n2b (&keys, - &net, - &brut)); - keys.stefan_lin = 1.0; - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:4", - &brut)); - GNUNET_log_skip (1, - GNUNET_NO); - GNUNET_assert (GNUNET_SYSERR == - TALER_EXCHANGE_keys_stefan_b2n (&keys, - &brut, - &net)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("MAGIC:4", - &net)); - GNUNET_log_skip (1, - GNUNET_NO); - GNUNET_assert (GNUNET_SYSERR == - TALER_EXCHANGE_keys_stefan_n2b (&keys, - &net, - &brut)); - GNUNET_assert (0 == GNUNET_get_log_skip ()); - keys.stefan_lin = 0.1; - - /* try various values for lin and log STEFAN values */ - for (unsigned int li = 1; li < 13; li += 1) - { - keys.stefan_lin = 1.0 * li / 100.0; - - for (unsigned int lx = 1; lx < 100; lx += 1) - { - keys.stefan_log.fraction = lx * TALER_AMOUNT_FRAC_BASE / 100; - - /* Check brutto-to-netto is stable */ - for (unsigned int i = 0; i<10; i++) - { - struct TALER_Amount rval; - - brut.value = i; - brut.fraction = i * TALER_AMOUNT_FRAC_BASE / 10; - GNUNET_assert (GNUNET_SYSERR != - TALER_EXCHANGE_keys_stefan_b2n (&keys, - &brut, - &net)); - GNUNET_assert (GNUNET_SYSERR != - TALER_EXCHANGE_keys_stefan_n2b (&keys, - &net, - &rval)); - if (TALER_amount_is_zero (&net)) - GNUNET_assert (TALER_amount_is_zero (&rval)); - else - { - GNUNET_assert (amount_close (&brut, - &rval)); - TALER_EXCHANGE_keys_stefan_round (&keys, - &rval); - GNUNET_assert (amount_close (&brut, - &rval)); - } - } - - /* Check netto-to-brutto is stable */ - for (unsigned int i = 0; i<10; i++) - { - struct TALER_Amount rval; - - net.value = i; - net.fraction = i * TALER_AMOUNT_FRAC_BASE / 10; - GNUNET_assert (GNUNET_SYSERR != - TALER_EXCHANGE_keys_stefan_n2b (&keys, - &net, - &brut)); - GNUNET_assert (GNUNET_SYSERR != - TALER_EXCHANGE_keys_stefan_b2n (&keys, - &brut, - &rval)); - GNUNET_assert (amount_close (&net, - &rval)); - TALER_EXCHANGE_keys_stefan_round (&keys, - &rval); - GNUNET_assert (amount_close (&net, - &rval)); - } - } - } - return 0; -} -- cgit v1.2.3 From aee7774d2c99739fe4029e930ecfe9be368876f4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 24 Oct 2023 09:22:43 +0200 Subject: Revert "[lib] delete some files" This reverts commit deabb1e506ae678eeec608649388a082ada7c11a. --- src/lib/auditor_api_curl_defaults.c | 61 ++ src/lib/auditor_api_curl_defaults.h | 38 + src/lib/auditor_api_deposit_confirmation.c | 418 ++++++++ src/lib/auditor_api_exchanges.c | 244 +++++ src/lib/auditor_api_get_config.c | 288 +++++ src/lib/exchange_api_age_withdraw.c | 1117 ++++++++++++++++++++ src/lib/exchange_api_age_withdraw_reveal.c | 471 +++++++++ src/lib/exchange_api_auditor_add_denomination.c | 238 +++++ src/lib/exchange_api_csr_melt.c | 317 ++++++ src/lib/exchange_api_kyc_check.c | 329 ++++++ src/lib/exchange_api_kyc_proof.c | 217 ++++ src/lib/exchange_api_kyc_wallet.c | 230 ++++ src/lib/exchange_api_lookup_aml_decision.c | 419 ++++++++ src/lib/exchange_api_lookup_aml_decisions.c | 378 +++++++ src/lib/exchange_api_management_add_partner.c | 218 ++++ src/lib/exchange_api_management_auditor_disable.c | 219 ++++ src/lib/exchange_api_management_auditor_enable.c | 224 ++++ src/lib/exchange_api_management_drain_profits.c | 213 ++++ src/lib/exchange_api_management_post_extensions.c | 213 ++++ src/lib/exchange_api_management_post_keys.c | 237 +++++ ...change_api_management_revoke_denomination_key.c | 221 ++++ .../exchange_api_management_revoke_signing_key.c | 211 ++++ src/lib/exchange_api_management_set_global_fee.c | 235 ++++ src/lib/exchange_api_management_set_wire_fee.c | 227 ++++ .../exchange_api_management_update_aml_officer.c | 229 ++++ src/lib/exchange_api_management_wire_disable.c | 220 ++++ src/lib/exchange_api_management_wire_enable.c | 245 +++++ src/lib/exchange_api_melt.c | 596 +++++++++++ src/lib/exchange_api_recoup.c | 369 +++++++ src/lib/exchange_api_recoup_refresh.c | 363 +++++++ src/lib/exchange_api_refresh_common.c | 249 +++++ src/lib/exchange_api_refresh_common.h | 201 ++++ src/lib/exchange_api_refreshes_reveal.c | 531 ++++++++++ src/lib/exchange_api_refund.c | 480 +++++++++ src/lib/exchange_api_stefan.c | 320 ++++++ src/lib/test_stefan.c | 206 ++++ 36 files changed, 10992 insertions(+) create mode 100644 src/lib/auditor_api_curl_defaults.c create mode 100644 src/lib/auditor_api_curl_defaults.h create mode 100644 src/lib/auditor_api_deposit_confirmation.c create mode 100644 src/lib/auditor_api_exchanges.c create mode 100644 src/lib/auditor_api_get_config.c create mode 100644 src/lib/exchange_api_age_withdraw.c create mode 100644 src/lib/exchange_api_age_withdraw_reveal.c create mode 100644 src/lib/exchange_api_auditor_add_denomination.c create mode 100644 src/lib/exchange_api_csr_melt.c create mode 100644 src/lib/exchange_api_kyc_check.c create mode 100644 src/lib/exchange_api_kyc_proof.c create mode 100644 src/lib/exchange_api_kyc_wallet.c create mode 100644 src/lib/exchange_api_lookup_aml_decision.c create mode 100644 src/lib/exchange_api_lookup_aml_decisions.c create mode 100644 src/lib/exchange_api_management_add_partner.c create mode 100644 src/lib/exchange_api_management_auditor_disable.c create mode 100644 src/lib/exchange_api_management_auditor_enable.c create mode 100644 src/lib/exchange_api_management_drain_profits.c create mode 100644 src/lib/exchange_api_management_post_extensions.c create mode 100644 src/lib/exchange_api_management_post_keys.c create mode 100644 src/lib/exchange_api_management_revoke_denomination_key.c create mode 100644 src/lib/exchange_api_management_revoke_signing_key.c create mode 100644 src/lib/exchange_api_management_set_global_fee.c create mode 100644 src/lib/exchange_api_management_set_wire_fee.c create mode 100644 src/lib/exchange_api_management_update_aml_officer.c create mode 100644 src/lib/exchange_api_management_wire_disable.c create mode 100644 src/lib/exchange_api_management_wire_enable.c create mode 100644 src/lib/exchange_api_melt.c create mode 100644 src/lib/exchange_api_recoup.c create mode 100644 src/lib/exchange_api_recoup_refresh.c create mode 100644 src/lib/exchange_api_refresh_common.c create mode 100644 src/lib/exchange_api_refresh_common.h create mode 100644 src/lib/exchange_api_refreshes_reveal.c create mode 100644 src/lib/exchange_api_refund.c create mode 100644 src/lib/exchange_api_stefan.c create mode 100644 src/lib/test_stefan.c diff --git a/src/lib/auditor_api_curl_defaults.c b/src/lib/auditor_api_curl_defaults.c new file mode 100644 index 000000000..81fcd7bac --- /dev/null +++ b/src/lib/auditor_api_curl_defaults.c @@ -0,0 +1,61 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/auditor_api_curl_defaults.c + * @brief curl easy handle defaults + * @author Florian Dold + */ +#include "auditor_api_curl_defaults.h" + + +CURL * +TALER_AUDITOR_curl_easy_get_ (const char *url) +{ + CURL *eh; + struct GNUNET_AsyncScopeSave scope; + + GNUNET_async_scope_get (&scope); + + eh = curl_easy_init (); + if (NULL == eh) + return NULL; + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_FOLLOWLOCATION, + 1L)); + /* Enable compression (using whatever curl likes), see + https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html */ + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_ACCEPT_ENCODING, + "")); + /* limit MAXREDIRS to 5 as a simple security measure against + a potential infinite loop caused by a malicious target */ + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_MAXREDIRS, + 5L)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TCP_FASTOPEN, + 1L)); + return eh; +} diff --git a/src/lib/auditor_api_curl_defaults.h b/src/lib/auditor_api_curl_defaults.h new file mode 100644 index 000000000..99e1e07e6 --- /dev/null +++ b/src/lib/auditor_api_curl_defaults.h @@ -0,0 +1,38 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/auditor_api_curl_defaults.h + * @brief curl easy handle defaults + * @author Florian Dold + */ +#ifndef _TALER_CURL_DEFAULTS_H +#define _TALER_CURL_DEFAULTS_H + +#include "platform.h" +#include + + +/** + * Get a curl handle with the right defaults + * for the auditor lib. In the future, we might manage a pool of connections here. + * + * @param url URL to query + */ +CURL * +TALER_AUDITOR_curl_easy_get_ (const char *url); + +#endif /* _TALER_CURL_DEFAULTS_H */ diff --git a/src/lib/auditor_api_deposit_confirmation.c b/src/lib/auditor_api_deposit_confirmation.c new file mode 100644 index 000000000..1e2ecc6cc --- /dev/null +++ b/src/lib/auditor_api_deposit_confirmation.c @@ -0,0 +1,418 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/auditor_api_deposit_confirmation.c + * @brief Implementation of the /deposit request of the auditor's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_util.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" +#include "taler_auditor_service.h" +#include "taler_signatures.h" +#include "auditor_api_curl_defaults.h" + + +/** + * @brief A DepositConfirmation Handle + */ +struct TALER_AUDITOR_DepositConfirmationHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_AUDITOR_DepositConfirmationResultCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + +}; + + +/** + * Function called when we're done processing the + * HTTP /deposit-confirmation request. + * + * @param cls the `struct TALER_AUDITOR_DepositConfirmationHandle` + * @param response_code HTTP response code, 0 on error + * @param djson parsed JSON result, NULL on error + */ +static void +handle_deposit_confirmation_finished (void *cls, + long response_code, + const void *djson) +{ + const json_t *json = djson; + struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls; + struct TALER_AUDITOR_DepositConfirmationResponse dcr = { + .hr.reply = json, + .hr.http_status = (unsigned int) response_code + }; + + dh->job = NULL; + switch (response_code) + { + case 0: + dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + dcr.hr.ec = TALER_EC_NONE; + break; + case MHD_HTTP_BAD_REQUEST: + dcr.hr.ec = TALER_JSON_get_error_code (json); + dcr.hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us or the auditor is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + dcr.hr.ec = TALER_JSON_get_error_code (json); + dcr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, auditor says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + dcr.hr.ec = TALER_JSON_get_error_code (json); + dcr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_GONE: + dcr.hr.ec = TALER_JSON_get_error_code (json); + dcr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, auditor says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + dcr.hr.ec = TALER_JSON_get_error_code (json); + dcr.hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + dcr.hr.ec = TALER_JSON_get_error_code (json); + dcr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for auditor deposit confirmation\n", + (unsigned int) response_code, + dcr.hr.ec); + break; + } + dh->cb (dh->cb_cls, + &dcr); + TALER_AUDITOR_deposit_confirmation_cancel (dh); +} + + +/** + * Verify signature information about the deposit-confirmation. + * + * @param h_wire hash of merchant wire details + * @param h_policy hash over the policy extension, if any + * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor) + * @param exchange_timestamp timestamp when the deposit was received by the wallet + * @param wire_deadline by what time must the amount be wired to the merchant + * @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline + * @param amount_without_fee the amount confirmed to be wired by the exchange to the merchant + * @param coin_pub coin’s public key + * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) + * @param exchange_sig the signature made with purpose #TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT + * @param exchange_pub the public key of the exchange that matches @a exchange_sig + * @param master_pub master public key of the exchange + * @param ep_start when does @a exchange_pub validity start + * @param ep_expire when does @a exchange_pub usage end + * @param ep_end when does @a exchange_pub legal validity end + * @param master_sig master signature affirming validity of @a exchange_pub + * @return #GNUNET_OK if signatures are OK, #GNUNET_SYSERR if not + */ +static enum GNUNET_GenericReturnValue +verify_signatures ( + const struct TALER_MerchantWireHashP *h_wire, + const struct TALER_ExtensionPolicyHashP *h_policy, + const struct TALER_PrivateContractHashP *h_contract_terms, + struct GNUNET_TIME_Timestamp exchange_timestamp, + struct GNUNET_TIME_Timestamp wire_deadline, + struct GNUNET_TIME_Timestamp refund_deadline, + const struct TALER_Amount *amount_without_fee, + unsigned int num_coins, + const struct TALER_CoinSpendSignatureP *coin_sigs[ + static num_coins], + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_ExchangeSignatureP *exchange_sig, + const struct TALER_MasterPublicKeyP *master_pub, + struct GNUNET_TIME_Timestamp ep_start, + struct GNUNET_TIME_Timestamp ep_expire, + struct GNUNET_TIME_Timestamp ep_end, + const struct TALER_MasterSignatureP *master_sig) +{ + if (GNUNET_OK != + TALER_exchange_online_deposit_confirmation_verify ( + h_contract_terms, + h_wire, + h_policy, + exchange_timestamp, + wire_deadline, + refund_deadline, + amount_without_fee, + num_coins, + coin_sigs, + merchant_pub, + exchange_pub, + exchange_sig)) + { + GNUNET_break_op (0); + TALER_LOG_WARNING ( + "Invalid signature on /deposit-confirmation request!\n"); + { + TALER_LOG_DEBUG ("... amount_without_fee was %s\n", + TALER_amount2s (amount_without_fee)); + } + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + TALER_exchange_offline_signkey_validity_verify ( + exchange_pub, + ep_start, + ep_expire, + ep_end, + master_pub, + master_sig)) + { + GNUNET_break (0); + TALER_LOG_WARNING ("Invalid signature on exchange signing key!\n"); + return GNUNET_SYSERR; + } + if (GNUNET_TIME_absolute_is_past (ep_end.abs_time)) + { + GNUNET_break (0); + TALER_LOG_WARNING ("Exchange signing key is no longer valid!\n"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +struct TALER_AUDITOR_DepositConfirmationHandle * +TALER_AUDITOR_deposit_confirmation ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_MerchantWireHashP *h_wire, + const struct TALER_ExtensionPolicyHashP *h_policy, + const struct TALER_PrivateContractHashP *h_contract_terms, + struct GNUNET_TIME_Timestamp exchange_timestamp, + struct GNUNET_TIME_Timestamp wire_deadline, + struct GNUNET_TIME_Timestamp refund_deadline, + const struct TALER_Amount *total_without_fee, + unsigned int num_coins, + const struct TALER_CoinSpendPublicKeyP *coin_pubs[ + static num_coins], + const struct TALER_CoinSpendSignatureP *coin_sigs[ + static num_coins], + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_ExchangeSignatureP *exchange_sig, + const struct TALER_MasterPublicKeyP *master_pub, + struct GNUNET_TIME_Timestamp ep_start, + struct GNUNET_TIME_Timestamp ep_expire, + struct GNUNET_TIME_Timestamp ep_end, + const struct TALER_MasterSignatureP *master_sig, + TALER_AUDITOR_DepositConfirmationResultCallback cb, + void *cb_cls) +{ + struct TALER_AUDITOR_DepositConfirmationHandle *dh; + json_t *deposit_confirmation_obj; + CURL *eh; + json_t *jcoin_sigs; + json_t *jcoin_pubs; + + if (0 == num_coins) + { + GNUNET_break (0); + return NULL; + } + if (GNUNET_OK != + verify_signatures (h_wire, + h_policy, + h_contract_terms, + exchange_timestamp, + wire_deadline, + refund_deadline, + total_without_fee, + num_coins, + coin_sigs, + merchant_pub, + exchange_pub, + exchange_sig, + master_pub, + ep_start, + ep_expire, + ep_end, + master_sig)) + { + GNUNET_break_op (0); + return NULL; + } + jcoin_sigs = json_array (); + GNUNET_assert (NULL != jcoin_sigs); + jcoin_pubs = json_array (); + GNUNET_assert (NULL != jcoin_pubs); + for (unsigned int i = 0; icb = cb; + dh->cb_cls = cb_cls; + dh->url = TALER_url_join (url, + "deposit-confirmation", + NULL); + if (NULL == dh->url) + { + GNUNET_free (dh); + return NULL; + } + eh = TALER_AUDITOR_curl_easy_get_ (dh->url); + if ( (NULL == eh) || + (CURLE_OK != + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + "PUT")) || + (GNUNET_OK != + TALER_curl_easy_post (&dh->ctx, + eh, + deposit_confirmation_obj)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (deposit_confirmation_obj); + GNUNET_free (dh->url); + GNUNET_free (dh); + return NULL; + } + json_decref (deposit_confirmation_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "URL for deposit-confirmation: `%s'\n", + dh->url); + dh->job = GNUNET_CURL_job_add2 (ctx, + eh, + dh->ctx.headers, + &handle_deposit_confirmation_finished, + dh); + { + /* Disable 100 continue processing */ + struct curl_slist *x_headers; + + x_headers = curl_slist_append (NULL, + "Expect:"); + GNUNET_CURL_extend_headers (dh->job, + x_headers); + curl_slist_free_all (x_headers); + } + return dh; +} + + +void +TALER_AUDITOR_deposit_confirmation_cancel ( + struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation) +{ + if (NULL != deposit_confirmation->job) + { + GNUNET_CURL_job_cancel (deposit_confirmation->job); + deposit_confirmation->job = NULL; + } + GNUNET_free (deposit_confirmation->url); + TALER_curl_easy_post_finished (&deposit_confirmation->ctx); + GNUNET_free (deposit_confirmation); +} + + +/* end of auditor_api_deposit_confirmation.c */ diff --git a/src/lib/auditor_api_exchanges.c b/src/lib/auditor_api_exchanges.c new file mode 100644 index 000000000..897dfe60f --- /dev/null +++ b/src/lib/auditor_api_exchanges.c @@ -0,0 +1,244 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/auditor_api_exchanges.c + * @brief Implementation of the /exchanges request of the auditor's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_auditor_service.h" +#include "taler_util.h" +#include "taler_curl_lib.h" +#include "taler_signatures.h" +#include "auditor_api_curl_defaults.h" + +/** + * How many exchanges do we allow a single auditor to + * audit at most? + */ +#define MAX_EXCHANGES 1024 + + +/** + * @brief A ListExchanges Handle + */ +struct TALER_AUDITOR_ListExchangesHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_AUDITOR_ListExchangesResultCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + +}; + + +/** + * Function called when we're done processing the + * HTTP /exchanges request. + * + * @param cls the `struct TALER_AUDITOR_ListExchangesHandle` + * @param response_code HTTP response code, 0 on error + * @param djson parsed JSON result, NULL on error + */ +static void +handle_exchanges_finished (void *cls, + long response_code, + const void *djson) +{ + const json_t *json = djson; + const json_t *ja; + unsigned int ja_len; + struct TALER_AUDITOR_ListExchangesHandle *leh = cls; + struct TALER_AUDITOR_ListExchangesResponse ler = { + .hr.reply = json, + .hr.http_status = (unsigned int) response_code + }; + + leh->job = NULL; + switch (response_code) + { + case 0: + ler.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + ja = json_object_get (json, + "exchanges"); + if ( (NULL == ja) || + (! json_is_array (ja)) ) + { + GNUNET_break (0); + ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + ler.hr.http_status = 0; + break; + } + + ja_len = json_array_size (ja); + if (ja_len > MAX_EXCHANGES) + { + GNUNET_break (0); + ler.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + ler.hr.http_status = 0; + break; + } + { + struct TALER_AUDITOR_ExchangeInfo ei[GNUNET_NZL (ja_len)]; + bool ok = true; + + for (unsigned int i = 0; icb (leh->cb_cls, + &ler); + TALER_AUDITOR_list_exchanges_cancel (leh); + return; + } + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the auditor is buggy + (or API version conflict); just pass JSON reply to the application */ + ler.hr.ec = TALER_JSON_get_error_code (json); + ler.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + ler.hr.ec = TALER_JSON_get_error_code (json); + ler.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + ler.hr.ec = TALER_JSON_get_error_code (json); + ler.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + ler.hr.ec = TALER_JSON_get_error_code (json); + ler.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for auditor list-exchanges request\n", + (unsigned int) response_code, + (int) ler.hr.ec); + GNUNET_break_op (0); + break; + } + if (NULL != leh->cb) + leh->cb (leh->cb_cls, + &ler); + TALER_AUDITOR_list_exchanges_cancel (leh); +} + + +struct TALER_AUDITOR_ListExchangesHandle * +TALER_AUDITOR_list_exchanges (struct GNUNET_CURL_Context *ctx, + const char *url, + TALER_AUDITOR_ListExchangesResultCallback cb, + void *cb_cls) +{ + struct TALER_AUDITOR_ListExchangesHandle *leh; + CURL *eh; + + leh = GNUNET_new (struct TALER_AUDITOR_ListExchangesHandle); + leh->cb = cb; + leh->cb_cls = cb_cls; + leh->url = TALER_url_join (url, + "exchanges", + NULL); + if (NULL == leh->url) + { + GNUNET_free (leh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "URL for list-exchanges: `%s'\n", + leh->url); + eh = TALER_AUDITOR_curl_easy_get_ (leh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (leh->url); + GNUNET_free (leh); + return NULL; + } + leh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_exchanges_finished, + leh); + return leh; +} + + +void +TALER_AUDITOR_list_exchanges_cancel ( + struct TALER_AUDITOR_ListExchangesHandle *leh) +{ + if (NULL != leh->job) + { + GNUNET_CURL_job_cancel (leh->job); + leh->job = NULL; + } + GNUNET_free (leh->url); + GNUNET_free (leh); +} + + +/* end of auditor_api_exchanges.c */ diff --git a/src/lib/auditor_api_get_config.c b/src/lib/auditor_api_get_config.c new file mode 100644 index 000000000..c9f366568 --- /dev/null +++ b/src/lib/auditor_api_get_config.c @@ -0,0 +1,288 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/auditor_api_get_config.c + * @brief Implementation of /config for the auditor's HTTP API + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_json_lib.h" +#include "taler_auditor_service.h" +#include "taler_signatures.h" +#include "auditor_api_curl_defaults.h" + + +/** + * Which revision of the Taler auditor protocol is implemented + * by this library? Used to determine compatibility. + */ +#define TALER_PROTOCOL_CURRENT 0 + +/** + * How many revisions back are we compatible to? + */ +#define TALER_PROTOCOL_AGE 0 + + +/** + * Log error related to CURL operations. + * + * @param type log level + * @param function which function failed to run + * @param code what was the curl error code + */ +#define CURL_STRERROR(type, function, code) \ + GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \ + function, __FILE__, __LINE__, curl_easy_strerror (code)); + + +/** + * Handle for the get config request. + */ +struct TALER_AUDITOR_GetConfigHandle +{ + /** + * The context of this handle + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Function to call with the auditor's certification data, + * NULL if this has already been done. + */ + TALER_AUDITOR_ConfigCallback config_cb; + + /** + * Closure to pass to @e config_cb. + */ + void *config_cb_cls; + + /** + * Data for the request to get the /config of a auditor, + * NULL once we are past stage #MHS_INIT. + */ + struct GNUNET_CURL_Job *vr; + + /** + * The url for the @e vr job. + */ + char *vr_url; + +}; + + +/* ***************** Internal /config fetching ************* */ + +/** + * Decode the JSON in @a resp_obj from the /config response and store the data + * in the @a key_data. + * + * @param[in] resp_obj JSON object to parse + * @param[in,out] vi where to store the results we decoded + * @param[out] vc where to store config compatibility data + * @return #TALER_EC_NONE on success + */ +static enum TALER_ErrorCode +decode_config_json (const json_t *resp_obj, + struct TALER_AUDITOR_ConfigInformation *vi, + enum TALER_AUDITOR_VersionCompatibility *vc) +{ + unsigned int age; + unsigned int revision; + unsigned int current; + char dummy; + const char *ver; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("version", + &ver), + GNUNET_JSON_spec_fixed_auto ("auditor_public_key", + &vi->auditor_pub), + GNUNET_JSON_spec_end () + }; + + if (JSON_OBJECT != json_typeof (resp_obj)) + { + GNUNET_break_op (0); + return TALER_EC_GENERIC_JSON_INVALID; + } + /* check the config */ + if (GNUNET_OK != + GNUNET_JSON_parse (resp_obj, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return TALER_EC_GENERIC_JSON_INVALID; + } + if (3 != sscanf (ver, + "%u:%u:%u%c", + ¤t, + &revision, + &age, + &dummy)) + { + GNUNET_break_op (0); + return TALER_EC_GENERIC_VERSION_MALFORMED; + } + vi->version = ver; + *vc = TALER_AUDITOR_VC_MATCH; + if (TALER_PROTOCOL_CURRENT < current) + { + *vc |= TALER_AUDITOR_VC_NEWER; + if (TALER_PROTOCOL_CURRENT < current - age) + *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; + } + if (TALER_PROTOCOL_CURRENT > current) + { + *vc |= TALER_AUDITOR_VC_OLDER; + if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > current) + *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; + } + return TALER_EC_NONE; +} + + +/** + * Callback used when downloading the reply to a /config request + * is complete. + * + * @param cls the `struct TALER_AUDITOR_GetConfigHandle` + * @param response_code HTTP response code, 0 on error + * @param gresp_obj parsed JSON result, NULL on error, must be a `const json_t *` + */ +static void +config_completed_cb (void *cls, + long response_code, + const void *gresp_obj) +{ + struct TALER_AUDITOR_GetConfigHandle *auditor = cls; + const json_t *resp_obj = gresp_obj; + struct TALER_AUDITOR_ConfigResponse vr = { + .hr.reply = resp_obj, + .hr.http_status = (unsigned int) response_code + }; + + auditor->vr = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received config from URL `%s' with status %ld.\n", + auditor->vr_url, + response_code); + switch (response_code) + { + case 0: + GNUNET_break_op (0); + vr.hr.ec = TALER_EC_INVALID; + break; + case MHD_HTTP_OK: + if (NULL == resp_obj) + { + GNUNET_break_op (0); + vr.hr.http_status = 0; + vr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + vr.hr.ec = decode_config_json (resp_obj, + &vr.details.ok.vi, + &vr.details.ok.compat); + if (TALER_EC_NONE != vr.hr.ec) + { + GNUNET_break_op (0); + vr.hr.http_status = 0; + break; + } + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + vr.hr.ec = TALER_JSON_get_error_code (resp_obj); + vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + break; + default: + vr.hr.ec = TALER_JSON_get_error_code (resp_obj); + vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) vr.hr.ec); + break; + } + auditor->config_cb (auditor->config_cb_cls, + &vr); + TALER_AUDITOR_get_config_cancel (auditor); +} + + +struct TALER_AUDITOR_GetConfigHandle * +TALER_AUDITOR_get_config (struct GNUNET_CURL_Context *ctx, + const char *url, + TALER_AUDITOR_ConfigCallback config_cb, + void *config_cb_cls) +{ + struct TALER_AUDITOR_GetConfigHandle *auditor; + CURL *eh; + + auditor = GNUNET_new (struct TALER_AUDITOR_GetConfigHandle); + auditor->config_cb = config_cb; + auditor->config_cb_cls = config_cb_cls; + auditor->ctx = ctx; + auditor->vr_url = TALER_url_join (url, + "config", + NULL); + if (NULL == auditor->vr_url) + { + GNUNET_break (0); + GNUNET_free (auditor->vr_url); + GNUNET_free (auditor); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting auditor config with URL `%s'.\n", + auditor->vr_url); + eh = TALER_AUDITOR_curl_easy_get_ (auditor->vr_url); + if (NULL == eh) + { + GNUNET_break (0); + TALER_AUDITOR_get_config_cancel (auditor); + return NULL; + } + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_TIMEOUT, + (long) 300)); + auditor->vr = GNUNET_CURL_job_add (auditor->ctx, + eh, + &config_completed_cb, + auditor); + return auditor; +} + + +void +TALER_AUDITOR_get_config_cancel (struct TALER_AUDITOR_GetConfigHandle *auditor) +{ + if (NULL != auditor->vr) + { + GNUNET_CURL_job_cancel (auditor->vr); + auditor->vr = NULL; + } + GNUNET_free (auditor->vr_url); + GNUNET_free (auditor); +} + + +/* end of auditor_api_get_config.c */ diff --git a/src/lib/exchange_api_age_withdraw.c b/src/lib/exchange_api_age_withdraw.c new file mode 100644 index 000000000..ea9c0371e --- /dev/null +++ b/src/lib/exchange_api_age_withdraw.c @@ -0,0 +1,1117 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_age_withdraw.c + * @brief Implementation of /reserves/$RESERVE_PUB/age-withdraw requests + * @author Özgür Kesim + */ + +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include +#include "taler_curl_lib.h" +#include "taler_error_codes.h" +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_common.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" +#include "taler_util.h" + +/** + * A CoinCandidate is populated from a master secret + */ +struct CoinCandidate +{ + /** + * Master key material for the coin candidates. + */ + struct TALER_PlanchetMasterSecretP secret; + + /** + * The details derived form the master secrets + */ + struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details; + + /** + * Blinded hash of the coin + **/ + struct TALER_BlindedCoinHashP blinded_coin_h; + +}; + + +/** + * Closure for a call to /csr-withdraw, contains data that is needed to process + * the result. + */ +struct CSRClosure +{ + /* Points to the actual candidate in CoinData.coin_candidates, to continue + * to build its contents based on the results from /csr-withdraw */ + struct CoinCandidate *candidate; + + /* The planchet to finally generate. Points to the corresponding candidate + * in CoindData.planchet_details */ + struct TALER_PlanchetDetail *planchet; + + /* Handler to the originating call to /age-withdraw, needed to either + * cancel the running age-withdraw request (on failure of the current call + * to /csr-withdraw), or to eventually perform the protocol, once all + * csr-withdraw requests have successfully finished. */ + struct TALER_EXCHANGE_AgeWithdrawHandle *age_withdraw_handle; + + /* Denomination information, needed for CS coins for the + * step after /csr-withdraw */ + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + + /* Handler for the CS R request */ + struct TALER_EXCHANGE_CsRWithdrawHandle *csr_withdraw_handle; +}; + +/** + * Data we keep per coin in the batch. + */ +struct CoinData +{ + /** + * The denomination of the coin. Must support age restriction, i.e + * its .keys.age_mask MUST not be 0 + */ + struct TALER_EXCHANGE_DenomPublicKey denom_pub; + + /** + * The Candidates for the coin + */ + struct CoinCandidate coin_candidates[TALER_CNC_KAPPA]; + + /** + * Details of the planchet(s). + */ + struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA]; + + /** + * Closure for each candidate of type CS for the preflight request to + * /csr-withdraw + */ + struct CSRClosure csr_cls[TALER_CNC_KAPPA]; +}; + +/** + * A /reserves/$RESERVE_PUB/age-withdraw request-handle for calls with + * pre-blinded planchets. Returned by TALER_EXCHANGE_age_withdraw_blinded. + */ +struct TALER_EXCHANGE_AgeWithdrawBlindedHandle +{ + + /** + * Reserve private key. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Reserve public key, calculated + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Signature of the reserve for the request, calculated after all + * parameters for the coins are collected. + */ + struct TALER_ReserveSignatureP reserve_sig; + + /* + * The denomination keys of the exchange + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * The age mask, extacted from the denominations. + * MUST be the same for all denominations + * + */ + struct TALER_AgeMask age_mask; + + /** + * Maximum age to commit to. + */ + uint8_t max_age; + + /** + * The commitment calculated as SHA512 hash over all blinded_coin_h + */ + struct TALER_AgeWithdrawCommitmentHashP h_commitment; + + /** + * Total amount requested (value plus withdraw fee). + */ + struct TALER_Amount amount_with_fee; + + /** + * Length of the @e blinded_input Array + */ + size_t num_input; + + /** + * The blinded planchet input for the call to /age-withdraw via + * TALER_EXCHANGE_age_withdraw_blinded + */ + const struct TALER_EXCHANGE_AgeWithdrawBlindedInput *blinded_input; + + /** + * The url for this request. + */ + char *request_url; + + /** + * Context for curl. + */ + struct GNUNET_CURL_Context *curl_ctx; + + /** + * CURL handle for the request job. + */ + struct GNUNET_CURL_Job *job; + + /** + * Post Context + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Function to call with age-withdraw response results. + */ + TALER_EXCHANGE_AgeWithdrawBlindedCallback callback; + + /** + * Closure for @e blinded_callback + */ + void *callback_cls; +}; + +/** + * A /reserves/$RESERVE_PUB/age-withdraw request-handle for calls from + * a wallet, i. e. when blinding data is available. + */ +struct TALER_EXCHANGE_AgeWithdrawHandle +{ + + /** + * Length of the @e coin_data Array + */ + size_t num_coins; + + /** + * The base-URL of the exchange. + */ + const char *exchange_url; + + /** + * Reserve private key. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Reserve public key, calculated + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Signature of the reserve for the request, calculated after all + * parameters for the coins are collected. + */ + struct TALER_ReserveSignatureP reserve_sig; + + /* + * The denomination keys of the exchange + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * The age mask, extacted from the denominations. + * MUST be the same for all denominations + * + */ + struct TALER_AgeMask age_mask; + + /** + * Maximum age to commit to. + */ + uint8_t max_age; + + /** + * Array of per-coin data + */ + struct CoinData *coin_data; + + /** + * Context for curl. + */ + struct GNUNET_CURL_Context *curl_ctx; + + struct + { + /** + * Number of /csr-withdraw requests still pending. + */ + unsigned int pending; + + /** + * CURL handle for the request job. + */ + struct GNUNET_CURL_Job *job; + } csr; + + + /** + * Function to call with age-withdraw response results. + */ + TALER_EXCHANGE_AgeWithdrawCallback callback; + + /** + * Closure for @e age_withdraw_cb + */ + void *callback_cls; + + /* The Handler for the actual call to the exchange */ + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *procotol_handle; +}; + +/** + * We got a 200 OK response for the /reserves/$RESERVE_PUB/age-withdraw operation. + * Extract the noreveal_index and return it to the caller. + * + * @param awbh operation handle + * @param j_response reply from the exchange + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static enum GNUNET_GenericReturnValue +reserve_age_withdraw_ok ( + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh, + const json_t *j_response) +{ + struct TALER_EXCHANGE_AgeWithdrawBlindedResponse response = { + .hr.reply = j_response, + .hr.http_status = MHD_HTTP_OK, + .details.ok.h_commitment = awbh->h_commitment + }; + struct TALER_ExchangeSignatureP exchange_sig; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint8 ("noreveal_index", + &response.details.ok.noreveal_index), + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + &response.details.ok.exchange_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK!= + GNUNET_JSON_parse (j_response, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + TALER_exchange_online_age_withdraw_confirmation_verify ( + &awbh->h_commitment, + response.details.ok.noreveal_index, + &response.details.ok.exchange_pub, + &exchange_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + + } + + awbh->callback (awbh->callback_cls, + &response); + /* make sure the callback isn't called again */ + awbh->callback = NULL; + + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /reserves/$RESERVE_PUB/age-withdraw request. + * + * @param cls the `struct TALER_EXCHANGE_AgeWithdrawHandle` + * @param response_code The HTTP response code + * @param response response data + */ +static void +handle_reserve_age_withdraw_blinded_finished ( + void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh = cls; + const json_t *j_response = response; + struct TALER_EXCHANGE_AgeWithdrawBlindedResponse awbr = { + .hr.reply = j_response, + .hr.http_status = (unsigned int) response_code + }; + + awbh->job = NULL; + switch (response_code) + { + case 0: + awbr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + reserve_age_withdraw_ok (awbh, + j_response)) + { + GNUNET_break_op (0); + awbr.hr.http_status = 0; + awbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + GNUNET_assert (NULL == awbh->callback); + TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); + return; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_FORBIDDEN: + GNUNET_break_op (0); + /* Nothing really to verify, exchange says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, the exchange basically just says + that it doesn't know this reserve. Can happen if we + query before the wire transfer went through. + We should simply pass the JSON reply to the application. */ + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_CONFLICT: + /* The age requirements might not have been met */ + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_GONE: + /* could happen if denomination was revoked */ + /* Note: one might want to check /keys for revocation + signature here, alas tricky in case our /keys + is outdated => left to clients */ + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* only validate reply is well-formed */ + { + uint64_t ptu; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint64 ("requirement_row", + &ptu), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j_response, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + awbr.hr.http_status = 0; + awbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + } + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + awbr.hr.ec = TALER_JSON_get_error_code (j_response); + awbr.hr.hint = TALER_JSON_get_error_hint (j_response); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange age-withdraw\n", + (unsigned int) response_code, + (int) awbr.hr.ec); + break; + } + awbh->callback (awbh->callback_cls, + &awbr); + TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); +} + + +/** + * Runs the actual age-withdraw operation with the blinded planchets. + * + * @param[in,out] awbh age withdraw handler + */ +static void +perform_protocol ( + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh) +{ +#define FAIL_IF(cond) \ + do { \ + if ((cond)) \ + { \ + GNUNET_break (! (cond)); \ + goto ERROR; \ + } \ + } while(0) + + struct GNUNET_HashContext *coins_hctx; + json_t *j_denoms = NULL; + json_t *j_array_candidates = NULL; + json_t *j_request_body = NULL; + CURL *curlh = NULL; + + GNUNET_assert (0 < awbh->num_input); + awbh->age_mask = awbh->blinded_input[0].denom_pub->key.age_mask; + + FAIL_IF (GNUNET_OK != + TALER_amount_set_zero (awbh->keys->currency, + &awbh->amount_with_fee)); + /* Accumulate total value with fees */ + for (size_t i = 0; i < awbh->num_input; i++) + { + struct TALER_Amount coin_total; + const struct TALER_EXCHANGE_DenomPublicKey *dpub = + awbh->blinded_input[i].denom_pub; + + FAIL_IF (0 > + TALER_amount_add (&coin_total, + &dpub->fees.withdraw, + &dpub->value)); + FAIL_IF (0 > + TALER_amount_add (&awbh->amount_with_fee, + &awbh->amount_with_fee, + &coin_total)); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Attempting to age-withdraw from reserve %s with maximum age %d\n", + TALER_B2S (&awbh->reserve_pub), + awbh->max_age); + + coins_hctx = GNUNET_CRYPTO_hash_context_start (); + FAIL_IF (NULL == coins_hctx); + + + j_denoms = json_array (); + j_array_candidates = json_array (); + FAIL_IF ((NULL == j_denoms) || + (NULL == j_array_candidates)); + + for (size_t i = 0; i< awbh->num_input; i++) + { + /* Build the denomination array */ + { + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub = + awbh->blinded_input[i].denom_pub; + const struct TALER_DenominationHashP *denom_h = &denom_pub->h_key; + json_t *jdenom; + + /* The mask must be the same for all coins */ + FAIL_IF (awbh->age_mask.bits != denom_pub->key.age_mask.bits); + + jdenom = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto (NULL, + denom_h)); + FAIL_IF (NULL == jdenom); + FAIL_IF (0 < json_array_append_new (j_denoms, + jdenom)); + + /* Build the candidate array */ + { + json_t *j_can = json_array (); + FAIL_IF (NULL == j_can); + + for (size_t k = 0; k < TALER_CNC_KAPPA; k++) + { + struct TALER_BlindedCoinHashP bch; + const struct TALER_PlanchetDetail *planchet = + &awbh->blinded_input[i].planchet_details[k]; + json_t *jc = GNUNET_JSON_PACK ( + TALER_JSON_pack_blinded_planchet ( + NULL, + &planchet->blinded_planchet)); + + FAIL_IF (NULL == jc); + FAIL_IF (0 < json_array_append_new (j_can, + jc)); + + TALER_coin_ev_hash (&planchet->blinded_planchet, + &planchet->denom_pub_hash, + &bch); + + GNUNET_CRYPTO_hash_context_read (coins_hctx, + &bch, + sizeof(bch)); + } + + FAIL_IF (0 < json_array_append_new (j_array_candidates, + j_can)); + } + } + } + + /* Build the hash of the commitment */ + GNUNET_CRYPTO_hash_context_finish (coins_hctx, + &awbh->h_commitment.hash); + + /* Sign the request */ + TALER_wallet_age_withdraw_sign (&awbh->h_commitment, + &awbh->amount_with_fee, + &awbh->age_mask, + awbh->max_age, + awbh->reserve_priv, + &awbh->reserve_sig); + + /* Initiate the POST-request */ + j_request_body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("denom_hs", j_denoms), + GNUNET_JSON_pack_array_steal ("blinded_coin_evs", j_array_candidates), + GNUNET_JSON_pack_uint64 ("max_age", awbh->max_age), + GNUNET_JSON_pack_data_auto ("reserve_sig", &awbh->reserve_sig)); + FAIL_IF (NULL == j_request_body); + + curlh = TALER_EXCHANGE_curl_easy_get_ (awbh->request_url); + FAIL_IF (NULL == curlh); + FAIL_IF (GNUNET_OK != + TALER_curl_easy_post (&awbh->post_ctx, + curlh, + j_request_body)); + json_decref (j_request_body); + j_request_body = NULL; + + awbh->job = GNUNET_CURL_job_add2 ( + awbh->curl_ctx, + curlh, + awbh->post_ctx.headers, + &handle_reserve_age_withdraw_blinded_finished, + awbh); + FAIL_IF (NULL == awbh->job); + + /* No errors, return */ + return; + +ERROR: + if (NULL != j_denoms) + json_decref (j_denoms); + if (NULL != j_array_candidates) + json_decref (j_array_candidates); + if (NULL != j_request_body) + json_decref (j_request_body); + if (NULL != curlh) + curl_easy_cleanup (curlh); + TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); + return; +#undef FAIL_IF +} + + +/** + * @brief Callback to copy the results from the call to TALER_age_withdraw_blinded + * to the result for the originating call from TALER_age_withdraw. + * + * @param cls struct TALER_AgeWithdrawHandle + * @param awbr The response + */ +static void +copy_results ( + void *cls, + const struct TALER_EXCHANGE_AgeWithdrawBlindedResponse *awbr) +{ + struct TALER_EXCHANGE_AgeWithdrawHandle *awh = cls; + uint8_t k = awbr->details.ok.noreveal_index; + struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details[awh->num_coins]; + struct TALER_BlindedCoinHashP blinded_coin_hs[awh->num_coins]; + struct TALER_EXCHANGE_AgeWithdrawResponse resp = { + .hr = awbr->hr, + .details = { + .ok = { .noreveal_index = awbr->details.ok.noreveal_index, + .h_commitment = awbr->details.ok.h_commitment, + .exchange_pub = awbr->details.ok.exchange_pub, + .num_coins = awh->num_coins, + .coin_details = details, + .blinded_coin_hs = blinded_coin_hs}, + }, + }; + + for (size_t n = 0; n< awh->num_coins; n++) + { + details[n] = awh->coin_data[n].coin_candidates[k].details; + details[n].planchet = awh->coin_data[n].planchet_details[k]; + blinded_coin_hs[n] = awh->coin_data[n].coin_candidates[k].blinded_coin_h; + } + + awh->callback (awh->callback_cls, + &resp); + + awh->callback = NULL; +} + + +/** + * @brief Prepares and executes TALER_EXCHANGE_age_withdraw_blinded. + * If there were CS-denominations involved, started once the all calls + * to /csr-withdraw are done. + */ +static void +call_age_withdraw_blinded ( + struct TALER_EXCHANGE_AgeWithdrawHandle *awh) +{ + struct TALER_EXCHANGE_AgeWithdrawBlindedInput blinded_input[awh->num_coins]; + + /* Prepare the blinded planchets as input */ + for (size_t n = 0; n < awh->num_coins; n++) + { + blinded_input[n].denom_pub = &awh->coin_data[n].denom_pub; + for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) + blinded_input[n].planchet_details[k] = + awh->coin_data[n].planchet_details[k]; + } + + awh->procotol_handle = + TALER_EXCHANGE_age_withdraw_blinded ( + awh->curl_ctx, + awh->keys, + awh->exchange_url, + awh->reserve_priv, + awh->max_age, + awh->num_coins, + blinded_input, + copy_results, + awh); +} + + +/** + * Prepares the request URL for the age-withdraw request + * + * @param awbh The handler + * @param exchange_url The base-URL to the exchange + */ +static +enum GNUNET_GenericReturnValue +prepare_url ( + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh, + const char *exchange_url) +{ + char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32]; + char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &awbh->reserve_pub, + sizeof (awbh->reserve_pub), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "reserves/%s/age-withdraw", + pub_str); + + awbh->request_url = TALER_url_join (exchange_url, + arg_str, + NULL); + if (NULL == awbh->request_url) + { + GNUNET_break (0); + TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * @brief Function called when CSR withdraw retrieval is finished + * + * @param cls the `struct CSRClosure *` + * @param csrr replies from the /csr-withdraw request + */ +static void +csr_withdraw_done ( + void *cls, + const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr) +{ + struct CSRClosure *csr = cls; + struct CoinCandidate *can; + struct TALER_PlanchetDetail *planchet; + struct TALER_EXCHANGE_AgeWithdrawHandle *awh; + + GNUNET_assert (NULL != csr); + awh = csr->age_withdraw_handle; + planchet = csr->planchet; + can = csr->candidate; + + GNUNET_assert (NULL != can); + GNUNET_assert (NULL != planchet); + GNUNET_assert (NULL != awh); + + csr->csr_withdraw_handle = NULL; + + switch (csrr->hr.http_status) + { + case MHD_HTTP_OK: + { + bool success = false; + /* Complete the initialization of the coin with CS denomination */ + can->details.alg_values = csrr->details.ok.alg_values; + GNUNET_assert (can->details.alg_values.cipher + == TALER_DENOMINATION_CS); + TALER_planchet_setup_coin_priv (&can->secret, + &can->details.alg_values, + &can->details.coin_priv); + TALER_planchet_blinding_secret_create (&can->secret, + &can->details.alg_values, + &can->details.blinding_key); + /* This initializes the 2nd half of the + can->planchet_detail.blinded_planchet! */ + do { + if (GNUNET_OK != + TALER_planchet_prepare (&csr->denom_pub->key, + &can->details.alg_values, + &can->details.blinding_key, + &can->details.coin_priv, + &can->details.h_age_commitment, + &can->details.h_coin_pub, + planchet)) + { + GNUNET_break (0); + TALER_EXCHANGE_age_withdraw_cancel (awh); + break; + } + + if (GNUNET_OK != + TALER_coin_ev_hash (&planchet->blinded_planchet, + &planchet->denom_pub_hash, + &can->blinded_coin_h)) + { + GNUNET_break (0); + TALER_EXCHANGE_age_withdraw_cancel (awh); + break; + } + success = true; + } while(0); + + awh->csr.pending--; + + /* No more pending requests to /csr-withdraw, we can now perform the + * actual age-withdraw operation */ + if (0 == awh->csr.pending && success) + call_age_withdraw_blinded (awh); + return; + } + default: + break; + } + + TALER_EXCHANGE_age_withdraw_cancel (awh); +} + + +/** + * @brief Prepare the coins for the call to age-withdraw and calculates + * the total amount with fees. + * + * For denomination with CS as cipher, initiates the preflight to retrieve the + * csr-parameter via /csr-withdraw. + * + * @param awh The handler to the age-withdraw + * @param num_coins The number of coins in @e coin_inputs + * @param coin_inputs The input for the individual coin(-candidates) + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +static +enum GNUNET_GenericReturnValue +prepare_coins ( + struct TALER_EXCHANGE_AgeWithdrawHandle *awh, + size_t num_coins, + const struct TALER_EXCHANGE_AgeWithdrawCoinInput coin_inputs[ + static num_coins]) +{ +#define FAIL_IF(cond) \ + do { \ + if ((cond)) \ + { \ + GNUNET_break (! (cond)); \ + goto ERROR; \ + } \ + } while(0) + + GNUNET_assert (0 < num_coins); + awh->age_mask = coin_inputs[0].denom_pub->key.age_mask; + + awh->coin_data = GNUNET_new_array (awh->num_coins, + struct CoinData); + + for (size_t i = 0; i < num_coins; i++) + { + struct CoinData *cd = &awh->coin_data[i]; + const struct TALER_EXCHANGE_AgeWithdrawCoinInput *input = &coin_inputs[i]; + cd->denom_pub = *input->denom_pub; + + /* The mask must be the same for all coins */ + FAIL_IF (awh->age_mask.bits != input->denom_pub->key.age_mask.bits); + + TALER_denom_pub_deep_copy (&cd->denom_pub.key, + &input->denom_pub->key); + + for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) + { + struct CoinCandidate *can = &cd->coin_candidates[k]; + struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; + + can->secret = input->secrets[k]; + /* Derive the age restriction from the given secret and + * the maximum age */ + TALER_age_restriction_from_secret ( + &can->secret, + &input->denom_pub->key.age_mask, + awh->max_age, + &can->details.age_commitment_proof); + + TALER_age_commitment_hash (&can->details.age_commitment_proof.commitment, + &can->details.h_age_commitment); + + switch (input->denom_pub->key.cipher) + { + case TALER_DENOMINATION_RSA: + { + can->details.alg_values.cipher = TALER_DENOMINATION_RSA; + TALER_planchet_setup_coin_priv (&can->secret, + &can->details.alg_values, + &can->details.coin_priv); + TALER_planchet_blinding_secret_create (&can->secret, + &can->details.alg_values, + &can->details.blinding_key); + FAIL_IF (GNUNET_OK != + TALER_planchet_prepare (&cd->denom_pub.key, + &can->details.alg_values, + &can->details.blinding_key, + &can->details.coin_priv, + &can->details.h_age_commitment, + &can->details.h_coin_pub, + planchet)); + FAIL_IF (GNUNET_OK != + TALER_coin_ev_hash (&planchet->blinded_planchet, + &planchet->denom_pub_hash, + &can->blinded_coin_h)); + break; + } + case TALER_DENOMINATION_CS: + { + can->details.alg_values.cipher = TALER_DENOMINATION_CS; + + struct CSRClosure *cls = &cd->csr_cls[k]; + /** + * Save the handler and the denomination for the callback + * after the call to csr-withdraw */ + cls->age_withdraw_handle = awh; + cls->candidate = can; + cls->planchet = planchet; + cls->denom_pub = &cd->denom_pub; + + TALER_cs_withdraw_nonce_derive ( + &can->secret, + &planchet->blinded_planchet.details.cs_blinded_planchet.nonce); + + /* Note that we only initialize the first half + of the blinded_planchet here; the other part + will be done after the /csr-withdraw request! */ + planchet->blinded_planchet.cipher = TALER_DENOMINATION_CS; + cls->csr_withdraw_handle = + TALER_EXCHANGE_csr_withdraw ( + awh->curl_ctx, + awh->exchange_url, + &cd->denom_pub, + &planchet->blinded_planchet.details.cs_blinded_planchet.nonce, + &csr_withdraw_done, + cls); + FAIL_IF (NULL == cls->csr_withdraw_handle); + + awh->csr.pending++; + break; + } + default: + FAIL_IF (1); + } + } + } + return GNUNET_OK; + +ERROR: + TALER_EXCHANGE_age_withdraw_cancel (awh); + return GNUNET_SYSERR; +#undef FAIL_IF +}; + +struct TALER_EXCHANGE_AgeWithdrawHandle * +TALER_EXCHANGE_age_withdraw ( + struct GNUNET_CURL_Context *curl_ctx, + struct TALER_EXCHANGE_Keys *keys, + const char *exchange_url, + const struct TALER_ReservePrivateKeyP *reserve_priv, + size_t num_coins, + const struct TALER_EXCHANGE_AgeWithdrawCoinInput coin_inputs[const static + num_coins], + uint8_t max_age, + TALER_EXCHANGE_AgeWithdrawCallback res_cb, + void *res_cb_cls) +{ + struct TALER_EXCHANGE_AgeWithdrawHandle *awh; + + awh = GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawHandle); + awh->exchange_url = exchange_url; + awh->keys = TALER_EXCHANGE_keys_incref (keys); + awh->curl_ctx = curl_ctx; + awh->reserve_priv = reserve_priv; + awh->callback = res_cb; + awh->callback_cls = res_cb_cls; + awh->num_coins = num_coins; + awh->max_age = max_age; + + + if (GNUNET_OK != prepare_coins (awh, + num_coins, + coin_inputs)) + return NULL; + + /* If there were no CS denominations, we can now perform the actual + * age-withdraw protocol. Otherwise, there are calls to /csr-withdraw + * in flight and once they finish, the age-withdraw-protocol will be + * called from within the csr_withdraw_done-function. + */ + if (0 == awh->csr.pending) + call_age_withdraw_blinded (awh); + + return awh; +} + + +void +TALER_EXCHANGE_age_withdraw_cancel ( + struct TALER_EXCHANGE_AgeWithdrawHandle *awh) +{ + /* Cleanup coin data */ + for (unsigned int i = 0; inum_coins; i++) + { + struct CoinData *cd = &awh->coin_data[i]; + + for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) + { + struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; + struct CSRClosure *cls = &cd->csr_cls[k]; + + if (NULL != cls->csr_withdraw_handle) + { + TALER_EXCHANGE_csr_withdraw_cancel (cls->csr_withdraw_handle); + cls->csr_withdraw_handle = NULL; + } + TALER_blinded_planchet_free (&planchet->blinded_planchet); + } + TALER_denom_pub_free (&cd->denom_pub.key); + } + GNUNET_free (awh->coin_data); + TALER_EXCHANGE_keys_decref (awh->keys); + TALER_EXCHANGE_age_withdraw_blinded_cancel (awh->procotol_handle); + awh->procotol_handle = NULL; + GNUNET_free (awh); +} + + +struct TALER_EXCHANGE_AgeWithdrawBlindedHandle * +TALER_EXCHANGE_age_withdraw_blinded ( + struct GNUNET_CURL_Context *curl_ctx, + struct TALER_EXCHANGE_Keys *keys, + const char *exchange_url, + const struct TALER_ReservePrivateKeyP *reserve_priv, + uint8_t max_age, + unsigned int num_input, + const struct TALER_EXCHANGE_AgeWithdrawBlindedInput blinded_input[static + num_input], + TALER_EXCHANGE_AgeWithdrawBlindedCallback res_cb, + void *res_cb_cls) +{ + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh = + GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawBlindedHandle); + + awbh->num_input = num_input; + awbh->blinded_input = blinded_input; + awbh->keys = TALER_EXCHANGE_keys_incref (keys); + awbh->curl_ctx = curl_ctx; + awbh->reserve_priv = reserve_priv; + awbh->callback = res_cb; + awbh->callback_cls = res_cb_cls; + awbh->max_age = max_age; + + GNUNET_CRYPTO_eddsa_key_get_public (&awbh->reserve_priv->eddsa_priv, + &awbh->reserve_pub.eddsa_pub); + + if (GNUNET_OK != prepare_url (awbh, + exchange_url)) + return NULL; + + perform_protocol (awbh); + return awbh; +} + + +void +TALER_EXCHANGE_age_withdraw_blinded_cancel ( + struct TALER_EXCHANGE_AgeWithdrawBlindedHandle *awbh) +{ + if (NULL == awbh) + return; + + if (NULL != awbh->job) + { + GNUNET_CURL_job_cancel (awbh->job); + awbh->job = NULL; + } + GNUNET_free (awbh->request_url); + TALER_EXCHANGE_keys_decref (awbh->keys); + TALER_curl_easy_post_finished (&awbh->post_ctx); + GNUNET_free (awbh); +} + + +/* exchange_api_age_withdraw.c */ diff --git a/src/lib/exchange_api_age_withdraw_reveal.c b/src/lib/exchange_api_age_withdraw_reveal.c new file mode 100644 index 000000000..a448d109d --- /dev/null +++ b/src/lib/exchange_api_age_withdraw_reveal.c @@ -0,0 +1,471 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_age_withdraw_reveal.c + * @brief Implementation of /age-withdraw/$ACH/reveal requests + * @author Özgür Kesim + */ + +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_curl_lib.h" +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_common.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + +/** + * Handler for a running age-withdraw-reveal request + */ +struct TALER_EXCHANGE_AgeWithdrawRevealHandle +{ + + /* The index not to be disclosed */ + uint8_t noreveal_index; + + /* The age-withdraw commitment */ + struct TALER_AgeWithdrawCommitmentHashP h_commitment; + + /* The reserve's public key */ + const struct TALER_ReservePublicKeyP *reserve_pub; + + /* Number of coins */ + size_t num_coins; + + /* The @e num_coins * kappa coin secrets from the age-withdraw commitment */ + const struct TALER_EXCHANGE_AgeWithdrawCoinInput *coins_input; + + /* The url for the reveal request */ + const char *request_url; + + /** + * CURL handle for the request job. + */ + struct GNUNET_CURL_Job *job; + + /** + * Post Context + */ + struct TALER_CURL_PostContext post_ctx; + + /* Callback */ + TALER_EXCHANGE_AgeWithdrawRevealCallback callback; + + /* Reveal */ + void *callback_cls; +}; + + +/** + * We got a 200 OK response for the /age-withdraw/$ACH/reveal operation. + * Extract the signed blindedcoins and return it to the caller. + * + * @param awrh operation handle + * @param j_response reply from the exchange + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static enum GNUNET_GenericReturnValue +age_withdraw_reveal_ok ( + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh, + const json_t *j_response) +{ + struct TALER_EXCHANGE_AgeWithdrawRevealResponse response = { + .hr.reply = j_response, + .hr.http_status = MHD_HTTP_OK, + }; + const json_t *j_sigs; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("ev_sigs", + &j_sigs), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != GNUNET_JSON_parse (j_response, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (awrh->num_coins != json_array_size (j_sigs)) + { + /* Number of coins generated does not match our expectation */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + { + struct TALER_BlindedDenominationSignature denom_sigs[awrh->num_coins]; + json_t *j_sig; + size_t n; + + /* Reconstruct the coins and unblind the signatures */ + json_array_foreach (j_sigs, n, j_sig) + { + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_blinded_denom_sig (NULL, + &denom_sigs[n]), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != GNUNET_JSON_parse (j_sig, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + } + + response.details.ok.num_sigs = awrh->num_coins; + response.details.ok.blinded_denom_sigs = denom_sigs; + awrh->callback (awrh->callback_cls, + &response); + /* Make sure the callback isn't called again */ + awrh->callback = NULL; + } + + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /age-withdraw/$ACH/reveal request. + * + * @param cls the `struct TALER_EXCHANGE_AgeWithdrawRevealHandle` + * @param response_code The HTTP response code + * @param response response data + */ +static void +handle_age_withdraw_reveal_finished ( + void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh = cls; + const json_t *j_response = response; + struct TALER_EXCHANGE_AgeWithdrawRevealResponse awr = { + .hr.reply = j_response, + .hr.http_status = (unsigned int) response_code + }; + + awrh->job = NULL; + /* FIXME[oec]: Only handle response-codes that are in the spec */ + switch (response_code) + { + case 0: + awr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + enum GNUNET_GenericReturnValue ret; + + ret = age_withdraw_reveal_ok (awrh, + j_response); + if (GNUNET_OK != ret) + { + GNUNET_break_op (0); + awr.hr.http_status = 0; + awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + GNUNET_assert (NULL == awrh->callback); + TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); + return; + } + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* only validate reply is well-formed */ + { + uint64_t ptu; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint64 ("legitimization_uuid", + &ptu), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j_response, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + awr.hr.http_status = 0; + awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + } + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_FORBIDDEN: + GNUNET_break_op (0); + /** + * This should never happen, as we don't sent any signatures + * to the exchange to verify. We should simply pass the JSON reply + * to the application + **/ + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, the exchange basically just says + that it doesn't know this age-withdraw commitment. */ + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_CONFLICT: + /* An age commitment for one of the coins did not fulfill + * the required maximum age requirement of the corresponding + * reserve. + * Error code: TALER_EC_EXCHANGE_GENERIC_COIN_AGE_REQUIREMENT_FAILURE. + */ + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_GONE: + /* could happen if denomination was revoked */ + /* Note: one might want to check /keys for revocation + signature here, alas tricky in case our /keys + is outdated => left to clients */ + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + awr.hr.ec = TALER_JSON_get_error_code (j_response); + awr.hr.hint = TALER_JSON_get_error_hint (j_response); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange age-withdraw\n", + (unsigned int) response_code, + (int) awr.hr.ec); + break; + } + awrh->callback (awrh->callback_cls, + &awr); + TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); +} + + +/** + * Prepares the request URL for the age-withdraw-reveal request + * + * @param exchange_url The base-URL to the exchange + * @param[in,out] awrh The handler + * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + */ +static +enum GNUNET_GenericReturnValue +prepare_url ( + const char *exchange_url, + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh) +{ + char arg_str[sizeof (struct TALER_AgeWithdrawCommitmentHashP) * 2 + 32]; + char pub_str[sizeof (struct TALER_AgeWithdrawCommitmentHashP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string (&awrh->h_commitment, + sizeof (awrh->h_commitment), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "age-withdraw/%s/reveal", + pub_str); + + awrh->request_url = TALER_url_join (exchange_url, + arg_str, + NULL); + if (NULL == awrh->request_url) + { + GNUNET_break (0); + TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Call /age-withdraw/$ACH/reveal + * + * @param curl_ctx The context for CURL + * @param awrh The handler + */ +static +void +perform_protocol ( + struct GNUNET_CURL_Context *curl_ctx, + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh) +{ + CURL *curlh = NULL; + json_t *j_request_body = NULL; + json_t *j_array_of_secrets = NULL; + json_t *j_secrets = NULL; + json_t *j_sec = NULL; + +#define FAIL_IF(cond) \ + do { \ + if ((cond)) \ + { \ + GNUNET_break (! (cond)); \ + goto ERROR; \ + } \ + } while(0) + + j_array_of_secrets = json_array (); + FAIL_IF (NULL == j_array_of_secrets); + + for (size_t n = 0; n < awrh->num_coins; n++) + { + const struct TALER_PlanchetMasterSecretP *secrets = + awrh->coins_input[n].secrets; + + j_secrets = json_array (); + FAIL_IF (NULL == j_secrets); + + for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) + { + const struct TALER_PlanchetMasterSecretP *secret = &secrets[k]; + if (awrh->noreveal_index == k) + continue; + + j_sec = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto (NULL, secret)); + + FAIL_IF (NULL == j_sec); + FAIL_IF (0 < json_array_append_new (j_secrets, + j_sec)); + } + + FAIL_IF (0 < json_array_append_new (j_array_of_secrets, + j_secrets)); + } + j_request_body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + awrh->reserve_pub), + GNUNET_JSON_pack_array_steal ("disclosed_coin_secrets", + j_array_of_secrets)); + FAIL_IF (NULL == j_request_body); + + curlh = TALER_EXCHANGE_curl_easy_get_ (awrh->request_url); + FAIL_IF (NULL == curlh); + FAIL_IF (GNUNET_OK != + TALER_curl_easy_post (&awrh->post_ctx, + curlh, + j_request_body)); + json_decref (j_request_body); + j_request_body = NULL; + + awrh->job = GNUNET_CURL_job_add2 (curl_ctx, + curlh, + awrh->post_ctx.headers, + &handle_age_withdraw_reveal_finished, + awrh); + FAIL_IF (NULL == awrh->job); + + /* No error, return */ + return; + +ERROR: + if (NULL != j_sec) + json_decref (j_sec); + if (NULL != j_secrets) + json_decref (j_secrets); + if (NULL != j_array_of_secrets) + json_decref (j_array_of_secrets); + if (NULL != j_request_body) + json_decref (j_request_body); + if (NULL != curlh) + curl_easy_cleanup (curlh); + TALER_EXCHANGE_age_withdraw_reveal_cancel (awrh); + return; +#undef FAIL_IF +} + + +struct TALER_EXCHANGE_AgeWithdrawRevealHandle * +TALER_EXCHANGE_age_withdraw_reveal ( + struct GNUNET_CURL_Context *curl_ctx, + const char *exchange_url, + size_t num_coins, + const struct TALER_EXCHANGE_AgeWithdrawCoinInput coins_input[static + num_coins], + uint8_t noreveal_index, + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_ReservePublicKeyP *reserve_pub, + TALER_EXCHANGE_AgeWithdrawRevealCallback reveal_cb, + void *reveal_cb_cls) +{ + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh = + GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawRevealHandle); + awrh->noreveal_index = noreveal_index; + awrh->h_commitment = *h_commitment; + awrh->num_coins = num_coins; + awrh->coins_input = coins_input; + awrh->callback = reveal_cb; + awrh->callback_cls = reveal_cb_cls; + awrh->reserve_pub = reserve_pub; + + if (GNUNET_OK != + prepare_url (exchange_url, + awrh)) + return NULL; + + perform_protocol (curl_ctx, awrh); + + return awrh; +} + + +void +TALER_EXCHANGE_age_withdraw_reveal_cancel ( + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh) +{ + if (NULL != awrh->job) + { + GNUNET_CURL_job_cancel (awrh->job); + awrh->job = NULL; + } + TALER_curl_easy_post_finished (&awrh->post_ctx); + /* FIXME[oec]: anything else left to cleanup!? */ + GNUNET_free (awrh); +} + + +/* exchange_api_age_withdraw_reveal.c */ diff --git a/src/lib/exchange_api_auditor_add_denomination.c b/src/lib/exchange_api_auditor_add_denomination.c new file mode 100644 index 000000000..89de0d7f1 --- /dev/null +++ b/src/lib/exchange_api_auditor_add_denomination.c @@ -0,0 +1,238 @@ +/* + This file is part of TALER + Copyright (C) 2015-2021 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_auditor_add_denomination.c + * @brief functions for the auditor to add its signature for denomination at the exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "auditor_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_AuditorAddDenominationHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_AuditorAddDenominationCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /auditor/$AUDITOR_PUB/$H_DENOM_PUB request. + * + * @param cls the `struct TALER_EXCHANGE_AuditorAddDenominationHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_auditor_add_denomination_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah = cls; + const json_t *json = response; + struct TALER_EXCHANGE_AuditorAddDenominationResponse adr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ah->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_GONE: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_PRECONDITION_FAILED: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + if (NULL != json) + { + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange auditor-add-denomination at URL `%s'\n", + (unsigned int) response_code, + (int) adr.hr.ec, + ah->url); + } + else + { + adr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + adr.hr.hint = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code %u (no JSON returned) at URL `%s'\n", + (unsigned int) response_code, + ah->url); + } + break; + } + if (NULL != ah->cb) + { + ah->cb (ah->cb_cls, + &adr); + ah->cb = NULL; + } + TALER_EXCHANGE_add_auditor_denomination_cancel (ah); +} + + +struct TALER_EXCHANGE_AuditorAddDenominationHandle * +TALER_EXCHANGE_add_auditor_denomination ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_DenominationHashP *h_denom_pub, + const struct TALER_AuditorPublicKeyP *auditor_pub, + const struct TALER_AuditorSignatureP *auditor_sig, + TALER_EXCHANGE_AuditorAddDenominationCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah; + CURL *eh; + json_t *body; + + ah = GNUNET_new (struct TALER_EXCHANGE_AuditorAddDenominationHandle); + ah->cb = cb; + ah->cb_cls = cb_cls; + ah->ctx = ctx; + { + char apub_str[sizeof (*auditor_pub) * 2]; + char denom_str[sizeof (*h_denom_pub) * 2]; + char arg_str[sizeof (apub_str) + sizeof (denom_str) + 32]; + char *end; + + end = GNUNET_STRINGS_data_to_string (auditor_pub, + sizeof (*auditor_pub), + apub_str, + sizeof (apub_str)); + *end = '\0'; + end = GNUNET_STRINGS_data_to_string (h_denom_pub, + sizeof (*h_denom_pub), + denom_str, + sizeof (denom_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "auditors/%s/%s", + apub_str, + denom_str); + ah->url = TALER_url_join (url, + arg_str, + NULL); + } + if (NULL == ah->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ah); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("auditor_sig", + auditor_sig)); + eh = TALER_AUDITOR_curl_easy_get_ (ah->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ah->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (ah->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ah->url); + ah->job = GNUNET_CURL_job_add2 (ctx, + eh, + ah->post_ctx.headers, + &handle_auditor_add_denomination_finished, + ah); + if (NULL == ah->job) + { + TALER_EXCHANGE_add_auditor_denomination_cancel (ah); + return NULL; + } + return ah; +} + + +void +TALER_EXCHANGE_add_auditor_denomination_cancel ( + struct TALER_EXCHANGE_AuditorAddDenominationHandle *ah) +{ + if (NULL != ah->job) + { + GNUNET_CURL_job_cancel (ah->job); + ah->job = NULL; + } + TALER_curl_easy_post_finished (&ah->post_ctx); + GNUNET_free (ah->url); + GNUNET_free (ah); +} diff --git a/src/lib/exchange_api_csr_melt.c b/src/lib/exchange_api_csr_melt.c new file mode 100644 index 000000000..f59995af3 --- /dev/null +++ b/src/lib/exchange_api_csr_melt.c @@ -0,0 +1,317 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_csr_melt.c + * @brief Implementation of /csr-melt requests (get R in exchange used for Clause Schnorr refresh) + * @author Lucien Heuzeveldt + * @author Gian Demarmels + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A Clause Schnorr R Handle + */ +struct TALER_EXCHANGE_CsRMeltHandle +{ + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_CsRMeltCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation. + * Extract the coin's signature and return it to the caller. The signature we + * get from the exchange is for the blinded value. Thus, we first must + * unblind it and then should verify its validity against our coin's hash. + * + * If everything checks out, we return the unblinded signature + * to the application via the callback. + * + * @param csrh operation handle + * @param arr reply from the exchange + * @param hr http response details + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static enum GNUNET_GenericReturnValue +csr_ok (struct TALER_EXCHANGE_CsRMeltHandle *csrh, + const json_t *arr, + struct TALER_EXCHANGE_HttpResponse *hr) +{ + unsigned int alen = json_array_size (arr); + struct TALER_ExchangeWithdrawValues alg_values[GNUNET_NZL (alen)]; + struct TALER_EXCHANGE_CsRMeltResponse csrr = { + .hr = *hr, + .details.ok.alg_values_len = alen, + .details.ok.alg_values = alg_values + }; + + for (unsigned int i = 0; icb (csrh->cb_cls, + &csrr); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the HTTP /csr request. + * + * @param cls the `struct TALER_EXCHANGE_CsRMeltHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_csr_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_CsRMeltHandle *csrh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_HttpResponse hr = { + .reply = j, + .http_status = (unsigned int) response_code + }; + struct TALER_EXCHANGE_CsRMeltResponse csrr = { + .hr = hr + }; + + csrh->job = NULL; + switch (response_code) + { + case 0: + csrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + json_t *arr; + + arr = json_object_get (j, + "ewvs"); + if ( (NULL == arr) || + (0 == json_array_size (arr)) || + (GNUNET_OK != + csr_ok (csrh, + arr, + &hr)) ) + { + GNUNET_break_op (0); + csrr.hr.http_status = 0; + csrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + } + TALER_EXCHANGE_csr_melt_cancel (csrh); + return; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + csrr.hr.ec = TALER_JSON_get_error_code (j); + csrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, the exchange basically just says + that it doesn't know the /csr endpoint or denomination. + Can happen if the exchange doesn't support Clause Schnorr. + We should simply pass the JSON reply to the application. */ + csrr.hr.ec = TALER_JSON_get_error_code (j); + csrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_GONE: + /* could happen if denomination was revoked */ + /* Note: one might want to check /keys for revocation + signature here, alas tricky in case our /keys + is outdated => left to clients */ + csrr.hr.ec = TALER_JSON_get_error_code (j); + csrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + csrr.hr.ec = TALER_JSON_get_error_code (j); + csrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + csrr.hr.ec = TALER_JSON_get_error_code (j); + csrr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for CS R request\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + csrh->cb (csrh->cb_cls, + &csrr); + csrh->cb = NULL; + TALER_EXCHANGE_csr_melt_cancel (csrh); +} + + +struct TALER_EXCHANGE_CsRMeltHandle * +TALER_EXCHANGE_csr_melt ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_RefreshMasterSecretP *rms, + unsigned int nks_len, + struct TALER_EXCHANGE_NonceKey nks[static nks_len], + TALER_EXCHANGE_CsRMeltCallback res_cb, + void *res_cb_cls) +{ + struct TALER_EXCHANGE_CsRMeltHandle *csrh; + json_t *csr_arr; + + if (0 == nks_len) + { + GNUNET_break (0); + return NULL; + } + for (unsigned int i = 0; ikey.cipher) + { + GNUNET_break (0); + return NULL; + } + csrh = GNUNET_new (struct TALER_EXCHANGE_CsRMeltHandle); + csrh->cb = res_cb; + csrh->cb_cls = res_cb_cls; + csr_arr = json_array (); + GNUNET_assert (NULL != csr_arr); + for (unsigned int i = 0; icnc_num), + GNUNET_JSON_pack_data_auto ("denom_pub_hash", + &nk->pk->h_key)); + GNUNET_assert (NULL != csr_obj); + GNUNET_assert (0 == + json_array_append_new (csr_arr, + csr_obj)); + } + csrh->url = TALER_url_join (url, + "csr-melt", + NULL); + if (NULL == csrh->url) + { + json_decref (csr_arr); + GNUNET_free (csrh); + return NULL; + } + { + CURL *eh; + json_t *req; + + req = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("rms", + rms), + GNUNET_JSON_pack_array_steal ("nks", + csr_arr)); + eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&csrh->post_ctx, + eh, + req)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (req); + GNUNET_free (csrh->url); + GNUNET_free (csrh); + return NULL; + } + json_decref (req); + csrh->job = GNUNET_CURL_job_add2 (ctx, + eh, + csrh->post_ctx.headers, + &handle_csr_finished, + csrh); + } + return csrh; +} + + +void +TALER_EXCHANGE_csr_melt_cancel (struct TALER_EXCHANGE_CsRMeltHandle *csrh) +{ + if (NULL != csrh->job) + { + GNUNET_CURL_job_cancel (csrh->job); + csrh->job = NULL; + } + GNUNET_free (csrh->url); + TALER_curl_easy_post_finished (&csrh->post_ctx); + GNUNET_free (csrh); +} diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c new file mode 100644 index 000000000..373dd89a7 --- /dev/null +++ b/src/lib/exchange_api_kyc_check.c @@ -0,0 +1,329 @@ +/* + This file is part of TALER + Copyright (C) 2021-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_kyc_check.c + * @brief Implementation of the /kyc-check request + * @author Christian Grothoff + */ +#include "platform.h" +#include /* just for HTTP check codes */ +#include +#include +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A ``/kyc-check`` handle + */ +struct TALER_EXCHANGE_KycCheckHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Keys of the exchange. + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_KycStatusCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Hash of the payto:// URL that is being KYC'ed. + */ + struct TALER_PaytoHashP h_payto; + +}; + + +/** + * Function called when we're done processing the + * HTTP /kyc-check request. + * + * @param cls the `struct TALER_EXCHANGE_KycCheckHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_kyc_check_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_KycCheckHandle *kch = cls; + const json_t *j = response; + struct TALER_EXCHANGE_KycStatus ks = { + .http_status = (unsigned int) response_code + }; + + kch->job = NULL; + switch (response_code) + { + case 0: + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + const json_t *kyc_details; + uint32_t status; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &ks.details.ok.exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + &ks.details.ok.exchange_pub), + GNUNET_JSON_spec_timestamp ("now", + &ks.details.ok.timestamp), + GNUNET_JSON_spec_object_const ("kyc_details", + &kyc_details), + GNUNET_JSON_spec_uint32 ("aml_status", + &status), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + ks.details.ok.kyc_details = kyc_details; + ks.details.ok.aml_status + = (enum TALER_AmlDecisionState) status; + if (GNUNET_OK != + TALER_EXCHANGE_test_signing_key (kch->keys, + &ks.details.ok.exchange_pub)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_JSON_parse_free (spec); + break; + } + + if (GNUNET_OK != + TALER_exchange_online_account_setup_success_verify ( + &kch->h_payto, + ks.details.ok.kyc_details, + ks.details.ok.timestamp, + &ks.details.ok.exchange_pub, + &ks.details.ok.exchange_sig)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + GNUNET_JSON_parse_free (spec); + break; + } + kch->cb (kch->cb_cls, + &ks); + GNUNET_JSON_parse_free (spec); + TALER_EXCHANGE_kyc_check_cancel (kch); + return; + } + case MHD_HTTP_ACCEPTED: + { + uint32_t status; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("kyc_url", + &ks.details.accepted.kyc_url), + GNUNET_JSON_spec_uint32 ("aml_status", + &status), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + ks.details.accepted.aml_status + = (enum TALER_AmlDecisionState) status; + kch->cb (kch->cb_cls, + &ks); + GNUNET_JSON_parse_free (spec); + TALER_EXCHANGE_kyc_check_cancel (kch); + return; + } + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + ks.ec = TALER_JSON_get_error_code (j); + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + ks.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_NOT_FOUND: + ks.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + { + uint32_t status; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint32 ("aml_status", + &status), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + ks.details.unavailable_for_legal_reasons.aml_status + = (enum TALER_AmlDecisionState) status; + kch->cb (kch->cb_cls, + &ks); + GNUNET_JSON_parse_free (spec); + TALER_EXCHANGE_kyc_check_cancel (kch); + return; + } + case MHD_HTTP_INTERNAL_SERVER_ERROR: + ks.ec = TALER_JSON_get_error_code (j); + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + ks.ec = TALER_JSON_get_error_code (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange kyc_check\n", + (unsigned int) response_code, + (int) ks.ec); + break; + } + kch->cb (kch->cb_cls, + &ks); + TALER_EXCHANGE_kyc_check_cancel (kch); +} + + +struct TALER_EXCHANGE_KycCheckHandle * +TALER_EXCHANGE_kyc_check ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *keys, + uint64_t requirement_row, + const struct TALER_PaytoHashP *h_payto, + enum TALER_KYCLOGIC_KycUserType ut, + struct GNUNET_TIME_Relative timeout, + TALER_EXCHANGE_KycStatusCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_KycCheckHandle *kch; + CURL *eh; + char *arg_str; + + { + char payto_str[sizeof (*h_payto) * 2]; + char *end; + unsigned long long timeout_ms; + + end = GNUNET_STRINGS_data_to_string ( + h_payto, + sizeof (*h_payto), + payto_str, + sizeof (payto_str) - 1); + *end = '\0'; + timeout_ms = timeout.rel_value_us + / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; + GNUNET_asprintf (&arg_str, + "kyc-check/%llu/%s/%s?timeout_ms=%llu", + (unsigned long long) requirement_row, + payto_str, + TALER_KYCLOGIC_kyc_user_type2s (ut), + timeout_ms); + } + kch = GNUNET_new (struct TALER_EXCHANGE_KycCheckHandle); + kch->h_payto = *h_payto; + kch->cb = cb; + kch->cb_cls = cb_cls; + kch->url = TALER_url_join (url, + arg_str, + NULL); + GNUNET_free (arg_str); + if (NULL == kch->url) + { + GNUNET_free (kch); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (kch->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (kch->url); + GNUNET_free (kch); + return NULL; + } + kch->keys = TALER_EXCHANGE_keys_incref (keys); + kch->job = GNUNET_CURL_job_add_with_ct_json (ctx, + eh, + &handle_kyc_check_finished, + kch); + return kch; +} + + +void +TALER_EXCHANGE_kyc_check_cancel (struct TALER_EXCHANGE_KycCheckHandle *kch) +{ + if (NULL != kch->job) + { + GNUNET_CURL_job_cancel (kch->job); + kch->job = NULL; + } + TALER_EXCHANGE_keys_decref (kch->keys); + GNUNET_free (kch->url); + GNUNET_free (kch); +} + + +/* end of exchange_api_kyc_check.c */ diff --git a/src/lib/exchange_api_kyc_proof.c b/src/lib/exchange_api_kyc_proof.c new file mode 100644 index 000000000..e7cc9c4cf --- /dev/null +++ b/src/lib/exchange_api_kyc_proof.c @@ -0,0 +1,217 @@ +/* + This file is part of TALER + Copyright (C) 2021, 2022 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_kyc_proof.c + * @brief Implementation of the /kyc-proof request + * @author Christian Grothoff + */ +#include "platform.h" +#include /* just for HTTP proof codes */ +#include +#include +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A ``/kyc-proof`` handle + */ +struct TALER_EXCHANGE_KycProofHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle to our CURL request. + */ + CURL *eh; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_KycProofCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + +}; + + +/** + * Function called when we're done processing the + * HTTP /kyc-proof request. + * + * @param cls the `struct TALER_EXCHANGE_KycProofHandle` + * @param response_code HTTP response code, 0 on error + * @param body response body + * @param body_size number of bytes in @a body + */ +static void +handle_kyc_proof_finished (void *cls, + long response_code, + const void *body, + size_t body_size) +{ + struct TALER_EXCHANGE_KycProofHandle *kph = cls; + struct TALER_EXCHANGE_KycProofResponse kpr = { + .http_status = (unsigned int) response_code + }; + + (void) body; + (void) body_size; + kph->job = NULL; + switch (response_code) + { + case 0: + break; + case MHD_HTTP_SEE_OTHER: + { + char *redirect_url; + + GNUNET_assert (CURLE_OK == + curl_easy_getinfo (kph->eh, + CURLINFO_REDIRECT_URL, + &redirect_url)); + kpr.details.found.redirect_url = redirect_url; + break; + } + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + break; + case MHD_HTTP_FORBIDDEN: + break; + case MHD_HTTP_NOT_FOUND: + break; + case MHD_HTTP_BAD_GATEWAY: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + case MHD_HTTP_GATEWAY_TIMEOUT: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u for exchange kyc_proof\n", + (unsigned int) response_code); + break; + } + kph->cb (kph->cb_cls, + &kpr); + TALER_EXCHANGE_kyc_proof_cancel (kph); +} + + +struct TALER_EXCHANGE_KycProofHandle * +TALER_EXCHANGE_kyc_proof ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_PaytoHashP *h_payto, + const char *logic, + const char *args, + TALER_EXCHANGE_KycProofCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_KycProofHandle *kph; + char *arg_str; + + if (NULL == args) + args = ""; + else + GNUNET_assert (args[0] == '&'); + { + char hstr[sizeof (struct TALER_PaytoHashP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_payto, + sizeof (*h_payto), + hstr, + sizeof (hstr)); + *end = '\0'; + GNUNET_asprintf (&arg_str, + "kyc-proof/%s?state=%s%s", + logic, + hstr, + args); + } + kph = GNUNET_new (struct TALER_EXCHANGE_KycProofHandle); + kph->cb = cb; + kph->cb_cls = cb_cls; + kph->url = TALER_url_join (url, + arg_str, + NULL); + GNUNET_free (arg_str); + if (NULL == kph->url) + { + GNUNET_free (kph); + return NULL; + } + kph->eh = TALER_EXCHANGE_curl_easy_get_ (kph->url); + if (NULL == kph->eh) + { + GNUNET_break (0); + GNUNET_free (kph->url); + GNUNET_free (kph); + return NULL; + } + /* disable location following, we want to learn the + result of a 303 redirect! */ + GNUNET_assert (CURLE_OK == + curl_easy_setopt (kph->eh, + CURLOPT_FOLLOWLOCATION, + 0L)); + kph->job = GNUNET_CURL_job_add_raw (ctx, + kph->eh, + NULL, + &handle_kyc_proof_finished, + kph); + return kph; +} + + +void +TALER_EXCHANGE_kyc_proof_cancel (struct TALER_EXCHANGE_KycProofHandle *kph) +{ + if (NULL != kph->job) + { + GNUNET_CURL_job_cancel (kph->job); + kph->job = NULL; + } + GNUNET_free (kph->url); + GNUNET_free (kph); +} + + +/* end of exchange_api_kyc_proof.c */ diff --git a/src/lib/exchange_api_kyc_wallet.c b/src/lib/exchange_api_kyc_wallet.c new file mode 100644 index 000000000..7197694ae --- /dev/null +++ b/src/lib/exchange_api_kyc_wallet.c @@ -0,0 +1,230 @@ +/* + This file is part of TALER + Copyright (C) 2021 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_kyc_wallet.c + * @brief Implementation of the /kyc-wallet request + * @author Christian Grothoff + */ +#include "platform.h" +#include /* just for HTTP wallet codes */ +#include +#include +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A ``/kyc-wallet`` handle + */ +struct TALER_EXCHANGE_KycWalletHandle +{ + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_KycWalletCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + +}; + + +/** + * Function called when we're done processing the + * HTTP /kyc-wallet request. + * + * @param cls the `struct TALER_EXCHANGE_KycWalletHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_kyc_wallet_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_KycWalletHandle *kwh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_WalletKycResponse ks = { + .http_status = (unsigned int) response_code + }; + + kwh->job = NULL; + switch (response_code) + { + case 0: + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + ks.ec = TALER_JSON_get_error_code (j); + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + ks.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_NOT_FOUND: + ks.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ( + "h_payto", + &ks.details.unavailable_for_legal_reasons.h_payto), + GNUNET_JSON_spec_uint64 ( + "requirement_row", + &ks.details.unavailable_for_legal_reasons.requirement_row), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + break; + } + case MHD_HTTP_INTERNAL_SERVER_ERROR: + ks.ec = TALER_JSON_get_error_code (j); + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + ks.ec = TALER_JSON_get_error_code (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange /kyc-wallet\n", + (unsigned int) response_code, + (int) ks.ec); + break; + } + kwh->cb (kwh->cb_cls, + &ks); + TALER_EXCHANGE_kyc_wallet_cancel (kwh); +} + + +struct TALER_EXCHANGE_KycWalletHandle * +TALER_EXCHANGE_kyc_wallet ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_Amount *balance, + TALER_EXCHANGE_KycWalletCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_KycWalletHandle *kwh; + CURL *eh; + json_t *req; + struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_ReserveSignatureP reserve_sig; + + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, + &reserve_pub.eddsa_pub); + TALER_wallet_account_setup_sign (reserve_priv, + balance, + &reserve_sig); + req = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("balance", + balance), + GNUNET_JSON_pack_data_auto ("reserve_pub", + &reserve_pub), + GNUNET_JSON_pack_data_auto ("reserve_sig", + &reserve_sig)); + GNUNET_assert (NULL != req); + kwh = GNUNET_new (struct TALER_EXCHANGE_KycWalletHandle); + kwh->cb = cb; + kwh->cb_cls = cb_cls; + kwh->url = TALER_url_join (url, + "kyc-wallet", + NULL); + if (NULL == kwh->url) + { + json_decref (req); + GNUNET_free (kwh); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (kwh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&kwh->ctx, + eh, + req)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (req); + GNUNET_free (kwh->url); + GNUNET_free (kwh); + return NULL; + } + json_decref (req); + kwh->job = GNUNET_CURL_job_add2 (ctx, + eh, + kwh->ctx.headers, + &handle_kyc_wallet_finished, + kwh); + return kwh; +} + + +void +TALER_EXCHANGE_kyc_wallet_cancel (struct TALER_EXCHANGE_KycWalletHandle *kwh) +{ + if (NULL != kwh->job) + { + GNUNET_CURL_job_cancel (kwh->job); + kwh->job = NULL; + } + GNUNET_free (kwh->url); + TALER_curl_easy_post_finished (&kwh->ctx); + GNUNET_free (kwh); +} + + +/* end of exchange_api_kyc_wallet.c */ diff --git a/src/lib/exchange_api_lookup_aml_decision.c b/src/lib/exchange_api_lookup_aml_decision.c new file mode 100644 index 000000000..01e98213b --- /dev/null +++ b/src/lib/exchange_api_lookup_aml_decision.c @@ -0,0 +1,419 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_lookup_aml_decision.c + * @brief Implementation of the /aml/$OFFICER_PUB/decision request + * @author Christian Grothoff + */ +#include "platform.h" +#include /* just for HTTP status codes */ +#include +#include +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A /coins/$COIN_PUB/link Handle + */ +struct TALER_EXCHANGE_LookupAmlDecision +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_LookupAmlDecisionCallback decision_cb; + + /** + * Closure for @e cb. + */ + void *decision_cb_cls; + + /** + * HTTP headers for the job. + */ + struct curl_slist *job_headers; +}; + + +/** + * Parse AML decision history. + * + * @param aml_history JSON array with AML history + * @param[out] aml_history_ar where to write the result + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_aml_history (const json_t *aml_history, + struct TALER_EXCHANGE_AmlDecisionDetail *aml_history_ar) +{ + json_t *obj; + size_t idx; + + json_array_foreach (aml_history, idx, obj) + { + struct TALER_EXCHANGE_AmlDecisionDetail *aml = &aml_history_ar[idx]; + uint32_t state32; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("decision_time", + &aml->decision_time), + GNUNET_JSON_spec_string ("justification", + &aml->justification), + TALER_JSON_spec_amount_any ("new_threshold", + &aml->new_threshold), + GNUNET_JSON_spec_uint32 ("new_state", + &state32), + GNUNET_JSON_spec_fixed_auto ("decider_pub", + &aml->decider_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (obj, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + aml->new_state = (enum TALER_AmlDecisionState) state32; + } + return GNUNET_OK; +} + + +/** + * Parse KYC response array. + * + * @param kyc_attributes JSON array with KYC details + * @param[out] kyc_attributes_ar where to write the result + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_kyc_attributes (const json_t *kyc_attributes, + struct TALER_EXCHANGE_KycHistoryDetail *kyc_attributes_ar) +{ + json_t *obj; + size_t idx; + + json_array_foreach (kyc_attributes, idx, obj) + { + struct TALER_EXCHANGE_KycHistoryDetail *kyc = &kyc_attributes_ar[idx]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("collection_time", + &kyc->collection_time), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("attributes", + &kyc->attributes), + NULL), + GNUNET_JSON_spec_string ("provider_section", + &kyc->provider_section), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (obj, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +/** + * Parse the provided decision data from the "200 OK" response. + * + * @param[in,out] lh handle (callback may be zero'ed out) + * @param json json reply with the data for one coin + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +parse_decision_ok (struct TALER_EXCHANGE_LookupAmlDecision *lh, + const json_t *json) +{ + struct TALER_EXCHANGE_AmlDecisionResponse lr = { + .hr.reply = json, + .hr.http_status = MHD_HTTP_OK + }; + const json_t *aml_history; + const json_t *kyc_attributes; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("aml_history", + &aml_history), + GNUNET_JSON_spec_array_const ("kyc_attributes", + &kyc_attributes), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + lr.details.ok.aml_history_length = json_array_size (aml_history); + lr.details.ok.kyc_attributes_length = json_array_size (kyc_attributes); + { + struct TALER_EXCHANGE_AmlDecisionDetail aml_history_ar[ + GNUNET_NZL (lr.details.ok.aml_history_length)]; + struct TALER_EXCHANGE_KycHistoryDetail kyc_attributes_ar[ + GNUNET_NZL (lr.details.ok.kyc_attributes_length)]; + enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; + + memset (aml_history_ar, + 0, + sizeof (aml_history_ar)); + memset (kyc_attributes_ar, + 0, + sizeof (kyc_attributes_ar)); + lr.details.ok.aml_history = aml_history_ar; + lr.details.ok.kyc_attributes = kyc_attributes_ar; + ret = parse_aml_history (aml_history, + aml_history_ar); + if (GNUNET_OK == ret) + ret = parse_kyc_attributes (kyc_attributes, + kyc_attributes_ar); + if (GNUNET_OK == ret) + { + lh->decision_cb (lh->decision_cb_cls, + &lr); + lh->decision_cb = NULL; + } + return ret; + } +} + + +/** + * Function called when we're done processing the + * HTTP /aml/$OFFICER_PUB/decision request. + * + * @param cls the `struct TALER_EXCHANGE_LookupAmlDecision` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_lookup_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_LookupAmlDecision *lh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_AmlDecisionResponse lr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + lh->job = NULL; + switch (response_code) + { + case 0: + lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + parse_decision_ok (lh, + j)) + { + GNUNET_break_op (0); + lr.hr.http_status = 0; + lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + GNUNET_assert (NULL == lh->decision_cb); + TALER_EXCHANGE_lookup_aml_decision_cancel (lh); + return; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Nothing really to verify, exchange says this coin was not melted; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Nothing really to verify, exchange says this coin was not melted; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange lookup AML decision\n", + (unsigned int) response_code, + (int) lr.hr.ec); + break; + } + if (NULL != lh->decision_cb) + lh->decision_cb (lh->decision_cb_cls, + &lr); + TALER_EXCHANGE_lookup_aml_decision_cancel (lh); +} + + +struct TALER_EXCHANGE_LookupAmlDecision * +TALER_EXCHANGE_lookup_aml_decision ( + struct GNUNET_CURL_Context *ctx, + const char *exchange_url, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + bool history, + TALER_EXCHANGE_LookupAmlDecisionCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_LookupAmlDecision *lh; + CURL *eh; + struct TALER_AmlOfficerPublicKeyP officer_pub; + struct TALER_AmlOfficerSignatureP officer_sig; + char arg_str[sizeof (officer_pub) * 2 + + sizeof (*h_payto) * 2 + 32]; + + GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, + &officer_pub.eddsa_pub); + TALER_officer_aml_query_sign (officer_priv, + &officer_sig); + { + char pub_str[sizeof (officer_pub) * 2]; + char pt_str[sizeof (*h_payto) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &officer_pub, + sizeof (officer_pub), + pub_str, + sizeof (pub_str)); + *end = '\0'; + end = GNUNET_STRINGS_data_to_string ( + h_payto, + sizeof (*h_payto), + pt_str, + sizeof (pt_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "aml/%s/decision/%s", + pub_str, + pt_str); + } + lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecision); + lh->decision_cb = cb; + lh->decision_cb_cls = cb_cls; + lh->url = TALER_url_join (exchange_url, + arg_str, + "history", + history + ? "true" + : NULL, + NULL); + if (NULL == lh->url) + { + GNUNET_free (lh); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (lh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (lh->url); + GNUNET_free (lh); + return NULL; + } + { + char *hdr; + char sig_str[sizeof (officer_sig) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &officer_sig, + sizeof (officer_sig), + sig_str, + sizeof (sig_str)); + *end = '\0'; + + GNUNET_asprintf (&hdr, + "%s: %s", + TALER_AML_OFFICER_SIGNATURE_HEADER, + sig_str); + lh->job_headers = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + lh->job_headers = curl_slist_append (lh->job_headers, + "Content-type: application/json"); + lh->job = GNUNET_CURL_job_add2 (ctx, + eh, + lh->job_headers, + &handle_lookup_finished, + lh); + } + return lh; +} + + +void +TALER_EXCHANGE_lookup_aml_decision_cancel ( + struct TALER_EXCHANGE_LookupAmlDecision *lh) +{ + if (NULL != lh->job) + { + GNUNET_CURL_job_cancel (lh->job); + lh->job = NULL; + } + curl_slist_free_all (lh->job_headers); + GNUNET_free (lh->url); + GNUNET_free (lh); +} + + +/* end of exchange_api_lookup_aml_decision.c */ diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c new file mode 100644 index 000000000..22222b1e4 --- /dev/null +++ b/src/lib/exchange_api_lookup_aml_decisions.c @@ -0,0 +1,378 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_lookup_aml_decisions.c + * @brief Implementation of the /aml/$OFFICER_PUB/decisions request + * @author Christian Grothoff + */ +#include "platform.h" +#include /* just for HTTP status codes */ +#include +#include +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A /coins/$COIN_PUB/link Handle + */ +struct TALER_EXCHANGE_LookupAmlDecisions +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_LookupAmlDecisionsCallback decisions_cb; + + /** + * Closure for @e cb. + */ + void *decisions_cb_cls; + + /** + * HTTP headers for the job. + */ + struct curl_slist *job_headers; +}; + + +/** + * Parse AML decision summary array. + * + * @param decisions JSON array with AML decision summaries + * @param[out] decision_ar where to write the result + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_aml_decisions (const json_t *decisions, + struct TALER_EXCHANGE_AmlDecisionSummary *decision_ar) +{ + json_t *obj; + size_t idx; + + json_array_foreach (decisions, idx, obj) + { + struct TALER_EXCHANGE_AmlDecisionSummary *decision = &decision_ar[idx]; + uint32_t state32; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_payto", + &decision->h_payto), + GNUNET_JSON_spec_uint32 ("current_state", + &state32), + TALER_JSON_spec_amount_any ("threshold", + &decision->threshold), + GNUNET_JSON_spec_uint64 ("rowid", + &decision->rowid), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (obj, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + decision->current_state = (enum TALER_AmlDecisionState) state32; + } + return GNUNET_OK; +} + + +/** + * Parse the provided decision data from the "200 OK" response. + * + * @param[in,out] lh handle (callback may be zero'ed out) + * @param json json reply with the data for one coin + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh, + const json_t *json) +{ + struct TALER_EXCHANGE_AmlDecisionsResponse lr = { + .hr.reply = json, + .hr.http_status = MHD_HTTP_OK + }; + const json_t *records; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("records", + &records), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + lr.details.ok.decisions_length = json_array_size (records); + { + struct TALER_EXCHANGE_AmlDecisionSummary decisions[ + GNUNET_NZL (lr.details.ok.decisions_length)]; + enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; + + lr.details.ok.decisions = decisions; + ret = parse_aml_decisions (records, + decisions); + if (GNUNET_OK == ret) + { + lh->decisions_cb (lh->decisions_cb_cls, + &lr); + lh->decisions_cb = NULL; + } + return ret; + } +} + + +/** + * Function called when we're done processing the + * HTTP /aml/$OFFICER_PUB/decisions request. + * + * @param cls the `struct TALER_EXCHANGE_LookupAmlDecisions` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_lookup_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_LookupAmlDecisions *lh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_AmlDecisionsResponse lr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + lh->job = NULL; + switch (response_code) + { + case 0: + lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + parse_decisions_ok (lh, + j)) + { + GNUNET_break_op (0); + lr.hr.http_status = 0; + lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + GNUNET_assert (NULL == lh->decisions_cb); + TALER_EXCHANGE_lookup_aml_decisions_cancel (lh); + return; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Nothing really to verify, exchange says this coin was not melted; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Nothing really to verify, exchange says this coin was not melted; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for lookup AML decisions\n", + (unsigned int) response_code, + (int) lr.hr.ec); + break; + } + if (NULL != lh->decisions_cb) + lh->decisions_cb (lh->decisions_cb_cls, + &lr); + TALER_EXCHANGE_lookup_aml_decisions_cancel (lh); +} + + +struct TALER_EXCHANGE_LookupAmlDecisions * +TALER_EXCHANGE_lookup_aml_decisions ( + struct GNUNET_CURL_Context *ctx, + const char *exchange_url, + uint64_t start, + int delta, + enum TALER_AmlDecisionState state, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + TALER_EXCHANGE_LookupAmlDecisionsCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_LookupAmlDecisions *lh; + CURL *eh; + struct TALER_AmlOfficerPublicKeyP officer_pub; + struct TALER_AmlOfficerSignatureP officer_sig; + char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32]; + const char *state_str = NULL; + + switch (state) + { + case TALER_AML_NORMAL: + state_str = "normal"; + break; + case TALER_AML_PENDING: + state_str = "pending"; + break; + case TALER_AML_FROZEN: + state_str = "frozen"; + break; + } + GNUNET_assert (NULL != state_str); + GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, + &officer_pub.eddsa_pub); + TALER_officer_aml_query_sign (officer_priv, + &officer_sig); + { + char pub_str[sizeof (officer_pub) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &officer_pub, + sizeof (officer_pub), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "aml/%s/decisions/%s", + pub_str, + state_str); + } + lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecisions); + lh->decisions_cb = cb; + lh->decisions_cb_cls = cb_cls; + { + char delta_s[24]; + char start_s[24]; + + GNUNET_snprintf (delta_s, + sizeof (delta_s), + "%d", + delta); + GNUNET_snprintf (start_s, + sizeof (start_s), + "%llu", + (unsigned long long) start); + lh->url = TALER_url_join (exchange_url, + arg_str, + "delta", + delta_s, + "start", + start_s, + NULL); + } + if (NULL == lh->url) + { + GNUNET_free (lh); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (lh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (lh->url); + GNUNET_free (lh); + return NULL; + } + { + char *hdr; + char sig_str[sizeof (officer_sig) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &officer_sig, + sizeof (officer_sig), + sig_str, + sizeof (sig_str)); + *end = '\0'; + + GNUNET_asprintf (&hdr, + "%s: %s", + TALER_AML_OFFICER_SIGNATURE_HEADER, + sig_str); + lh->job_headers = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + lh->job_headers = curl_slist_append (lh->job_headers, + "Content-type: application/json"); + lh->job = GNUNET_CURL_job_add2 (ctx, + eh, + lh->job_headers, + &handle_lookup_finished, + lh); + } + return lh; +} + + +void +TALER_EXCHANGE_lookup_aml_decisions_cancel ( + struct TALER_EXCHANGE_LookupAmlDecisions *lh) +{ + if (NULL != lh->job) + { + GNUNET_CURL_job_cancel (lh->job); + lh->job = NULL; + } + curl_slist_free_all (lh->job_headers); + GNUNET_free (lh->url); + GNUNET_free (lh); +} + + +/* end of exchange_api_lookup_aml_decisions.c */ diff --git a/src/lib/exchange_api_management_add_partner.c b/src/lib/exchange_api_management_add_partner.c new file mode 100644 index 000000000..fec66c567 --- /dev/null +++ b/src/lib/exchange_api_management_add_partner.c @@ -0,0 +1,218 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_add_partner.c + * @brief functions to add an partner by an AML officer + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementAddPartner +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementAddPartnerCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /management/partners request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAddPartner *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_add_partner_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementAddPartner *wh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementAddPartnerResponse apr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + wh->job = NULL; + switch (response_code) + { + case 0: + /* no reply */ + apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + apr.hr.hint = "server offline?"; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + apr.hr.ec = TALER_JSON_get_error_code (json); + apr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for adding exchange partner\n", + (unsigned int) response_code, + (int) apr.hr.ec); + break; + } + if (NULL != wh->cb) + { + wh->cb (wh->cb_cls, + &apr); + wh->cb = NULL; + } + TALER_EXCHANGE_management_add_partner_cancel (wh); +} + + +struct TALER_EXCHANGE_ManagementAddPartner * +TALER_EXCHANGE_management_add_partner ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_MasterPublicKeyP *partner_pub, + struct GNUNET_TIME_Timestamp start_date, + struct GNUNET_TIME_Timestamp end_date, + struct GNUNET_TIME_Relative wad_frequency, + const struct TALER_Amount *wad_fee, + const char *partner_base_url, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementAddPartnerCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementAddPartner *wh; + CURL *eh; + json_t *body; + + wh = GNUNET_new (struct TALER_EXCHANGE_ManagementAddPartner); + wh->cb = cb; + wh->cb_cls = cb_cls; + wh->ctx = ctx; + wh->url = TALER_url_join (url, + "management/partners", + NULL); + if (NULL == wh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("partner_base_url", + partner_base_url), + GNUNET_JSON_pack_timestamp ("start_date", + start_date), + GNUNET_JSON_pack_timestamp ("end_date", + end_date), + GNUNET_JSON_pack_time_rel ("wad_frequency", + wad_frequency), + GNUNET_JSON_pack_data_auto ("partner_pub", + &partner_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &master_sig), + TALER_JSON_pack_amount ("wad_fee", + wad_fee) + ); + eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&wh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (wh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wh->url); + wh->job = GNUNET_CURL_job_add2 (ctx, + eh, + wh->post_ctx.headers, + &handle_add_partner_finished, + wh); + if (NULL == wh->job) + { + TALER_EXCHANGE_management_add_partner_cancel (wh); + return NULL; + } + return wh; +} + + +void +TALER_EXCHANGE_management_add_partner_cancel ( + struct TALER_EXCHANGE_ManagementAddPartner *wh) +{ + if (NULL != wh->job) + { + GNUNET_CURL_job_cancel (wh->job); + wh->job = NULL; + } + TALER_curl_easy_post_finished (&wh->post_ctx); + GNUNET_free (wh->url); + GNUNET_free (wh); +} diff --git a/src/lib/exchange_api_management_auditor_disable.c b/src/lib/exchange_api_management_auditor_disable.c new file mode 100644 index 000000000..8bce7f74f --- /dev/null +++ b/src/lib/exchange_api_management_auditor_disable.c @@ -0,0 +1,219 @@ +/* + This file is part of TALER + Copyright (C) 2015-2021 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_auditor_disable.c + * @brief functions to disable an auditor + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + +/** + * @brief Handle for a POST /management/auditors/$AUDITOR_PUB/disable request. + */ +struct TALER_EXCHANGE_ManagementAuditorDisableHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementAuditorDisableCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/auditors/%s/disable request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorDisableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_auditor_disable_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementAuditorDisableHandle *ah = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementAuditorDisableResponse adr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ah->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + adr.hr.ec = TALER_JSON_get_error_code (json); + adr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management auditor disable\n", + (unsigned int) response_code, + (int) adr.hr.ec); + break; + } + if (NULL != ah->cb) + { + ah->cb (ah->cb_cls, + &adr); + ah->cb = NULL; + } + TALER_EXCHANGE_management_disable_auditor_cancel (ah); +} + + +struct TALER_EXCHANGE_ManagementAuditorDisableHandle * +TALER_EXCHANGE_management_disable_auditor ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AuditorPublicKeyP *auditor_pub, + struct GNUNET_TIME_Timestamp validity_end, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementAuditorDisableCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementAuditorDisableHandle *ah; + CURL *eh; + json_t *body; + + ah = GNUNET_new (struct TALER_EXCHANGE_ManagementAuditorDisableHandle); + ah->cb = cb; + ah->cb_cls = cb_cls; + ah->ctx = ctx; + { + char epub_str[sizeof (*auditor_pub) * 2]; + char arg_str[sizeof (epub_str) + 64]; + char *end; + + end = GNUNET_STRINGS_data_to_string (auditor_pub, + sizeof (*auditor_pub), + epub_str, + sizeof (epub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "management/auditors/%s/disable", + epub_str); + ah->url = TALER_url_join (url, + arg_str, + NULL); + } + if (NULL == ah->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ah); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig), + GNUNET_JSON_pack_timestamp ("validity_end", + validity_end)); + eh = TALER_EXCHANGE_curl_easy_get_ (ah->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ah->post_ctx, + eh, + body))) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (ah->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ah->url); + ah->job = GNUNET_CURL_job_add2 (ctx, + eh, + ah->post_ctx.headers, + &handle_auditor_disable_finished, + ah); + if (NULL == ah->job) + { + TALER_EXCHANGE_management_disable_auditor_cancel (ah); + return NULL; + } + return ah; +} + + +void +TALER_EXCHANGE_management_disable_auditor_cancel ( + struct TALER_EXCHANGE_ManagementAuditorDisableHandle *ah) +{ + if (NULL != ah->job) + { + GNUNET_CURL_job_cancel (ah->job); + ah->job = NULL; + } + TALER_curl_easy_post_finished (&ah->post_ctx); + GNUNET_free (ah->url); + GNUNET_free (ah); +} diff --git a/src/lib/exchange_api_management_auditor_enable.c b/src/lib/exchange_api_management_auditor_enable.c new file mode 100644 index 000000000..41c5049c2 --- /dev/null +++ b/src/lib/exchange_api_management_auditor_enable.c @@ -0,0 +1,224 @@ +/* + This file is part of TALER + Copyright (C) 2015-2021 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_auditor_enable.c + * @brief functions to enable an auditor + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +/** + * @brief Handle for a POST /management/auditors request. + */ +struct TALER_EXCHANGE_ManagementAuditorEnableHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementAuditorEnableCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /management/auditors request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_auditor_enable_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementAuditorEnableHandle *ah = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementAuditorEnableResponse aer = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ah->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + aer.hr.ec = TALER_JSON_get_error_code (json); + aer.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + ah->url); + if (NULL != json) + { + aer.hr.ec = TALER_JSON_get_error_code (json); + aer.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + aer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + aer.hr.hint = TALER_ErrorCode_get_hint (aer.hr.ec); + } + break; + case MHD_HTTP_CONFLICT: + aer.hr.ec = TALER_JSON_get_error_code (json); + aer.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + aer.hr.ec = TALER_JSON_get_error_code (json); + aer.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management auditor enable\n", + (unsigned int) response_code, + (int) aer.hr.ec); + break; + } + if (NULL != ah->cb) + { + ah->cb (ah->cb_cls, + &aer); + ah->cb = NULL; + } + TALER_EXCHANGE_management_enable_auditor_cancel (ah); +} + + +struct TALER_EXCHANGE_ManagementAuditorEnableHandle * +TALER_EXCHANGE_management_enable_auditor ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AuditorPublicKeyP *auditor_pub, + const char *auditor_url, + const char *auditor_name, + struct GNUNET_TIME_Timestamp validity_start, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementAuditorEnableCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementAuditorEnableHandle *ah; + CURL *eh; + json_t *body; + + ah = GNUNET_new (struct TALER_EXCHANGE_ManagementAuditorEnableHandle); + ah->cb = cb; + ah->cb_cls = cb_cls; + ah->ctx = ctx; + ah->url = TALER_url_join (url, + "management/auditors", + NULL); + if (NULL == ah->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ah); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("auditor_url", + auditor_url), + GNUNET_JSON_pack_string ("auditor_name", + auditor_name), + GNUNET_JSON_pack_data_auto ("auditor_pub", + auditor_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig), + GNUNET_JSON_pack_timestamp ("validity_start", + validity_start)); + eh = TALER_EXCHANGE_curl_easy_get_ (ah->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ah->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + json_decref (body); + if (NULL != eh) + curl_easy_cleanup (eh); + GNUNET_free (ah->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ah->url); + ah->job = GNUNET_CURL_job_add2 (ctx, + eh, + ah->post_ctx.headers, + &handle_auditor_enable_finished, + ah); + if (NULL == ah->job) + { + TALER_EXCHANGE_management_enable_auditor_cancel (ah); + return NULL; + } + return ah; +} + + +void +TALER_EXCHANGE_management_enable_auditor_cancel ( + struct TALER_EXCHANGE_ManagementAuditorEnableHandle *ah) +{ + if (NULL != ah->job) + { + GNUNET_CURL_job_cancel (ah->job); + ah->job = NULL; + } + TALER_curl_easy_post_finished (&ah->post_ctx); + GNUNET_free (ah->url); + GNUNET_free (ah); +} diff --git a/src/lib/exchange_api_management_drain_profits.c b/src/lib/exchange_api_management_drain_profits.c new file mode 100644 index 000000000..bc7232b87 --- /dev/null +++ b/src/lib/exchange_api_management_drain_profits.c @@ -0,0 +1,213 @@ +/* + This file is part of TALER + Copyright (C) 2020-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_drain_profits.c + * @brief functions to set wire fees at an exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "exchange_api_curl_defaults.h" +#include "taler_exchange_service.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementDrainProfitsHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementDrainProfitsCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/drain request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementDrainProfitsHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_drain_profits_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementDrainResponse dr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + dp->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + dr.hr.ec = TALER_JSON_get_error_code (json); + dr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + dr.hr.ec = TALER_JSON_get_error_code (json); + dr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_PRECONDITION_FAILED: + dr.hr.ec = TALER_JSON_get_error_code (json); + dr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + dr.hr.ec = TALER_JSON_get_error_code (json); + dr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management drain profits\n", + (unsigned int) response_code, + (int) dr.hr.ec); + break; + } + if (NULL != dp->cb) + { + dp->cb (dp->cb_cls, + &dr); + dp->cb = NULL; + } + TALER_EXCHANGE_management_drain_profits_cancel (dp); +} + + +struct TALER_EXCHANGE_ManagementDrainProfitsHandle * +TALER_EXCHANGE_management_drain_profits ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_WireTransferIdentifierRawP *wtid, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Timestamp date, + const char *account_section, + const char *payto_uri, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementDrainProfitsCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp; + CURL *eh; + json_t *body; + + dp = GNUNET_new (struct TALER_EXCHANGE_ManagementDrainProfitsHandle); + dp->cb = cb; + dp->cb_cls = cb_cls; + dp->ctx = ctx; + dp->url = TALER_url_join (url, + "management/drain", + NULL); + if (NULL == dp->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (dp); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("debit_account_section", + account_section), + GNUNET_JSON_pack_string ("credit_payto_uri", + payto_uri), + GNUNET_JSON_pack_data_auto ("wtid", + wtid), + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig), + GNUNET_JSON_pack_timestamp ("date", + date), + TALER_JSON_pack_amount ("amount", + amount)); + eh = TALER_EXCHANGE_curl_easy_get_ (dp->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&dp->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (dp->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + dp->url); + dp->job = GNUNET_CURL_job_add2 (ctx, + eh, + dp->post_ctx.headers, + &handle_drain_profits_finished, + dp); + if (NULL == dp->job) + { + TALER_EXCHANGE_management_drain_profits_cancel (dp); + return NULL; + } + return dp; +} + + +void +TALER_EXCHANGE_management_drain_profits_cancel ( + struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp) +{ + if (NULL != dp->job) + { + GNUNET_CURL_job_cancel (dp->job); + dp->job = NULL; + } + TALER_curl_easy_post_finished (&dp->post_ctx); + GNUNET_free (dp->url); + GNUNET_free (dp); +} diff --git a/src/lib/exchange_api_management_post_extensions.c b/src/lib/exchange_api_management_post_extensions.c new file mode 100644 index 000000000..00d1c5e3f --- /dev/null +++ b/src/lib/exchange_api_management_post_extensions.c @@ -0,0 +1,213 @@ +/* + This file is part of TALER + Copyright (C) 2015-2023 Taler Systems SA + + 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, see + + */ +/** + * @file lib/exchange_api_management_post_extensions.c + * @brief functions to handle the settings for extensions (p2p and age restriction) + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_extensions.h" +#include "exchange_api_curl_defaults.h" +#include "taler_exchange_service.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +/** + * @brief Handle for a POST /management/extensions request. + */ +struct TALER_EXCHANGE_ManagementPostExtensionsHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementPostExtensionsCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /management/extensions request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementPostExtensionsHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_extensions_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementPostExtensionsResponse per = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ph->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + per.hr.ec = TALER_JSON_get_error_code (json); + per.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + ph->url); + if (NULL != json) + { + per.hr.ec = TALER_JSON_get_error_code (json); + per.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + per.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + per.hr.hint = TALER_ErrorCode_get_hint (per.hr.ec); + } + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + per.hr.ec = TALER_JSON_get_error_code (json); + per.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management post extensions\n", + (unsigned int) response_code, + (int) per.hr.ec); + break; + } + if (NULL != ph->cb) + { + ph->cb (ph->cb_cls, + &per); + ph->cb = NULL; + } + TALER_EXCHANGE_management_post_extensions_cancel (ph); +} + + +struct TALER_EXCHANGE_ManagementPostExtensionsHandle * +TALER_EXCHANGE_management_post_extensions ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_EXCHANGE_ManagementPostExtensionsData *ped, + TALER_EXCHANGE_ManagementPostExtensionsCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph; + CURL *eh = NULL; + json_t *body = NULL; + + ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostExtensionsHandle); + ph->cb = cb; + ph->cb_cls = cb_cls; + ph->ctx = ctx; + ph->url = TALER_url_join (url, + "management/extensions", + NULL); + if (NULL == ph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ph); + return NULL; + } + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_object_steal ("extensions", + (json_t *) ped->extensions), + GNUNET_JSON_pack_data_auto ("extensions_sig", + &ped->extensions_sig)); + + eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ph->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (ph->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting URL '%s'\n", + ph->url); + ph->job = GNUNET_CURL_job_add2 (ctx, + eh, + ph->post_ctx.headers, + &handle_post_extensions_finished, + ph); + if (NULL == ph->job) + { + TALER_EXCHANGE_management_post_extensions_cancel (ph); + return NULL; + } + return ph; +} + + +void +TALER_EXCHANGE_management_post_extensions_cancel ( + struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph) +{ + if (NULL != ph->job) + { + GNUNET_CURL_job_cancel (ph->job); + ph->job = NULL; + } + TALER_curl_easy_post_finished (&ph->post_ctx); + GNUNET_free (ph->url); + GNUNET_free (ph); +} diff --git a/src/lib/exchange_api_management_post_keys.c b/src/lib/exchange_api_management_post_keys.c new file mode 100644 index 000000000..a46124d90 --- /dev/null +++ b/src/lib/exchange_api_management_post_keys.c @@ -0,0 +1,237 @@ +/* + This file is part of TALER + Copyright (C) 2015-2021 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_post_keys.c + * @brief functions to affirm the validity of exchange keys using the master private key + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +/** + * @brief Handle for a POST /management/keys request. + */ +struct TALER_EXCHANGE_ManagementPostKeysHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementPostKeysCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /management/keys request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementPostKeysHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_keys_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementPostKeysHandle *ph = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementPostKeysResponse pkr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + ph->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + pkr.hr.ec = TALER_JSON_get_error_code (json); + pkr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + pkr.hr.ec = TALER_JSON_get_error_code (json); + pkr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: + pkr.hr.ec = TALER_JSON_get_error_code (json); + pkr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + pkr.hr.ec = TALER_JSON_get_error_code (json); + pkr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management post keys\n", + (unsigned int) response_code, + (int) pkr.hr.ec); + break; + } + if (NULL != ph->cb) + { + ph->cb (ph->cb_cls, + &pkr); + ph->cb = NULL; + } + TALER_EXCHANGE_post_management_keys_cancel (ph); +} + + +struct TALER_EXCHANGE_ManagementPostKeysHandle * +TALER_EXCHANGE_post_management_keys ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_EXCHANGE_ManagementPostKeysData *pkd, + TALER_EXCHANGE_ManagementPostKeysCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementPostKeysHandle *ph; + CURL *eh; + json_t *body; + json_t *denom_sigs; + json_t *signkey_sigs; + + ph = GNUNET_new (struct TALER_EXCHANGE_ManagementPostKeysHandle); + ph->cb = cb; + ph->cb_cls = cb_cls; + ph->ctx = ctx; + ph->url = TALER_url_join (url, + "management/keys", + NULL); + if (NULL == ph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (ph); + return NULL; + } + denom_sigs = json_array (); + GNUNET_assert (NULL != denom_sigs); + for (unsigned int i = 0; inum_denom_sigs; i++) + { + const struct TALER_EXCHANGE_DenominationKeySignature *dks + = &pkd->denom_sigs[i]; + + GNUNET_assert (0 == + json_array_append_new ( + denom_sigs, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &dks->h_denom_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &dks->master_sig)))); + } + signkey_sigs = json_array (); + GNUNET_assert (NULL != signkey_sigs); + for (unsigned int i = 0; inum_sign_sigs; i++) + { + const struct TALER_EXCHANGE_SigningKeySignature *sks + = &pkd->sign_sigs[i]; + + GNUNET_assert (0 == + json_array_append_new ( + signkey_sigs, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("exchange_pub", + &sks->exchange_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &sks->master_sig)))); + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("denom_sigs", + denom_sigs), + GNUNET_JSON_pack_array_steal ("signkey_sigs", + signkey_sigs)); + eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ph->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (ph->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + ph->url); + ph->job = GNUNET_CURL_job_add2 (ctx, + eh, + ph->post_ctx.headers, + &handle_post_keys_finished, + ph); + if (NULL == ph->job) + { + TALER_EXCHANGE_post_management_keys_cancel (ph); + return NULL; + } + return ph; +} + + +void +TALER_EXCHANGE_post_management_keys_cancel ( + struct TALER_EXCHANGE_ManagementPostKeysHandle *ph) +{ + if (NULL != ph->job) + { + GNUNET_CURL_job_cancel (ph->job); + ph->job = NULL; + } + TALER_curl_easy_post_finished (&ph->post_ctx); + GNUNET_free (ph->url); + GNUNET_free (ph); +} diff --git a/src/lib/exchange_api_management_revoke_denomination_key.c b/src/lib/exchange_api_management_revoke_denomination_key.c new file mode 100644 index 000000000..aa4d527a7 --- /dev/null +++ b/src/lib/exchange_api_management_revoke_denomination_key.c @@ -0,0 +1,221 @@ +/* + This file is part of TALER + Copyright (C) 2015-2020 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_revoke_denomination_key.c + * @brief functions to revoke an exchange denomination key + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +/** + * @brief Handle for a POST /management/denominations/$H_DENOM_PUB/revoke request. + */ +struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementRevokeDenominationKeyCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/denominations/$H_DENOM_PUB/revoke request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_revoke_denomination_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementRevokeDenominationResponse rdr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + rh->job = NULL; + switch (response_code) + { + case 0: + /* no reply */ + rdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + rdr.hr.hint = "server offline?"; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + rdr.hr.ec = TALER_JSON_get_error_code (json); + rdr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + rdr.hr.ec = TALER_JSON_get_error_code (json); + rdr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management revoke denomination\n", + (unsigned int) response_code, + (int) rdr.hr.ec); + break; + } + if (NULL != rh->cb) + { + rh->cb (rh->cb_cls, + &rdr); + rh->cb = NULL; + } + TALER_EXCHANGE_management_revoke_denomination_key_cancel (rh); +} + + +struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle * +TALER_EXCHANGE_management_revoke_denomination_key ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_DenominationHashP *h_denom_pub, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementRevokeDenominationKeyCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh; + CURL *eh; + json_t *body; + + rh = GNUNET_new (struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle); + rh->cb = cb; + rh->cb_cls = cb_cls; + rh->ctx = ctx; + { + char epub_str[sizeof (*h_denom_pub) * 2]; + char arg_str[sizeof (epub_str) + 64]; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_denom_pub, + sizeof (*h_denom_pub), + epub_str, + sizeof (epub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "management/denominations/%s/revoke", + epub_str); + rh->url = TALER_url_join (url, + arg_str, + NULL); + } + if (NULL == rh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (rh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig)); + if (NULL == body) + { + GNUNET_break (0); + GNUNET_free (rh->url); + GNUNET_free (rh); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (rh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&rh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (rh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + rh->url); + rh->job = GNUNET_CURL_job_add2 (ctx, + eh, + rh->post_ctx.headers, + &handle_revoke_denomination_finished, + rh); + if (NULL == rh->job) + { + TALER_EXCHANGE_management_revoke_denomination_key_cancel (rh); + return NULL; + } + return rh; +} + + +void +TALER_EXCHANGE_management_revoke_denomination_key_cancel ( + struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *rh) +{ + if (NULL != rh->job) + { + GNUNET_CURL_job_cancel (rh->job); + rh->job = NULL; + } + TALER_curl_easy_post_finished (&rh->post_ctx); + GNUNET_free (rh->url); + GNUNET_free (rh); +} diff --git a/src/lib/exchange_api_management_revoke_signing_key.c b/src/lib/exchange_api_management_revoke_signing_key.c new file mode 100644 index 000000000..c4d634248 --- /dev/null +++ b/src/lib/exchange_api_management_revoke_signing_key.c @@ -0,0 +1,211 @@ +/* + This file is part of TALER + Copyright (C) 2015-2021 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_revoke_signing_key.c + * @brief functions to revoke an exchange online signing key + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementRevokeSigningKeyCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/signkeys/%s/revoke request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_revoke_signing_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse rsr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + rh->job = NULL; + switch (response_code) + { + case 0: + /* no reply */ + rsr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + rsr.hr.hint = "server offline?"; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + rsr.hr.ec = TALER_JSON_get_error_code (json); + rsr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + rsr.hr.ec = TALER_JSON_get_error_code (json); + rsr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management revoke signkey\n", + (unsigned int) response_code, + (int) rsr.hr.ec); + break; + } + if (NULL != rh->cb) + { + rh->cb (rh->cb_cls, + &rsr); + rh->cb = NULL; + } + TALER_EXCHANGE_management_revoke_signing_key_cancel (rh); +} + + +struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle * +TALER_EXCHANGE_management_revoke_signing_key ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementRevokeSigningKeyCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh; + CURL *eh; + json_t *body; + + rh = GNUNET_new (struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle); + rh->cb = cb; + rh->cb_cls = cb_cls; + rh->ctx = ctx; + { + char epub_str[sizeof (*exchange_pub) * 2]; + char arg_str[sizeof (epub_str) + 64]; + char *end; + + end = GNUNET_STRINGS_data_to_string (exchange_pub, + sizeof (*exchange_pub), + epub_str, + sizeof (epub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "management/signkeys/%s/revoke", + epub_str); + rh->url = TALER_url_join (url, + arg_str, + NULL); + } + if (NULL == rh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (rh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig)); + eh = TALER_EXCHANGE_curl_easy_get_ (rh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&rh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (rh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + rh->url); + rh->job = GNUNET_CURL_job_add2 (ctx, + eh, + rh->post_ctx.headers, + &handle_revoke_signing_finished, + rh); + if (NULL == rh->job) + { + TALER_EXCHANGE_management_revoke_signing_key_cancel (rh); + return NULL; + } + return rh; +} + + +void +TALER_EXCHANGE_management_revoke_signing_key_cancel ( + struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *rh) +{ + if (NULL != rh->job) + { + GNUNET_CURL_job_cancel (rh->job); + rh->job = NULL; + } + TALER_curl_easy_post_finished (&rh->post_ctx); + GNUNET_free (rh->url); + GNUNET_free (rh); +} diff --git a/src/lib/exchange_api_management_set_global_fee.c b/src/lib/exchange_api_management_set_global_fee.c new file mode 100644 index 000000000..54c37fd64 --- /dev/null +++ b/src/lib/exchange_api_management_set_global_fee.c @@ -0,0 +1,235 @@ +/* + This file is part of TALER + Copyright (C) 2020-2022 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_set_global_fee.c + * @brief functions to set global fees at an exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "exchange_api_curl_defaults.h" +#include "taler_exchange_service.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementSetGlobalFeeCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/global request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_set_global_fee_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *sgfh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse sfr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + sgfh->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + sfr.hr.ec = TALER_JSON_get_error_code (json); + sfr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + sgfh->url); + if (NULL != json) + { + sfr.hr.ec = TALER_JSON_get_error_code (json); + sfr.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + sfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + sfr.hr.hint = TALER_ErrorCode_get_hint (sfr.hr.ec); + } + break; + case MHD_HTTP_CONFLICT: + sfr.hr.ec = TALER_JSON_get_error_code (json); + sfr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_PRECONDITION_FAILED: + sfr.hr.ec = TALER_JSON_get_error_code (json); + sfr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + sfr.hr.ec = TALER_JSON_get_error_code (json); + sfr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management set global fee\n", + (unsigned int) response_code, + (int) sfr.hr.ec); + break; + } + if (NULL != sgfh->cb) + { + sgfh->cb (sgfh->cb_cls, + &sfr); + sgfh->cb = NULL; + } + TALER_EXCHANGE_management_set_global_fees_cancel (sgfh); +} + + +struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle * +TALER_EXCHANGE_management_set_global_fees ( + struct GNUNET_CURL_Context *ctx, + const char *exchange_base_url, + struct GNUNET_TIME_Timestamp validity_start, + struct GNUNET_TIME_Timestamp validity_end, + const struct TALER_GlobalFeeSet *fees, + struct GNUNET_TIME_Relative purse_timeout, + struct GNUNET_TIME_Relative history_expiration, + uint32_t purse_account_limit, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementSetGlobalFeeCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *sgfh; + CURL *eh; + json_t *body; + + sgfh = GNUNET_new (struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle); + sgfh->cb = cb; + sgfh->cb_cls = cb_cls; + sgfh->ctx = ctx; + sgfh->url = TALER_url_join (exchange_base_url, + "management/global-fee", + NULL); + if (NULL == sgfh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (sgfh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig), + GNUNET_JSON_pack_timestamp ("fee_start", + validity_start), + GNUNET_JSON_pack_timestamp ("fee_end", + validity_end), + TALER_JSON_pack_amount ("history_fee", + &fees->history), + TALER_JSON_pack_amount ("account_fee", + &fees->account), + TALER_JSON_pack_amount ("purse_fee", + &fees->purse), + GNUNET_JSON_pack_time_rel ("purse_timeout", + purse_timeout), + GNUNET_JSON_pack_time_rel ("history_expiration", + history_expiration), + GNUNET_JSON_pack_uint64 ("purse_account_limit", + purse_account_limit)); + eh = TALER_EXCHANGE_curl_easy_get_ (sgfh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&sgfh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (sgfh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + sgfh->url); + sgfh->job = GNUNET_CURL_job_add2 (ctx, + eh, + sgfh->post_ctx.headers, + &handle_set_global_fee_finished, + sgfh); + if (NULL == sgfh->job) + { + TALER_EXCHANGE_management_set_global_fees_cancel (sgfh); + return NULL; + } + return sgfh; +} + + +void +TALER_EXCHANGE_management_set_global_fees_cancel ( + struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *sgfh) +{ + if (NULL != sgfh->job) + { + GNUNET_CURL_job_cancel (sgfh->job); + sgfh->job = NULL; + } + TALER_curl_easy_post_finished (&sgfh->post_ctx); + GNUNET_free (sgfh->url); + GNUNET_free (sgfh); +} diff --git a/src/lib/exchange_api_management_set_wire_fee.c b/src/lib/exchange_api_management_set_wire_fee.c new file mode 100644 index 000000000..03cab8c99 --- /dev/null +++ b/src/lib/exchange_api_management_set_wire_fee.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + Copyright (C) 2020-2022 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_set_wire_fee.c + * @brief functions to set wire fees at an exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "exchange_api_curl_defaults.h" +#include "taler_exchange_service.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementSetWireFeeHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementSetWireFeeCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/wire request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_set_wire_fee_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementSetWireFeeHandle *swfh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementSetWireFeeResponse swr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + swfh->job = NULL; + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + swr.hr.ec = TALER_JSON_get_error_code (json); + swr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + swfh->url); + if (NULL != json) + { + swr.hr.ec = TALER_JSON_get_error_code (json); + swr.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + swr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + swr.hr.hint = TALER_ErrorCode_get_hint (swr.hr.ec); + } + break; + case MHD_HTTP_CONFLICT: + swr.hr.ec = TALER_JSON_get_error_code (json); + swr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_PRECONDITION_FAILED: + swr.hr.ec = TALER_JSON_get_error_code (json); + swr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + swr.hr.ec = TALER_JSON_get_error_code (json); + swr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management set wire fee\n", + (unsigned int) response_code, + (int) swr.hr.ec); + break; + } + if (NULL != swfh->cb) + { + swfh->cb (swfh->cb_cls, + &swr); + swfh->cb = NULL; + } + TALER_EXCHANGE_management_set_wire_fees_cancel (swfh); +} + + +struct TALER_EXCHANGE_ManagementSetWireFeeHandle * +TALER_EXCHANGE_management_set_wire_fees ( + struct GNUNET_CURL_Context *ctx, + const char *exchange_base_url, + const char *wire_method, + struct GNUNET_TIME_Timestamp validity_start, + struct GNUNET_TIME_Timestamp validity_end, + const struct TALER_WireFeeSet *fees, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementSetWireFeeCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementSetWireFeeHandle *swfh; + CURL *eh; + json_t *body; + + swfh = GNUNET_new (struct TALER_EXCHANGE_ManagementSetWireFeeHandle); + swfh->cb = cb; + swfh->cb_cls = cb_cls; + swfh->ctx = ctx; + swfh->url = TALER_url_join (exchange_base_url, + "management/wire-fee", + NULL); + if (NULL == swfh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (swfh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("wire_method", + wire_method), + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig), + GNUNET_JSON_pack_timestamp ("fee_start", + validity_start), + GNUNET_JSON_pack_timestamp ("fee_end", + validity_end), + TALER_JSON_pack_amount ("closing_fee", + &fees->closing), + TALER_JSON_pack_amount ("wire_fee", + &fees->wire)); + eh = TALER_EXCHANGE_curl_easy_get_ (swfh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&swfh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (swfh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + swfh->url); + swfh->job = GNUNET_CURL_job_add2 (ctx, + eh, + swfh->post_ctx.headers, + &handle_set_wire_fee_finished, + swfh); + if (NULL == swfh->job) + { + TALER_EXCHANGE_management_set_wire_fees_cancel (swfh); + return NULL; + } + return swfh; +} + + +void +TALER_EXCHANGE_management_set_wire_fees_cancel ( + struct TALER_EXCHANGE_ManagementSetWireFeeHandle *swfh) +{ + if (NULL != swfh->job) + { + GNUNET_CURL_job_cancel (swfh->job); + swfh->job = NULL; + } + TALER_curl_easy_post_finished (&swfh->post_ctx); + GNUNET_free (swfh->url); + GNUNET_free (swfh); +} diff --git a/src/lib/exchange_api_management_update_aml_officer.c b/src/lib/exchange_api_management_update_aml_officer.c new file mode 100644 index 000000000..76cbc7e8d --- /dev/null +++ b/src/lib/exchange_api_management_update_aml_officer.c @@ -0,0 +1,229 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_update_aml_officer.c + * @brief functions to update AML officer status + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementUpdateAmlOfficer +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementUpdateAmlOfficerCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/wire request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_update_aml_officer_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse uar = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + wh->job = NULL; + switch (response_code) + { + case 0: + /* no reply */ + uar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + uar.hr.hint = "server offline?"; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + uar.hr.ec = TALER_JSON_get_error_code (json); + uar.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + wh->url); + if (NULL != json) + { + uar.hr.ec = TALER_JSON_get_error_code (json); + uar.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + uar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + uar.hr.hint = TALER_ErrorCode_get_hint (uar.hr.ec); + } + break; + case MHD_HTTP_CONFLICT: + uar.hr.ec = TALER_JSON_get_error_code (json); + uar.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + uar.hr.ec = TALER_JSON_get_error_code (json); + uar.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management update AML officer\n", + (unsigned int) response_code, + (int) uar.hr.ec); + break; + } + if (NULL != wh->cb) + { + wh->cb (wh->cb_cls, + &uar); + wh->cb = NULL; + } + TALER_EXCHANGE_management_update_aml_officer_cancel (wh); +} + + +struct TALER_EXCHANGE_ManagementUpdateAmlOfficer * +TALER_EXCHANGE_management_update_aml_officer ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *officer_name, + struct GNUNET_TIME_Timestamp change_date, + bool is_active, + bool read_only, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementUpdateAmlOfficerCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh; + CURL *eh; + json_t *body; + + wh = GNUNET_new (struct TALER_EXCHANGE_ManagementUpdateAmlOfficer); + wh->cb = cb; + wh->cb_cls = cb_cls; + wh->ctx = ctx; + wh->url = TALER_url_join (url, + "management/aml-officers", + NULL); + if (NULL == wh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("officer_name", + officer_name), + GNUNET_JSON_pack_data_auto ("officer_pub", + officer_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + master_sig), + GNUNET_JSON_pack_bool ("is_active", + is_active), + GNUNET_JSON_pack_bool ("read_only", + read_only), + GNUNET_JSON_pack_timestamp ("change_date", + change_date)); + eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&wh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (wh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wh->url); + wh->job = GNUNET_CURL_job_add2 (ctx, + eh, + wh->post_ctx.headers, + &handle_update_aml_officer_finished, + wh); + if (NULL == wh->job) + { + TALER_EXCHANGE_management_update_aml_officer_cancel (wh); + return NULL; + } + return wh; +} + + +void +TALER_EXCHANGE_management_update_aml_officer_cancel ( + struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *wh) +{ + if (NULL != wh->job) + { + GNUNET_CURL_job_cancel (wh->job); + wh->job = NULL; + } + TALER_curl_easy_post_finished (&wh->post_ctx); + GNUNET_free (wh->url); + GNUNET_free (wh); +} diff --git a/src/lib/exchange_api_management_wire_disable.c b/src/lib/exchange_api_management_wire_disable.c new file mode 100644 index 000000000..30749b0e4 --- /dev/null +++ b/src/lib/exchange_api_management_wire_disable.c @@ -0,0 +1,220 @@ +/* + This file is part of TALER + Copyright (C) 2015-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_wire_disable.c + * @brief functions to disable an exchange wire method / bank account + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementWireDisableHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementWireDisableCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/wire/disable request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorDisableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_auditor_disable_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementWireDisableHandle *wh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementWireDisableResponse wdr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + wh->job = NULL; + switch (response_code) + { + case 0: + /* no reply */ + wdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + wdr.hr.hint = "server offline?"; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + wdr.hr.ec = TALER_JSON_get_error_code (json); + wdr.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + wh->url); + if (NULL != json) + { + wdr.hr.ec = TALER_JSON_get_error_code (json); + wdr.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + wdr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + wdr.hr.hint = TALER_ErrorCode_get_hint (wdr.hr.ec); + } + break; + case MHD_HTTP_CONFLICT: + wdr.hr.ec = TALER_JSON_get_error_code (json); + wdr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + wdr.hr.ec = TALER_JSON_get_error_code (json); + wdr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d exchange management disable wire\n", + (unsigned int) response_code, + (int) wdr.hr.ec); + break; + } + if (NULL != wh->cb) + { + wh->cb (wh->cb_cls, + &wdr); + wh->cb = NULL; + } + TALER_EXCHANGE_management_disable_wire_cancel (wh); +} + + +struct TALER_EXCHANGE_ManagementWireDisableHandle * +TALER_EXCHANGE_management_disable_wire ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const char *payto_uri, + struct GNUNET_TIME_Timestamp validity_end, + const struct TALER_MasterSignatureP *master_sig, + TALER_EXCHANGE_ManagementWireDisableCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementWireDisableHandle *wh; + CURL *eh; + json_t *body; + + wh = GNUNET_new (struct TALER_EXCHANGE_ManagementWireDisableHandle); + wh->cb = cb; + wh->cb_cls = cb_cls; + wh->ctx = ctx; + wh->url = TALER_url_join (url, + "management/wire/disable", + NULL); + if (NULL == wh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("payto_uri", + payto_uri), + GNUNET_JSON_pack_data_auto ("master_sig_del", + master_sig), + GNUNET_JSON_pack_timestamp ("validity_end", + validity_end)); + eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&wh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (wh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wh->url); + wh->job = GNUNET_CURL_job_add2 (ctx, + eh, + wh->post_ctx.headers, + &handle_auditor_disable_finished, + wh); + if (NULL == wh->job) + { + TALER_EXCHANGE_management_disable_wire_cancel (wh); + return NULL; + } + return wh; +} + + +void +TALER_EXCHANGE_management_disable_wire_cancel ( + struct TALER_EXCHANGE_ManagementWireDisableHandle *wh) +{ + if (NULL != wh->job) + { + GNUNET_CURL_job_cancel (wh->job); + wh->job = NULL; + } + TALER_curl_easy_post_finished (&wh->post_ctx); + GNUNET_free (wh->url); + GNUNET_free (wh); +} diff --git a/src/lib/exchange_api_management_wire_enable.c b/src/lib/exchange_api_management_wire_enable.c new file mode 100644 index 000000000..5add3e0b0 --- /dev/null +++ b/src/lib/exchange_api_management_wire_enable.c @@ -0,0 +1,245 @@ +/* + This file is part of TALER + Copyright (C) 2015-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_management_wire_enable.c + * @brief functions to enable an exchange wire method / bank account + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "exchange_api_curl_defaults.h" +#include "taler_signatures.h" +#include "taler_curl_lib.h" +#include "taler_json_lib.h" + + +struct TALER_EXCHANGE_ManagementWireEnableHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_ManagementWireEnableCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP /management/wire request. + * + * @param cls the `struct TALER_EXCHANGE_ManagementAuditorEnableHandle *` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_auditor_enable_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_ManagementWireEnableHandle *wh = cls; + const json_t *json = response; + struct TALER_EXCHANGE_ManagementWireEnableResponse wer = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + wh->job = NULL; + switch (response_code) + { + case 0: + /* no reply */ + wer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + wer.hr.hint = "server offline?"; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + wer.hr.ec = TALER_JSON_get_error_code (json); + wer.hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", + wh->url); + if (NULL != json) + { + wer.hr.ec = TALER_JSON_get_error_code (json); + wer.hr.hint = TALER_JSON_get_error_hint (json); + } + else + { + wer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + wer.hr.hint = TALER_ErrorCode_get_hint (wer.hr.ec); + } + break; + case MHD_HTTP_CONFLICT: + wer.hr.ec = TALER_JSON_get_error_code (json); + wer.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + wer.hr.ec = TALER_JSON_get_error_code (json); + wer.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange management enable wire\n", + (unsigned int) response_code, + (int) wer.hr.ec); + break; + } + if (NULL != wh->cb) + { + wh->cb (wh->cb_cls, + &wer); + wh->cb = NULL; + } + TALER_EXCHANGE_management_enable_wire_cancel (wh); +} + + +struct TALER_EXCHANGE_ManagementWireEnableHandle * +TALER_EXCHANGE_management_enable_wire ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const char *payto_uri, + const char *conversion_url, + const json_t *debit_restrictions, + const json_t *credit_restrictions, + struct GNUNET_TIME_Timestamp validity_start, + const struct TALER_MasterSignatureP *master_sig1, + const struct TALER_MasterSignatureP *master_sig2, + TALER_EXCHANGE_ManagementWireEnableCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGE_ManagementWireEnableHandle *wh; + CURL *eh; + json_t *body; + + { + char *msg = TALER_payto_validate (payto_uri); + + if (NULL != msg) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "payto URI is malformed: %s\n", + msg); + GNUNET_free (msg); + return NULL; + } + } + wh = GNUNET_new (struct TALER_EXCHANGE_ManagementWireEnableHandle); + wh->cb = cb; + wh->cb_cls = cb_cls; + wh->ctx = ctx; + wh->url = TALER_url_join (url, + "management/wire", + NULL); + if (NULL == wh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (wh); + return NULL; + } + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("payto_uri", + payto_uri), + GNUNET_JSON_pack_array_incref ("debit_restrictions", + (json_t *) debit_restrictions), + GNUNET_JSON_pack_array_incref ("credit_restrictions", + (json_t *) credit_restrictions), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("conversion_url", + conversion_url)), + GNUNET_JSON_pack_data_auto ("master_sig_add", + master_sig1), + GNUNET_JSON_pack_data_auto ("master_sig_wire", + master_sig2), + GNUNET_JSON_pack_timestamp ("validity_start", + validity_start)); + eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&wh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (wh->url); + return NULL; + } + json_decref (body); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + wh->url); + wh->job = GNUNET_CURL_job_add2 (ctx, + eh, + wh->post_ctx.headers, + &handle_auditor_enable_finished, + wh); + if (NULL == wh->job) + { + TALER_EXCHANGE_management_enable_wire_cancel (wh); + return NULL; + } + return wh; +} + + +void +TALER_EXCHANGE_management_enable_wire_cancel ( + struct TALER_EXCHANGE_ManagementWireEnableHandle *wh) +{ + if (NULL != wh->job) + { + GNUNET_CURL_job_cancel (wh->job); + wh->job = NULL; + } + TALER_curl_easy_post_finished (&wh->post_ctx); + GNUNET_free (wh->url); + GNUNET_free (wh); +} diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c new file mode 100644 index 000000000..ba4241dab --- /dev/null +++ b/src/lib/exchange_api_melt.c @@ -0,0 +1,596 @@ +/* + This file is part of TALER + Copyright (C) 2015-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_melt.c + * @brief Implementation of the /coins/$COIN_PUB/melt request + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_common.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" +#include "exchange_api_refresh_common.h" + + +/** + * @brief A /coins/$COIN_PUB/melt Handle + */ +struct TALER_EXCHANGE_MeltHandle +{ + + /** + * The keys of the this request handle will use + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * The url for this request. + */ + char *url; + + /** + * The exchange base url. + */ + char *exchange_url; + + /** + * Curl context. + */ + struct GNUNET_CURL_Context *cctx; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with refresh melt failure results. + */ + TALER_EXCHANGE_MeltCallback melt_cb; + + /** + * Closure for @e result_cb and @e melt_failure_cb. + */ + void *melt_cb_cls; + + /** + * Actual information about the melt operation. + */ + struct MeltData md; + + /** + * The secret the entire melt operation is seeded from. + */ + struct TALER_RefreshMasterSecretP rms; + + /** + * Details about the characteristics of the requested melt operation. + */ + const struct TALER_EXCHANGE_RefreshData *rd; + + /** + * Array of `num_fresh_coins` per-coin values + * returned from melt operation. + */ + struct TALER_EXCHANGE_MeltBlindingDetail *mbds; + + /** + * Handle for the preflight request, or NULL. + */ + struct TALER_EXCHANGE_CsRMeltHandle *csr; + + /** + * Public key of the coin being melted. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Signature affirming the melt. + */ + struct TALER_CoinSpendSignatureP coin_sig; + + /** + * @brief Public information about the coin's denomination key + */ + const struct TALER_EXCHANGE_DenomPublicKey *dki; + + /** + * Gamma value chosen by the exchange during melt. + */ + uint32_t noreveal_index; + + /** + * True if we need to include @e rms in our melt request. + */ + bool send_rms; +}; + + +/** + * Verify that the signature on the "200 OK" response + * from the exchange is valid. + * + * @param[in,out] mh melt handle + * @param json json reply with the signature + * @param[out] exchange_pub public key of the exchange used for the signature + * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not + */ +static enum GNUNET_GenericReturnValue +verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh, + const json_t *json, + struct TALER_ExchangePublicKeyP *exchange_pub) +{ + struct TALER_ExchangeSignatureP exchange_sig; + const struct TALER_EXCHANGE_Keys *key_state; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + &exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + exchange_pub), + GNUNET_JSON_spec_uint32 ("noreveal_index", + &mh->noreveal_index), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* check that exchange signing key is permitted */ + key_state = mh->keys; + if (GNUNET_OK != + TALER_EXCHANGE_test_signing_key (key_state, + exchange_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* check that noreveal index is in permitted range */ + if (TALER_CNC_KAPPA <= mh->noreveal_index) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + TALER_exchange_online_melt_confirmation_verify ( + &mh->md.rc, + mh->noreveal_index, + exchange_pub, + &exchange_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /coins/$COIN_PUB/melt request. + * + * @param cls the `struct TALER_EXCHANGE_MeltHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_melt_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_MeltHandle *mh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_MeltResponse mr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + mh->job = NULL; + switch (response_code) + { + case 0: + mr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + verify_melt_signature_ok (mh, + j, + &mr.details.ok.sign_key)) + { + GNUNET_break_op (0); + mr.hr.http_status = 0; + mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; + break; + } + mr.details.ok.noreveal_index = mh->noreveal_index; + mr.details.ok.num_mbds = mh->rd->fresh_pks_len; + mr.details.ok.mbds = mh->mbds; + mh->melt_cb (mh->melt_cb_cls, + &mr); + mh->melt_cb = NULL; + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + mr.hr.ec = TALER_JSON_get_error_code (j); + mr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONFLICT: + mr.hr.ec = TALER_JSON_get_error_code (j); + mr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify, exchange says one of the signatures is + invalid; assuming we checked them, this should never happen, we + should pass the JSON reply to the application */ + mr.hr.ec = TALER_JSON_get_error_code (j); + mr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + mr.hr.ec = TALER_JSON_get_error_code (j); + mr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + mr.hr.ec = TALER_JSON_get_error_code (j); + mr.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + mr.hr.ec = TALER_JSON_get_error_code (j); + mr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange melt\n", + (unsigned int) response_code, + mr.hr.ec); + GNUNET_break_op (0); + break; + } + if (NULL != mh->melt_cb) + mh->melt_cb (mh->melt_cb_cls, + &mr); + TALER_EXCHANGE_melt_cancel (mh); +} + + +/** + * Start the actual melt operation, now that we have + * the exchange's input values. + * + * @param[in,out] mh melt operation to run + * @return #GNUNET_OK if we could start the operation + */ +static enum GNUNET_GenericReturnValue +start_melt (struct TALER_EXCHANGE_MeltHandle *mh) +{ + const struct TALER_EXCHANGE_Keys *key_state; + json_t *melt_obj; + CURL *eh; + char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; + struct TALER_DenominationHashP h_denom_pub; + struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len]; + + for (unsigned int i = 0; ird->fresh_pks_len; i++) + alg_values[i] = mh->mbds[i].alg_value; + if (GNUNET_OK != + TALER_EXCHANGE_get_melt_data_ (&mh->rms, + mh->rd, + alg_values, + &mh->md)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + TALER_denom_pub_hash (&mh->md.melted_coin.pub_key, + &h_denom_pub); + TALER_wallet_melt_sign ( + &mh->md.melted_coin.melt_amount_with_fee, + &mh->md.melted_coin.fee_melt, + &mh->md.rc, + &h_denom_pub, + mh->md.melted_coin.h_age_commitment, + &mh->md.melted_coin.coin_priv, + &mh->coin_sig); + GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv, + &mh->coin_pub.eddsa_pub); + melt_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("denom_pub_hash", + &h_denom_pub), + TALER_JSON_pack_denom_sig ("denom_sig", + &mh->md.melted_coin.sig), + GNUNET_JSON_pack_data_auto ("confirm_sig", + &mh->coin_sig), + TALER_JSON_pack_amount ("value_with_fee", + &mh->md.melted_coin.melt_amount_with_fee), + GNUNET_JSON_pack_data_auto ("rc", + &mh->md.rc), + GNUNET_JSON_pack_allow_null ( + (NULL != mh->md.melted_coin.h_age_commitment) + ? GNUNET_JSON_pack_data_auto ("age_commitment_hash", + mh->md.melted_coin.h_age_commitment) + : GNUNET_JSON_pack_string ("age_commitment_hash", + NULL)), + GNUNET_JSON_pack_allow_null ( + mh->send_rms + ? GNUNET_JSON_pack_data_auto ("rms", + &mh->rms) + : GNUNET_JSON_pack_string ("rms", + NULL))); + { + char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &mh->coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "coins/%s/melt", + pub_str); + } + + key_state = mh->keys; + mh->dki = TALER_EXCHANGE_get_denomination_key (key_state, + &mh->md.melted_coin.pub_key); + + /* and now we can at last begin the actual request handling */ + + mh->url = TALER_url_join (mh->exchange_url, + arg_str, + NULL); + if (NULL == mh->url) + { + json_decref (melt_obj); + return GNUNET_SYSERR; + } + eh = TALER_EXCHANGE_curl_easy_get_ (mh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&mh->ctx, + eh, + melt_obj)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (melt_obj); + return GNUNET_SYSERR; + } + json_decref (melt_obj); + mh->job = GNUNET_CURL_job_add2 (mh->cctx, + eh, + mh->ctx.headers, + &handle_melt_finished, + mh); + return GNUNET_OK; +} + + +/** + * The melt request @a mh failed, return an error to + * the application and cancel the operation. + * + * @param[in] mh melt request that failed + * @param ec error code to fail with + */ +static void +fail_mh (struct TALER_EXCHANGE_MeltHandle *mh, + enum TALER_ErrorCode ec) +{ + struct TALER_EXCHANGE_MeltResponse mr = { + .hr.ec = ec + }; + + mh->melt_cb (mh->melt_cb_cls, + &mr); + TALER_EXCHANGE_melt_cancel (mh); +} + + +/** + * Callbacks of this type are used to serve the result of submitting a + * CS R request to a exchange. + * + * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` + * @param csrr response details + */ +static void +csr_cb (void *cls, + const struct TALER_EXCHANGE_CsRMeltResponse *csrr) +{ + struct TALER_EXCHANGE_MeltHandle *mh = cls; + unsigned int nks_off = 0; + + mh->csr = NULL; + if (MHD_HTTP_OK != csrr->hr.http_status) + { + struct TALER_EXCHANGE_MeltResponse mr = { + .hr = csrr->hr + }; + + mr.hr.hint = "/csr-melt failed"; + mh->melt_cb (mh->melt_cb_cls, + &mr); + TALER_EXCHANGE_melt_cancel (mh); + return; + } + for (unsigned int i = 0; ird->fresh_pks_len; i++) + { + const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = + &mh->rd->fresh_pks[i]; + struct TALER_ExchangeWithdrawValues *wv = &mh->mbds[i].alg_value; + + switch (fresh_pk->key.cipher) + { + case TALER_DENOMINATION_INVALID: + GNUNET_break (0); + fail_mh (mh, + TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); + return; + case TALER_DENOMINATION_RSA: + GNUNET_assert (TALER_DENOMINATION_RSA == wv->cipher); + break; + case TALER_DENOMINATION_CS: + GNUNET_assert (TALER_DENOMINATION_CS == wv->cipher); + *wv = csrr->details.ok.alg_values[nks_off]; + nks_off++; + break; + } + } + mh->send_rms = true; + if (GNUNET_OK != + start_melt (mh)) + { + GNUNET_break (0); + fail_mh (mh, + TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); + return; + } +} + + +struct TALER_EXCHANGE_MeltHandle * +TALER_EXCHANGE_melt ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *keys, + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_EXCHANGE_RefreshData *rd, + TALER_EXCHANGE_MeltCallback melt_cb, + void *melt_cb_cls) +{ + struct TALER_EXCHANGE_NonceKey nks[GNUNET_NZL (rd->fresh_pks_len)]; + unsigned int nks_off = 0; + struct TALER_EXCHANGE_MeltHandle *mh; + + if (0 == rd->fresh_pks_len) + { + GNUNET_break (0); + return NULL; + } + mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle); + mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */ + mh->cctx = ctx; + mh->exchange_url = GNUNET_strdup (url); + mh->rd = rd; + mh->rms = *rms; + mh->melt_cb = melt_cb; + mh->melt_cb_cls = melt_cb_cls; + mh->mbds = GNUNET_new_array (rd->fresh_pks_len, + struct TALER_EXCHANGE_MeltBlindingDetail); + for (unsigned int i = 0; ifresh_pks_len; i++) + { + const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = &rd->fresh_pks[i]; + struct TALER_ExchangeWithdrawValues *wv = &mh->mbds[i].alg_value; + + switch (fresh_pk->key.cipher) + { + case TALER_DENOMINATION_INVALID: + GNUNET_break (0); + GNUNET_free (mh->mbds); + GNUNET_free (mh); + return NULL; + case TALER_DENOMINATION_RSA: + wv->cipher = TALER_DENOMINATION_RSA; + break; + case TALER_DENOMINATION_CS: + wv->cipher = TALER_DENOMINATION_CS; + nks[nks_off].pk = fresh_pk; + nks[nks_off].cnc_num = nks_off; + nks_off++; + break; + } + } + mh->keys = TALER_EXCHANGE_keys_incref (keys); + if (0 != nks_off) + { + mh->csr = TALER_EXCHANGE_csr_melt (ctx, + url, + rms, + nks_off, + nks, + &csr_cb, + mh); + if (NULL == mh->csr) + { + GNUNET_break (0); + TALER_EXCHANGE_melt_cancel (mh); + return NULL; + } + return mh; + } + if (GNUNET_OK != + start_melt (mh)) + { + GNUNET_break (0); + TALER_EXCHANGE_melt_cancel (mh); + return NULL; + } + return mh; +} + + +void +TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh) +{ + if (NULL != mh->job) + { + GNUNET_CURL_job_cancel (mh->job); + mh->job = NULL; + } + if (NULL != mh->csr) + { + TALER_EXCHANGE_csr_melt_cancel (mh->csr); + mh->csr = NULL; + } + TALER_EXCHANGE_free_melt_data_ (&mh->md); /* does not free 'md' itself */ + GNUNET_free (mh->mbds); + GNUNET_free (mh->url); + GNUNET_free (mh->exchange_url); + TALER_curl_easy_post_finished (&mh->ctx); + TALER_EXCHANGE_keys_decref (mh->keys); + GNUNET_free (mh); +} + + +/* end of exchange_api_melt.c */ diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c new file mode 100644 index 000000000..cfd265f04 --- /dev/null +++ b/src/lib/exchange_api_recoup.c @@ -0,0 +1,369 @@ +/* + This file is part of TALER + Copyright (C) 2017-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_recoup.c + * @brief Implementation of the /recoup request of the exchange's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_common.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A Recoup Handle + */ +struct TALER_EXCHANGE_RecoupHandle +{ + + /** + * The keys of the exchange this request handle will use + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * The url for this request. + */ + char *url; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Denomination key of the coin. + */ + struct TALER_EXCHANGE_DenomPublicKey pk; + + /** + * Our signature requesting the recoup. + */ + struct TALER_CoinSpendSignatureP coin_sig; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_RecoupResultCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Public key of the coin we are trying to get paid back. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + +}; + + +/** + * Parse a recoup response. If it is valid, call the callback. + * + * @param ph recoup handle + * @param json json reply with the signature + * @return #GNUNET_OK if the signature is valid and we called the callback; + * #GNUNET_SYSERR if not (callback must still be called) + */ +static enum GNUNET_GenericReturnValue +process_recoup_response (const struct TALER_EXCHANGE_RecoupHandle *ph, + const json_t *json) +{ + struct TALER_EXCHANGE_RecoupResponse rr = { + .hr.reply = json, + .hr.http_status = MHD_HTTP_OK + }; + struct GNUNET_JSON_Specification spec_withdraw[] = { + GNUNET_JSON_spec_fixed_auto ("reserve_pub", + &rr.details.ok.reserve_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec_withdraw, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ph->cb (ph->cb_cls, + &rr); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /recoup request. + * + * @param cls the `struct TALER_EXCHANGE_RecoupHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_recoup_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_RecoupHandle *ph = cls; + const json_t *j = response; + struct TALER_EXCHANGE_RecoupResponse rr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + ph->job = NULL; + switch (response_code) + { + case 0: + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + process_recoup_response (ph, + j)) + { + GNUNET_break_op (0); + rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rr.hr.http_status = 0; + break; + } + TALER_EXCHANGE_recoup_cancel (ph); + return; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONFLICT: + { + struct TALER_Amount min_key; + + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + if (GNUNET_OK != + TALER_EXCHANGE_get_min_denomination_ (ph->keys, + &min_key)) + { + GNUNET_break (0); + rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rr.hr.http_status = 0; + break; + } + break; + } + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify, exchange says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_GONE: + /* Kind of normal: the money was already sent to the merchant + (it was too late for the refund). */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange recoup\n", + (unsigned int) response_code, + (int) rr.hr.ec); + GNUNET_break (0); + break; + } + ph->cb (ph->cb_cls, + &rr); + TALER_EXCHANGE_recoup_cancel (ph); +} + + +struct TALER_EXCHANGE_RecoupHandle * +TALER_EXCHANGE_recoup ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *keys, + const struct TALER_EXCHANGE_DenomPublicKey *pk, + const struct TALER_DenominationSignature *denom_sig, + const struct TALER_ExchangeWithdrawValues *exchange_vals, + const struct TALER_PlanchetMasterSecretP *ps, + TALER_EXCHANGE_RecoupResultCallback recoup_cb, + void *recoup_cb_cls) +{ + struct TALER_EXCHANGE_RecoupHandle *ph; + struct TALER_DenominationHashP h_denom_pub; + json_t *recoup_obj; + CURL *eh; + char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; + struct TALER_CoinSpendPrivateKeyP coin_priv; + union TALER_DenominationBlindingKeyP bks; + + ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle); + TALER_planchet_setup_coin_priv (ps, + exchange_vals, + &coin_priv); + TALER_planchet_blinding_secret_create (ps, + exchange_vals, + &bks); + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, + &ph->coin_pub.eddsa_pub); + TALER_denom_pub_hash (&pk->key, + &h_denom_pub); + TALER_wallet_recoup_sign (&h_denom_pub, + &bks, + &coin_priv, + &ph->coin_sig); + recoup_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("denom_pub_hash", + &h_denom_pub), + TALER_JSON_pack_denom_sig ("denom_sig", + denom_sig), + TALER_JSON_pack_exchange_withdraw_values ("ewv", + exchange_vals), + GNUNET_JSON_pack_data_auto ("coin_sig", + &ph->coin_sig), + GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", + &bks)); + if (TALER_DENOMINATION_CS == denom_sig->cipher) + { + struct TALER_CsNonce nonce; + + /* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash() + it is not strictly clear that the nonce is needed. Best case would be + to find a way to include it more 'naturally' somehow, for example with + the variant union version of bks! */ + TALER_cs_withdraw_nonce_derive (ps, + &nonce); + GNUNET_assert ( + 0 == + json_object_set_new (recoup_obj, + "cs_nonce", + GNUNET_JSON_from_data_auto ( + &nonce))); + } + + { + char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &ph->coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "coins/%s/recoup", + pub_str); + } + + ph->pk = *pk; + memset (&ph->pk.key, + 0, + sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */ + ph->cb = recoup_cb; + ph->cb_cls = recoup_cb_cls; + ph->url = TALER_url_join (url, + arg_str, + NULL); + if (NULL == ph->url) + { + json_decref (recoup_obj); + GNUNET_free (ph); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ph->ctx, + eh, + recoup_obj)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (recoup_obj); + GNUNET_free (ph->url); + GNUNET_free (ph); + return NULL; + } + json_decref (recoup_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "URL for recoup: `%s'\n", + ph->url); + ph->keys = TALER_EXCHANGE_keys_incref (keys); + ph->job = GNUNET_CURL_job_add2 (ctx, + eh, + ph->ctx.headers, + &handle_recoup_finished, + ph); + return ph; +} + + +void +TALER_EXCHANGE_recoup_cancel (struct TALER_EXCHANGE_RecoupHandle *ph) +{ + if (NULL != ph->job) + { + GNUNET_CURL_job_cancel (ph->job); + ph->job = NULL; + } + GNUNET_free (ph->url); + TALER_curl_easy_post_finished (&ph->ctx); + TALER_EXCHANGE_keys_decref (ph->keys); + GNUNET_free (ph); +} + + +/* end of exchange_api_recoup.c */ diff --git a/src/lib/exchange_api_recoup_refresh.c b/src/lib/exchange_api_recoup_refresh.c new file mode 100644 index 000000000..0bcd44dec --- /dev/null +++ b/src/lib/exchange_api_recoup_refresh.c @@ -0,0 +1,363 @@ +/* + This file is part of TALER + Copyright (C) 2017-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_recoup_refresh.c + * @brief Implementation of the /recoup-refresh request of the exchange's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_common.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A Recoup Handle + */ +struct TALER_EXCHANGE_RecoupRefreshHandle +{ + + /** + * The keys of the exchange this request handle will use + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * The url for this request. + */ + char *url; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Denomination key of the coin. + */ + struct TALER_EXCHANGE_DenomPublicKey pk; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_RecoupRefreshResultCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Public key of the coin we are trying to get paid back. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Signature affirming the recoup-refresh operation. + */ + struct TALER_CoinSpendSignatureP coin_sig; + +}; + + +/** + * Parse a recoup-refresh response. If it is valid, call the callback. + * + * @param ph recoup handle + * @param json json reply with the signature + * @return #GNUNET_OK if the signature is valid and we called the callback; + * #GNUNET_SYSERR if not (callback must still be called) + */ +static enum GNUNET_GenericReturnValue +process_recoup_response ( + const struct TALER_EXCHANGE_RecoupRefreshHandle *ph, + const json_t *json) +{ + struct TALER_EXCHANGE_RecoupRefreshResponse rrr = { + .hr.reply = json, + .hr.http_status = MHD_HTTP_OK + }; + struct GNUNET_JSON_Specification spec_refresh[] = { + GNUNET_JSON_spec_fixed_auto ("old_coin_pub", + &rrr.details.ok.old_coin_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec_refresh, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ph->cb (ph->cb_cls, + &rrr); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /recoup-refresh request. + * + * @param cls the `struct TALER_EXCHANGE_RecoupRefreshHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_recoup_refresh_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_RecoupRefreshHandle *ph = cls; + const json_t *j = response; + struct TALER_EXCHANGE_RecoupRefreshResponse rrr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + ph->job = NULL; + switch (response_code) + { + case 0: + rrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + process_recoup_response (ph, + j)) + { + GNUNET_break_op (0); + rrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rrr.hr.http_status = 0; + break; + } + TALER_EXCHANGE_recoup_refresh_cancel (ph); + return; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify, exchange says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONFLICT: + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_GONE: + /* Kind of normal: the money was already sent to the merchant + (it was too late for the refund). */ + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + rrr.hr.ec = TALER_JSON_get_error_code (j); + rrr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange recoup\n", + (unsigned int) response_code, + (int) rrr.hr.ec); + GNUNET_break (0); + break; + } + ph->cb (ph->cb_cls, + &rrr); + TALER_EXCHANGE_recoup_refresh_cancel (ph); +} + + +struct TALER_EXCHANGE_RecoupRefreshHandle * +TALER_EXCHANGE_recoup_refresh ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *keys, + const struct TALER_EXCHANGE_DenomPublicKey *pk, + const struct TALER_DenominationSignature *denom_sig, + const struct TALER_ExchangeWithdrawValues *exchange_vals, + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_PlanchetMasterSecretP *ps, + unsigned int idx, + TALER_EXCHANGE_RecoupRefreshResultCallback recoup_cb, + void *recoup_cb_cls) +{ + struct TALER_EXCHANGE_RecoupRefreshHandle *ph; + struct TALER_DenominationHashP h_denom_pub; + json_t *recoup_obj; + CURL *eh; + char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; + struct TALER_CoinSpendPrivateKeyP coin_priv; + union TALER_DenominationBlindingKeyP bks; + + GNUNET_assert (NULL != recoup_cb); + ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle); + ph->pk = *pk; + memset (&ph->pk.key, + 0, + sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */ + ph->cb = recoup_cb; + ph->cb_cls = recoup_cb_cls; + TALER_planchet_setup_coin_priv (ps, + exchange_vals, + &coin_priv); + TALER_planchet_blinding_secret_create (ps, + exchange_vals, + &bks); + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, + &ph->coin_pub.eddsa_pub); + TALER_denom_pub_hash (&pk->key, + &h_denom_pub); + TALER_wallet_recoup_refresh_sign (&h_denom_pub, + &bks, + &coin_priv, + &ph->coin_sig); + recoup_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("denom_pub_hash", + &h_denom_pub), + TALER_JSON_pack_denom_sig ("denom_sig", + denom_sig), + TALER_JSON_pack_exchange_withdraw_values ("ewv", + exchange_vals), + GNUNET_JSON_pack_data_auto ("coin_sig", + &ph->coin_sig), + GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", + &bks)); + + if (TALER_DENOMINATION_CS == denom_sig->cipher) + { + struct TALER_CsNonce nonce; + + /* NOTE: this is not elegant, and as per the note in TALER_coin_ev_hash() + it is not strictly clear that the nonce is needed. Best case would be + to find a way to include it more 'naturally' somehow, for example with + the variant union version of bks! */ + TALER_cs_refresh_nonce_derive (rms, + idx, + &nonce); + GNUNET_assert ( + 0 == + json_object_set_new (recoup_obj, + "cs_nonce", + GNUNET_JSON_from_data_auto ( + &nonce))); + } + + { + char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &ph->coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "coins/%s/recoup-refresh", + pub_str); + } + + ph->url = TALER_url_join (url, + arg_str, + NULL); + if (NULL == ph->url) + { + json_decref (recoup_obj); + GNUNET_free (ph); + return NULL; + } + eh = TALER_EXCHANGE_curl_easy_get_ (ph->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&ph->ctx, + eh, + recoup_obj)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (recoup_obj); + GNUNET_free (ph->url); + GNUNET_free (ph); + return NULL; + } + json_decref (recoup_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "URL for recoup-refresh: `%s'\n", + ph->url); + ph->keys = TALER_EXCHANGE_keys_incref (keys); + ph->job = GNUNET_CURL_job_add2 (ctx, + eh, + ph->ctx.headers, + &handle_recoup_refresh_finished, + ph); + return ph; +} + + +void +TALER_EXCHANGE_recoup_refresh_cancel ( + struct TALER_EXCHANGE_RecoupRefreshHandle *ph) +{ + if (NULL != ph->job) + { + GNUNET_CURL_job_cancel (ph->job); + ph->job = NULL; + } + GNUNET_free (ph->url); + TALER_curl_easy_post_finished (&ph->ctx); + TALER_EXCHANGE_keys_decref (ph->keys); + GNUNET_free (ph); +} + + +/* end of exchange_api_recoup_refresh.c */ diff --git a/src/lib/exchange_api_refresh_common.c b/src/lib/exchange_api_refresh_common.c new file mode 100644 index 000000000..0a6665b55 --- /dev/null +++ b/src/lib/exchange_api_refresh_common.c @@ -0,0 +1,249 @@ +/* + This file is part of TALER + Copyright (C) 2015-2022 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_refresh_common.c + * @brief Serialization logic shared between melt and reveal steps during refreshing + * @author Christian Grothoff + */ +#include "platform.h" +#include "exchange_api_refresh_common.h" + + +void +TALER_EXCHANGE_free_melt_data_ (struct MeltData *md) +{ + for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) + { + struct TALER_RefreshCoinData *rcds = md->rcd[i]; + + if (NULL == rcds) + continue; + for (unsigned int j = 0; j < md->num_fresh_coins; j++) + TALER_blinded_planchet_free (&rcds[j].blinded_planchet); + GNUNET_free (rcds); + } + TALER_denom_pub_free (&md->melted_coin.pub_key); + TALER_denom_sig_free (&md->melted_coin.sig); + if (NULL != md->fcds) + { + for (unsigned int j = 0; jnum_fresh_coins; j++) + { + struct FreshCoinData *fcd = &md->fcds[j]; + + TALER_denom_pub_free (&fcd->fresh_pk); + for (size_t i = 0; i < TALER_CNC_KAPPA; i++) + { + TALER_age_commitment_proof_free (fcd->age_commitment_proofs[i]); + GNUNET_free (fcd->age_commitment_proofs[i]); + } + } + GNUNET_free (md->fcds); + } + /* Finally, clean up a bit... */ + GNUNET_CRYPTO_zero_keys (md, + sizeof (struct MeltData)); +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_melt_data_ ( + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_EXCHANGE_RefreshData *rd, + const struct TALER_ExchangeWithdrawValues *alg_values, + struct MeltData *md) +{ + struct TALER_Amount total; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_CsNonce nonces[rd->fresh_pks_len]; + bool uses_cs = false; + + GNUNET_CRYPTO_eddsa_key_get_public (&rd->melt_priv.eddsa_priv, + &coin_pub.eddsa_pub); + /* build up melt data structure */ + memset (md, + 0, + sizeof (*md)); + md->num_fresh_coins = rd->fresh_pks_len; + md->melted_coin.coin_priv = rd->melt_priv; + md->melted_coin.melt_amount_with_fee = rd->melt_amount; + md->melted_coin.fee_melt = rd->melt_pk.fees.refresh; + md->melted_coin.original_value = rd->melt_pk.value; + md->melted_coin.expire_deposit = rd->melt_pk.expire_deposit; + md->melted_coin.age_commitment_proof = rd->melt_age_commitment_proof; + md->melted_coin.h_age_commitment = rd->melt_h_age_commitment; + + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (rd->melt_amount.currency, + &total)); + TALER_denom_pub_deep_copy (&md->melted_coin.pub_key, + &rd->melt_pk.key); + TALER_denom_sig_deep_copy (&md->melted_coin.sig, + &rd->melt_sig); + md->fcds = GNUNET_new_array (md->num_fresh_coins, + struct FreshCoinData); + for (unsigned int j = 0; jfresh_pks_len; j++) + { + struct FreshCoinData *fcd = &md->fcds[j]; + + if (alg_values[j].cipher != rd->fresh_pks[j].key.cipher) + { + GNUNET_break (0); + TALER_EXCHANGE_free_melt_data_ (md); + return GNUNET_SYSERR; + } + if (TALER_DENOMINATION_CS == alg_values[j].cipher) + { + uses_cs = true; + TALER_cs_refresh_nonce_derive ( + rms, + j, + &nonces[j]); + } + TALER_denom_pub_deep_copy (&fcd->fresh_pk, + &rd->fresh_pks[j].key); + if ( (0 > + TALER_amount_add (&total, + &total, + &rd->fresh_pks[j].value)) || + (0 > + TALER_amount_add (&total, + &total, + &rd->fresh_pks[j].fees.withdraw)) ) + { + GNUNET_break (0); + TALER_EXCHANGE_free_melt_data_ (md); + return GNUNET_SYSERR; + } + } + + /* verify that melt_amount is above total cost */ + if (1 == + TALER_amount_cmp (&total, + &rd->melt_amount) ) + { + /* Eh, this operation is more expensive than the + @a melt_amount. This is not OK. */ + GNUNET_break (0); + TALER_EXCHANGE_free_melt_data_ (md); + return GNUNET_SYSERR; + } + + /* build up coins */ + for (unsigned int i = 0; imelt_priv, + i, + &md->transfer_priv[i]); + + GNUNET_CRYPTO_ecdhe_key_get_public ( + &md->transfer_priv[i].ecdhe_priv, + &md->transfer_pub[i].ecdhe_pub); + + TALER_link_derive_transfer_secret (&rd->melt_priv, + &md->transfer_priv[i], + &trans_sec); + + md->rcd[i] = GNUNET_new_array (rd->fresh_pks_len, + struct TALER_RefreshCoinData); + + for (unsigned int j = 0; jfresh_pks_len; j++) + { + struct FreshCoinData *fcd = &md->fcds[j]; + struct TALER_CoinSpendPrivateKeyP *coin_priv = &fcd->coin_priv; + struct TALER_PlanchetMasterSecretP *ps = &fcd->ps[i]; + struct TALER_RefreshCoinData *rcd = &md->rcd[i][j]; + union TALER_DenominationBlindingKeyP *bks = &fcd->bks[i]; + struct TALER_PlanchetDetail pd; + struct TALER_CoinPubHashP c_hash; + struct TALER_AgeCommitmentHash ach; + struct TALER_AgeCommitmentHash *pah = NULL; + + TALER_transfer_secret_to_planchet_secret (&trans_sec, + j, + ps); + + TALER_planchet_setup_coin_priv (ps, + &alg_values[j], + coin_priv); + + TALER_planchet_blinding_secret_create (ps, + &alg_values[j], + bks); + + if (NULL != rd->melt_age_commitment_proof) + { + fcd->age_commitment_proofs[i] = GNUNET_new (struct + TALER_AgeCommitmentProof); + + GNUNET_assert (GNUNET_OK == + TALER_age_commitment_derive ( + md->melted_coin.age_commitment_proof, + &trans_sec.key, + fcd->age_commitment_proofs[i])); + + TALER_age_commitment_hash ( + &fcd->age_commitment_proofs[i]->commitment, + &ach); + pah = &ach; + } + + if (TALER_DENOMINATION_CS == alg_values[j].cipher) + pd.blinded_planchet.details.cs_blinded_planchet.nonce = nonces[j]; + + if (GNUNET_OK != + TALER_planchet_prepare (&fcd->fresh_pk, + &alg_values[j], + bks, + coin_priv, + pah, + &c_hash, + &pd)) + { + GNUNET_break_op (0); + TALER_EXCHANGE_free_melt_data_ (md); + return GNUNET_SYSERR; + } + rcd->blinded_planchet = pd.blinded_planchet; + rcd->dk = &fcd->fresh_pk; + } + } + + /* Finally, compute refresh commitment */ + { + struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; + + for (unsigned int i = 0; itransfer_pub[i]; + rce[i].new_coins = md->rcd[i]; + } + TALER_refresh_get_commitment (&md->rc, + TALER_CNC_KAPPA, + uses_cs + ? rms + : NULL, + rd->fresh_pks_len, + rce, + &coin_pub, + &rd->melt_amount); + } + return GNUNET_OK; +} diff --git a/src/lib/exchange_api_refresh_common.h b/src/lib/exchange_api_refresh_common.h new file mode 100644 index 000000000..0cb80f17e --- /dev/null +++ b/src/lib/exchange_api_refresh_common.h @@ -0,0 +1,201 @@ +/* + This file is part of TALER + Copyright (C) 2015-2022 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_refresh_common.h + * @brief shared (serialization) logic for refresh protocol + * @author Christian Grothoff + */ +#ifndef REFRESH_COMMON_H +#define REFRESH_COMMON_H +#include +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "taler_signatures.h" + + +/** + * Information about a coin we are melting. + */ +struct MeltedCoin +{ + /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** + * Amount this coin contributes to the melt, including fee. + */ + struct TALER_Amount melt_amount_with_fee; + + /** + * The applicable fee for melting a coin of this denomination + */ + struct TALER_Amount fee_melt; + + /** + * The original value of the coin. + */ + struct TALER_Amount original_value; + + /** + * The original age commitment, its proof and its hash. MUST be NULL if no + * age commitment was set. + */ + const struct TALER_AgeCommitmentProof *age_commitment_proof; + const struct TALER_AgeCommitmentHash *h_age_commitment; + + /** + * Timestamp indicating when coins of this denomination become invalid. + */ + struct GNUNET_TIME_Timestamp expire_deposit; + + /** + * Denomination key of the original coin. + */ + struct TALER_DenominationPublicKey pub_key; + + /** + * Exchange's signature over the coin. + */ + struct TALER_DenominationSignature sig; + +}; + + +/** + * Data we keep for each fresh coin created in the + * melt process. + */ +struct FreshCoinData +{ + /** + * Denomination public key of the coin. + */ + struct TALER_DenominationPublicKey fresh_pk; + + /** + * Array of planchet secrets for the coins, depending + * on the cut-and-choose. + */ + struct TALER_PlanchetMasterSecretP ps[TALER_CNC_KAPPA]; + + /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** + * Arrays of age commitments and proofs to be created, one for each + * cut-and-choose dimension. NULL if age restriction is not applicable. + */ + struct TALER_AgeCommitmentProof *age_commitment_proofs[TALER_CNC_KAPPA]; + + /** + * Blinding key secrets for the coins, depending on the + * cut-and-choose. + */ + union TALER_DenominationBlindingKeyP bks[TALER_CNC_KAPPA]; + +}; + + +/** + * Melt data in non-serialized format for convenient processing. + */ +struct MeltData +{ + + /** + * Hash over the committed data during refresh operation. + */ + struct TALER_RefreshCommitmentP rc; + + /** + * Information about the melted coin. + */ + struct MeltedCoin melted_coin; + + /** + * Array of length @e num_fresh_coins with information + * about each fresh coin. + */ + struct FreshCoinData *fcds; + + /** + * Transfer secrets, one per cut and choose. + */ + struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA]; + + /** + * Transfer private keys for each cut-and-choose dimension. + */ + struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA]; + + /** + * Transfer public key of this commitment. + */ + struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA]; + + /** + * Transfer secrets, one per cut and choose. + */ + struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; + + /** + * Blinded planchets and denominations of the fresh coins, depending on the cut-and-choose. Array of length + * @e num_fresh_coins. + */ + struct TALER_RefreshCoinData *rcd[TALER_CNC_KAPPA]; + + /** + * Number of coins we are creating + */ + uint16_t num_fresh_coins; + +}; + + +/** + * Compute the melt data from the refresh data and secret. + * + * @param rms secret internals of the refresh-reveal operation + * @param rd refresh data with the characteristics of the operation + * @param alg_values contributions from the exchange into the melt + * @param[out] md where to write the derived melt data + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_melt_data_ ( + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_EXCHANGE_RefreshData *rd, + const struct TALER_ExchangeWithdrawValues *alg_values, + struct MeltData *md); + + +/** + * Free all information associated with a melting session. Note + * that we allow the melting session to be only partially initialized, + * as we use this function also when freeing melt data that was not + * fully initialized. + * + * @param[in] md melting data to release, the pointer itself is NOT + * freed (as it is typically not allocated by itself) + */ +void +TALER_EXCHANGE_free_melt_data_ (struct MeltData *md); + +#endif diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c new file mode 100644 index 000000000..220682992 --- /dev/null +++ b/src/lib/exchange_api_refreshes_reveal.c @@ -0,0 +1,531 @@ +/* + This file is part of TALER + Copyright (C) 2015-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_refreshes_reveal.c + * @brief Implementation of the /refreshes/$RCH/reveal requests + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" +#include "exchange_api_refresh_common.h" + + +/** + * @brief A /refreshes/$RCH/reveal Handle + */ +struct TALER_EXCHANGE_RefreshesRevealHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Exchange-contributed values to the operation. + */ + struct TALER_ExchangeWithdrawValues *alg_values; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_RefreshesRevealCallback reveal_cb; + + /** + * Closure for @e reveal_cb. + */ + void *reveal_cb_cls; + + /** + * Actual information about the melt operation. + */ + struct MeltData md; + + /** + * The index selected by the exchange in cut-and-choose to not be revealed. + */ + uint16_t noreveal_index; + +}; + + +/** + * We got a 200 OK response for the /refreshes/$RCH/reveal operation. Extract + * the coin signatures and return them to the caller. The signatures we get + * from the exchange is for the blinded value. Thus, we first must unblind + * them and then should verify their validity. + * + * If everything checks out, we return the unblinded signatures + * to the application via the callback. + * + * @param rrh operation handle + * @param json reply from the exchange + * @param[out] rcis array of length `num_fresh_coins`, initialized to contain the coin data + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static enum GNUNET_GenericReturnValue +refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, + const json_t *json, + struct TALER_EXCHANGE_RevealedCoinInfo *rcis) +{ + const json_t *jsona; + struct GNUNET_JSON_Specification outer_spec[] = { + GNUNET_JSON_spec_array_const ("ev_sigs", + &jsona), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + outer_spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (rrh->md.num_fresh_coins != json_array_size (jsona)) + { + /* Number of coins generated does not match our expectation */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; imd.num_fresh_coins; i++) + { + struct TALER_EXCHANGE_RevealedCoinInfo *rci = &rcis[i]; + const struct FreshCoinData *fcd = &rrh->md.fcds[i]; + const struct TALER_DenominationPublicKey *pk; + json_t *jsonai; + struct TALER_BlindedDenominationSignature blind_sig; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_CoinPubHashP coin_hash; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_blinded_denom_sig ("ev_sig", + &blind_sig), + GNUNET_JSON_spec_end () + }; + struct TALER_FreshCoin coin; + union TALER_DenominationBlindingKeyP bks; + const struct TALER_AgeCommitmentHash *pah = NULL; + + rci->ps = fcd->ps[rrh->noreveal_index]; + rci->bks = fcd->bks[rrh->noreveal_index]; + rci->age_commitment_proof = NULL; + + pk = &fcd->fresh_pk; + jsonai = json_array_get (jsona, i); + + GNUNET_assert (NULL != jsonai); + + if (NULL != rrh->md.melted_coin.age_commitment_proof) + { + rci->age_commitment_proof = + fcd->age_commitment_proofs[rrh->noreveal_index]; + + TALER_age_commitment_hash (&rci->age_commitment_proof->commitment, + &rci->h_age_commitment); + pah = &rci->h_age_commitment; + } + + if (GNUNET_OK != + GNUNET_JSON_parse (jsonai, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + TALER_planchet_setup_coin_priv (&rci->ps, + &rrh->alg_values[i], + &rci->coin_priv); + TALER_planchet_blinding_secret_create (&rci->ps, + &rrh->alg_values[i], + &bks); + /* needed to verify the signature, and we didn't store it earlier, + hence recomputing it here... */ + GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); + TALER_coin_pub_hash ( + &coin_pub, + pah, + &coin_hash); + if (GNUNET_OK != + TALER_planchet_to_coin ( + pk, + &blind_sig, + &bks, + &rci->coin_priv, + pah, + &coin_hash, + &rrh->alg_values[i], + &coin)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + GNUNET_JSON_parse_free (spec); + rci->sig = coin.sig; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /refreshes/$RCH/reveal request. + * + * @param cls the `struct TALER_EXCHANGE_RefreshHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_refresh_reveal_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_RevealResult rr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + rrh->job = NULL; + switch (response_code) + { + case 0: + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + struct TALER_EXCHANGE_RevealedCoinInfo rcis[rrh->md.num_fresh_coins]; + enum GNUNET_GenericReturnValue ret; + + memset (rcis, + 0, + sizeof (rcis)); + ret = refresh_reveal_ok (rrh, + j, + rcis); + if (GNUNET_OK != ret) + { + rr.hr.http_status = 0; + rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + break; + } + else + { + GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA); + rr.details.ok.num_coins = rrh->md.num_fresh_coins; + rr.details.ok.coins = rcis; + rrh->reveal_cb (rrh->reveal_cb_cls, + &rr); + rrh->reveal_cb = NULL; + } + for (unsigned int i = 0; imd.num_fresh_coins; i++) + { + TALER_denom_sig_free (&rcis[i].sig); + TALER_age_commitment_proof_free (rcis[i].age_commitment_proof); + } + TALER_EXCHANGE_refreshes_reveal_cancel (rrh); + return; + } + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONFLICT: + /* Nothing really to verify, exchange says our reveal is inconsistent + with our commitment, so either side is buggy; we + should pass the JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_GONE: + /* Server claims key expired or has been revoked */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange refreshes reveal\n", + (unsigned int) response_code, + (int) rr.hr.ec); + break; + } + if (NULL != rrh->reveal_cb) + rrh->reveal_cb (rrh->reveal_cb_cls, + &rr); + TALER_EXCHANGE_refreshes_reveal_cancel (rrh); +} + + +struct TALER_EXCHANGE_RefreshesRevealHandle * +TALER_EXCHANGE_refreshes_reveal ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_EXCHANGE_RefreshData *rd, + unsigned int num_coins, + const struct TALER_ExchangeWithdrawValues alg_values[static num_coins], + uint32_t noreveal_index, + TALER_EXCHANGE_RefreshesRevealCallback reveal_cb, + void *reveal_cb_cls) +{ + struct TALER_EXCHANGE_RefreshesRevealHandle *rrh; + json_t *transfer_privs; + json_t *new_denoms_h; + json_t *coin_evs; + json_t *reveal_obj; + json_t *link_sigs; + json_t *old_age_commitment = NULL; + CURL *eh; + struct MeltData md; + char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32]; + bool send_rms = false; + + GNUNET_assert (num_coins == rd->fresh_pks_len); + if (noreveal_index >= TALER_CNC_KAPPA) + { + /* We check this here, as it would be really bad to below just + disclose all the transfer keys. Note that this error should + have been caught way earlier when the exchange replied, but maybe + we had some internal corruption that changed the value... */ + GNUNET_break (0); + return NULL; + } + if (GNUNET_OK != + TALER_EXCHANGE_get_melt_data_ (rms, + rd, + alg_values, + &md)) + { + GNUNET_break (0); + return NULL; + } + + /* now new_denoms */ + GNUNET_assert (NULL != (new_denoms_h = json_array ())); + GNUNET_assert (NULL != (coin_evs = json_array ())); + GNUNET_assert (NULL != (link_sigs = json_array ())); + for (unsigned int i = 0; iblinded_planchet)))); + { + struct TALER_CoinSpendSignatureP link_sig; + struct TALER_BlindedCoinHashP bch; + + TALER_coin_ev_hash (&rcd->blinded_planchet, + &denom_hash, + &bch); + TALER_wallet_link_sign ( + &denom_hash, + &md.transfer_pub[noreveal_index], + &bch, + &md.melted_coin.coin_priv, + &link_sig); + GNUNET_assert (0 == + json_array_append_new ( + link_sigs, + GNUNET_JSON_from_data_auto (&link_sig))); + } + } + + /* build array of transfer private keys */ + GNUNET_assert (NULL != (transfer_privs = json_array ())); + for (unsigned int j = 0; jmelt_age_commitment_proof) + { + GNUNET_assert (NULL != rd->melt_h_age_commitment); + GNUNET_assert (NULL != (old_age_commitment = json_array ())); + + for (size_t i = 0; i < rd->melt_age_commitment_proof->commitment.num; i++) + { + enum GNUNET_GenericReturnValue ret; + ret = json_array_append_new ( + old_age_commitment, + GNUNET_JSON_from_data_auto ( + &rd->melt_age_commitment_proof->commitment.keys[i])); + GNUNET_assert (0 == ret); + } + } + + /* build main JSON request */ + reveal_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("transfer_pub", + &md.transfer_pub[noreveal_index]), + GNUNET_JSON_pack_allow_null ( + send_rms + ? GNUNET_JSON_pack_data_auto ("rms", + rms) + : GNUNET_JSON_pack_string ("rms", + NULL)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("old_age_commitment", + old_age_commitment)), + GNUNET_JSON_pack_array_steal ("transfer_privs", + transfer_privs), + GNUNET_JSON_pack_array_steal ("link_sigs", + link_sigs), + GNUNET_JSON_pack_array_steal ("new_denoms_h", + new_denoms_h), + GNUNET_JSON_pack_array_steal ("coin_evs", + coin_evs)); + { + char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string (&md.rc, + sizeof (md.rc), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "refreshes/%s/reveal", + pub_str); + } + /* finally, we can actually issue the request */ + rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle); + rrh->noreveal_index = noreveal_index; + rrh->reveal_cb = reveal_cb; + rrh->reveal_cb_cls = reveal_cb_cls; + rrh->md = md; + rrh->alg_values + = GNUNET_memdup (alg_values, + md.num_fresh_coins + * sizeof (struct TALER_ExchangeWithdrawValues)); + rrh->url = TALER_url_join (url, + arg_str, + NULL); + if (NULL == rrh->url) + { + json_decref (reveal_obj); + TALER_EXCHANGE_free_melt_data_ (&md); + GNUNET_free (rrh->alg_values); + GNUNET_free (rrh); + return NULL; + } + + eh = TALER_EXCHANGE_curl_easy_get_ (rrh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&rrh->ctx, + eh, + reveal_obj)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (reveal_obj); + TALER_EXCHANGE_free_melt_data_ (&md); + GNUNET_free (rrh->alg_values); + GNUNET_free (rrh->url); + GNUNET_free (rrh); + return NULL; + } + json_decref (reveal_obj); + rrh->job = GNUNET_CURL_job_add2 (ctx, + eh, + rrh->ctx.headers, + &handle_refresh_reveal_finished, + rrh); + return rrh; +} + + +void +TALER_EXCHANGE_refreshes_reveal_cancel ( + struct TALER_EXCHANGE_RefreshesRevealHandle *rrh) +{ + if (NULL != rrh->job) + { + GNUNET_CURL_job_cancel (rrh->job); + rrh->job = NULL; + } + GNUNET_free (rrh->alg_values); + GNUNET_free (rrh->url); + TALER_curl_easy_post_finished (&rrh->ctx); + TALER_EXCHANGE_free_melt_data_ (&rrh->md); + GNUNET_free (rrh); +} + + +/* exchange_api_refreshes_reveal.c */ diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c new file mode 100644 index 000000000..7401bfe4f --- /dev/null +++ b/src/lib/exchange_api_refund.c @@ -0,0 +1,480 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_refund.c + * @brief Implementation of the /refund request of the exchange's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include /* just for HTTP status codes */ +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_exchange_service.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * @brief A Refund Handle + */ +struct TALER_EXCHANGE_RefundHandle +{ + + /** + * The keys of the exchange this request handle will use + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * The url for this request. + */ + char *url; + + /** + * Context for #TEH_curl_easy_post(). Keeps the data that must + * persist for Curl to make the upload. + */ + struct TALER_CURL_PostContext ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_RefundCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Hash over the proposal data to identify the contract + * which is being refunded. + */ + struct TALER_PrivateContractHashP h_contract_terms; + + /** + * The coin's public key. This is the value that must have been + * signed (blindly) by the Exchange. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * The Merchant's public key. Allows the merchant to later refund + * the transaction or to inquire about the wire transfer identifier. + */ + struct TALER_MerchantPublicKeyP merchant; + + /** + * Merchant-generated transaction ID for the refund. + */ + uint64_t rtransaction_id; + + /** + * Amount to be refunded, including refund fee charged by the + * exchange to the customer. + */ + struct TALER_Amount refund_amount; + +}; + + +/** + * Verify that the signature on the "200 OK" response + * from the exchange is valid. + * + * @param[in,out] rh refund handle (refund fee added) + * @param json json reply with the signature + * @param[out] exchange_pub set to the exchange's public key + * @param[out] exchange_sig set to the exchange's signature + * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not + */ +static enum GNUNET_GenericReturnValue +verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh, + const json_t *json, + struct TALER_ExchangePublicKeyP *exchange_pub, + struct TALER_ExchangeSignatureP *exchange_sig) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("exchange_sig", + exchange_sig), + GNUNET_JSON_spec_fixed_auto ("exchange_pub", + exchange_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_EXCHANGE_test_signing_key (rh->keys, + exchange_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_exchange_online_refund_confirmation_verify ( + &rh->h_contract_terms, + &rh->coin_pub, + &rh->merchant, + rh->rtransaction_id, + &rh->refund_amount, + exchange_pub, + exchange_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Verify that the information on the "412 Dependency Failed" response + * from the exchange is valid and indeed shows that there is a refund + * transaction ID reuse going on. + * + * @param[in,out] rh refund handle (refund fee added) + * @param json json reply with the signature + * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not + */ +static enum GNUNET_GenericReturnValue +verify_failed_dependency_ok (struct TALER_EXCHANGE_RefundHandle *rh, + const json_t *json) +{ + const json_t *h; + json_t *e; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("history", + &h), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (1 != json_array_size (h)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + e = json_array_get (h, 0); + { + struct TALER_Amount amount; + const char *type; + struct TALER_MerchantSignatureP sig; + struct TALER_Amount refund_fee; + struct TALER_PrivateContractHashP h_contract_terms; + uint64_t rtransaction_id; + struct TALER_MerchantPublicKeyP merchant_pub; + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount_any ("amount", + &amount), + GNUNET_JSON_spec_string ("type", + &type), + TALER_JSON_spec_amount_any ("refund_fee", + &refund_fee), + GNUNET_JSON_spec_fixed_auto ("merchant_sig", + &sig), + GNUNET_JSON_spec_fixed_auto ("h_contract_terms", + &h_contract_terms), + GNUNET_JSON_spec_fixed_auto ("merchant_pub", + &merchant_pub), + GNUNET_JSON_spec_uint64 ("rtransaction_id", + &rtransaction_id), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (e, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_merchant_refund_verify (&rh->coin_pub, + &h_contract_terms, + rtransaction_id, + &amount, + &merchant_pub, + &sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (rtransaction_id != rh->rtransaction_id) || + (0 != GNUNET_memcmp (&rh->h_contract_terms, + &h_contract_terms)) || + (0 != GNUNET_memcmp (&rh->merchant, + &merchant_pub)) || + (0 == TALER_amount_cmp (&rh->refund_amount, + &amount)) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /refund request. + * + * @param cls the `struct TALER_EXCHANGE_RefundHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_refund_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_RefundHandle *rh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_RefundResponse rr = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + rh->job = NULL; + switch (response_code) + { + case 0: + rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + verify_refund_signature_ok (rh, + j, + &rr.details.ok.exchange_pub, + &rr.details.ok.exchange_sig)) + { + GNUNET_break_op (0); + rr.hr.http_status = 0; + rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); also can happen if the currency + differs (which we should obviously never support). + Just pass JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_FORBIDDEN: + /* Nothing really to verify, exchange says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONFLICT: + /* Requested total refunds exceed deposited amount */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_GONE: + /* Kind of normal: the money was already sent to the merchant + (it was too late for the refund). */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_PRECONDITION_FAILED: + if (GNUNET_OK != + verify_failed_dependency_ok (rh, + j)) + { + GNUNET_break (0); + rr.hr.http_status = 0; + rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE; + rr.hr.hint = "failed precondition proof returned by exchange is invalid"; + break; + } + /* Two different refund requests were made about the same deposit, but + carrying identical refund transaction ids. */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for exchange refund\n", + (unsigned int) response_code, + rr.hr.ec); + break; + } + rh->cb (rh->cb_cls, + &rr); + TALER_EXCHANGE_refund_cancel (rh); +} + + +struct TALER_EXCHANGE_RefundHandle * +TALER_EXCHANGE_refund ( + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *keys, + const struct TALER_Amount *amount, + const struct TALER_PrivateContractHashP *h_contract_terms, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + uint64_t rtransaction_id, + const struct TALER_MerchantPrivateKeyP *merchant_priv, + TALER_EXCHANGE_RefundCallback cb, + void *cb_cls) +{ + struct TALER_MerchantPublicKeyP merchant_pub; + struct TALER_MerchantSignatureP merchant_sig; + struct TALER_EXCHANGE_RefundHandle *rh; + json_t *refund_obj; + CURL *eh; + char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; + + GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, + &merchant_pub.eddsa_pub); + TALER_merchant_refund_sign (coin_pub, + h_contract_terms, + rtransaction_id, + amount, + merchant_priv, + &merchant_sig); + { + char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "coins/%s/refund", + pub_str); + } + refund_obj = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("refund_amount", + amount), + GNUNET_JSON_pack_data_auto ("h_contract_terms", + h_contract_terms), + GNUNET_JSON_pack_uint64 ("rtransaction_id", + rtransaction_id), + GNUNET_JSON_pack_data_auto ("merchant_pub", + &merchant_pub), + GNUNET_JSON_pack_data_auto ("merchant_sig", + &merchant_sig)); + rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle); + rh->cb = cb; + rh->cb_cls = cb_cls; + rh->url = TALER_url_join (url, + arg_str, + NULL); + if (NULL == rh->url) + { + json_decref (refund_obj); + GNUNET_free (rh); + return NULL; + } + rh->h_contract_terms = *h_contract_terms; + rh->coin_pub = *coin_pub; + rh->merchant = merchant_pub; + rh->rtransaction_id = rtransaction_id; + rh->refund_amount = *amount; + eh = TALER_EXCHANGE_curl_easy_get_ (rh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&rh->ctx, + eh, + refund_obj)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (refund_obj); + GNUNET_free (rh->url); + GNUNET_free (rh); + return NULL; + } + json_decref (refund_obj); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "URL for refund: `%s'\n", + rh->url); + rh->keys = TALER_EXCHANGE_keys_incref (keys); + rh->job = GNUNET_CURL_job_add2 (ctx, + eh, + rh->ctx.headers, + &handle_refund_finished, + rh); + return rh; +} + + +void +TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund) +{ + if (NULL != refund->job) + { + GNUNET_CURL_job_cancel (refund->job); + refund->job = NULL; + } + GNUNET_free (refund->url); + TALER_curl_easy_post_finished (&refund->ctx); + TALER_EXCHANGE_keys_decref (refund->keys); + GNUNET_free (refund); +} + + +/* end of exchange_api_refund.c */ diff --git a/src/lib/exchange_api_stefan.c b/src/lib/exchange_api_stefan.c new file mode 100644 index 000000000..c3576cd73 --- /dev/null +++ b/src/lib/exchange_api_stefan.c @@ -0,0 +1,320 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/exchange_api_stefan.c + * @brief calculations on the STEFAN curve + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "exchange_api_handle.h" +#include + + +/** + * Determine smallest denomination in @a keys. + * + * @param keys exchange response to evaluate + * @return NULL on error (no denominations) + */ +static const struct TALER_Amount * +get_unit (const struct TALER_EXCHANGE_Keys *keys) +{ + const struct TALER_Amount *min = NULL; + + for (unsigned int i = 0; inum_denom_keys; i++) + { + const struct TALER_EXCHANGE_DenomPublicKey *dk + = &keys->denom_keys[i]; + + if ( (NULL == min) || + (1 == TALER_amount_cmp (min, + /* > */ + &dk->value)) ) + min = &dk->value; + } + GNUNET_break (NULL != min); + return min; +} + + +/** + * Convert amount to double for STEFAN curve evaluation. + * + * @param a input amount + * @return (rounded) amount as a double + */ +static double +amount_to_double (const struct TALER_Amount *a) +{ + double d = (double) a->value; + + d += a->fraction / ((double) TALER_AMOUNT_FRAC_BASE); + return d; +} + + +/** + * Convert double to amount for STEFAN curve evaluation. + * + * @param dv input amount + * @param currency deisred currency + * @param[out] rval (rounded) amount as a double + */ +static void +double_to_amount (double dv, + const char *currency, + struct TALER_Amount *rval) +{ + double rem; + + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (currency, + rval)); + rval->value = floorl (dv); + rem = dv - ((double) rval->value); + if (rem < 0.0) + rem = 0.0; + rem *= TALER_AMOUNT_FRAC_BASE; + rval->fraction = floorl (rem); + if (rval->fraction >= TALER_AMOUNT_FRAC_BASE) + { + /* Strange, multiplication overflowed our range, + round up value instead */ + rval->fraction = 0; + rval->value += 1; + } +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_keys_stefan_b2n ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_Amount *brut, + struct TALER_Amount *net) +{ + const struct TALER_Amount *min; + double log_d = amount_to_double (&keys->stefan_log); + double lin_d = keys->stefan_lin; + double abs_d = amount_to_double (&keys->stefan_abs); + double bru_d = amount_to_double (brut); + double min_d; + double fee_d; + double net_d; + + if (TALER_amount_is_zero (brut)) + { + *net = *brut; + return GNUNET_NO; + } + min = get_unit (keys); + if (NULL == min) + return GNUNET_SYSERR; + if (1.0f <= keys->stefan_lin) + { + /* This cannot work, linear STEFAN fee estimate always + exceed any gross amount. */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + min_d = amount_to_double (min); + fee_d = abs_d + + log_d * log2 (bru_d / min_d) + + lin_d * bru_d; + if (fee_d > bru_d) + { + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (brut->currency, + net)); + return GNUNET_NO; + } + net_d = bru_d - fee_d; + double_to_amount (net_d, + brut->currency, + net); + return GNUNET_OK; +} + + +/** + * Our function + * f(x) := ne + ab + lo * log2(x/mi) + li * x - x + * for #newton(). + */ +static double +eval_f (double mi, + double ab, + double lo, + double li, + double ne, + double x) +{ + return ne + ab + lo * log2 (x / mi) + li * x - x; +} + + +/** + * Our function + * f'(x) := lo / log(2) / x + li - 1 + * for #newton(). + */ +static double +eval_fp (double mi, + double lo, + double li, + double ne, + double x) +{ + return lo / log (2) / x + li - 1; +} + + +/** + * Use Newton's method to find x where f(x)=0. + * + * @return x where "eval_f(x)==0". + */ +static double +newton (double mi, + double ab, + double lo, + double li, + double ne) +{ + const double eps = 0.00000001; /* max error allowed */ + double min_ab = ne + ab; /* result cannot be smaller than this! */ + /* compute lower bounds by various heuristics */ + double min_ab_li = min_ab + min_ab * li; + double min_ab_li_lo = min_ab_li + log2 (min_ab_li / mi) * lo; + double min_ab_lo = min_ab + log2 (min_ab / mi) * lo; + double min_ab_lo_li = min_ab_lo + min_ab_lo * li; + /* take global lower bound */ + double x_min = GNUNET_MAX (min_ab_lo_li, + min_ab_li_lo); + double x = x_min; /* use lower bound as starting point */ + + /* Objective: invert + ne := br - ab - lo * log2 (br/mi) - li * br + to find 'br'. + Method: use Newton's method to find root of: + f(x) := ne + ab + lo * log2 (x/mi) + li * x - x + using also + f'(x) := lo / log(2) / x + li - 1 + */ + /* Loop to abort in case of divergence; + 100 is already very high, 2-4 is normal! */ + for (unsigned int i = 0; i<100; i++) + { + double fx = eval_f (mi, ab, lo, li, ne, x); + double fxp = eval_fp (mi, lo, li, ne, x); + double x_new = x - fx / fxp; + + if (fabs (x - x_new) <= eps) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Needed %u rounds from %f to result BRUT %f => NET: %f\n", + i, + x_min, + x_new, + x_new - ab - li * x_new - lo * log2 (x / mi)); + return x_new; + } + if (x_new < x_min) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Divergence, obtained very bad estimate %f after %u rounds!\n", + x_new, + i); + return x_min; + } + x = x_new; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Slow convergence, returning bad estimate %f!\n", + x); + return x; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_keys_stefan_n2b ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_Amount *net, + struct TALER_Amount *brut) +{ + const struct TALER_Amount *min; + double lin_d = keys->stefan_lin; + double log_d = amount_to_double (&keys->stefan_log); + double abs_d = amount_to_double (&keys->stefan_abs); + double net_d = amount_to_double (net); + double min_d; + double brut_d; + + if (TALER_amount_is_zero (net)) + { + *brut = *net; + return GNUNET_NO; + } + min = get_unit (keys); + if (NULL == min) + return GNUNET_SYSERR; + if (1.0f <= keys->stefan_lin) + { + /* This cannot work, linear STEFAN fee estimate always + exceed any gross amount. */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + min_d = amount_to_double (min); + brut_d = newton (min_d, + abs_d, + log_d, + lin_d, + net_d); + double_to_amount (brut_d, + net->currency, + brut); + return GNUNET_OK; +} + + +void +TALER_EXCHANGE_keys_stefan_round ( + const struct TALER_EXCHANGE_Keys *keys, + struct TALER_Amount *val) +{ + const struct TALER_Amount *min; + uint32_t mod = 1; + uint32_t frac; + uint32_t rst; + + min = get_unit (keys); + if (NULL == min) + return; + frac = min->fraction; + while (0 == frac % 10) + { + mod *= 10; + frac /= 10; + } + rst = val->fraction % mod; + if (rst < mod / 2) + val->fraction -= rst; + else + val->fraction += mod - rst; +} diff --git a/src/lib/test_stefan.c b/src/lib/test_stefan.c new file mode 100644 index 000000000..838cca769 --- /dev/null +++ b/src/lib/test_stefan.c @@ -0,0 +1,206 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see + +*/ +/** + * @file lib/test_stefan.c + * @brief test calculations on the STEFAN curve + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "exchange_api_handle.h" + + +/** + * Check if @a a and @a b are numerically close. + * + * @param a an amount + * @param b an amount + * @return true if both values are quite close + */ +static bool +amount_close (const struct TALER_Amount *a, + const struct TALER_Amount *b) +{ + struct TALER_Amount delta; + + switch (TALER_amount_cmp (a, + b)) + { + case -1: /* a < b */ + GNUNET_assert (0 < + TALER_amount_subtract (&delta, + b, + a)); + break; + case 0: + /* perfect */ + return true; + case 1: /* a > b */ + GNUNET_assert (0 < + TALER_amount_subtract (&delta, + a, + b)); + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rounding error is %s\n", + TALER_amount2s (&delta)); + if (delta.value > 0) + { + GNUNET_break (0); + return false; + } + if (delta.fraction > 5000) + { + GNUNET_break (0); + return false; + } + return true; /* let's consider this a rounding error */ +} + + +int +main (int argc, + char **argv) +{ + struct TALER_EXCHANGE_DenomPublicKey dk; + struct TALER_EXCHANGE_Keys keys = { + .denom_keys = &dk, + .num_denom_keys = 1 + }; + struct TALER_Amount brut; + struct TALER_Amount net; + + (void) argc; + (void) argv; + GNUNET_log_setup ("test-stefan", + "INFO", + NULL); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:0.13", + &dk.value)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:1", + &keys.stefan_abs)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:0.13", + &keys.stefan_log)); + keys.stefan_lin = 1.15; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:4", + &brut)); + GNUNET_log_skip (1, + GNUNET_NO); + GNUNET_assert (GNUNET_SYSERR == + TALER_EXCHANGE_keys_stefan_b2n (&keys, + &brut, + &net)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:4", + &net)); + GNUNET_log_skip (1, + GNUNET_NO); + GNUNET_assert (GNUNET_SYSERR == + TALER_EXCHANGE_keys_stefan_n2b (&keys, + &net, + &brut)); + keys.stefan_lin = 1.0; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:4", + &brut)); + GNUNET_log_skip (1, + GNUNET_NO); + GNUNET_assert (GNUNET_SYSERR == + TALER_EXCHANGE_keys_stefan_b2n (&keys, + &brut, + &net)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("MAGIC:4", + &net)); + GNUNET_log_skip (1, + GNUNET_NO); + GNUNET_assert (GNUNET_SYSERR == + TALER_EXCHANGE_keys_stefan_n2b (&keys, + &net, + &brut)); + GNUNET_assert (0 == GNUNET_get_log_skip ()); + keys.stefan_lin = 0.1; + + /* try various values for lin and log STEFAN values */ + for (unsigned int li = 1; li < 13; li += 1) + { + keys.stefan_lin = 1.0 * li / 100.0; + + for (unsigned int lx = 1; lx < 100; lx += 1) + { + keys.stefan_log.fraction = lx * TALER_AMOUNT_FRAC_BASE / 100; + + /* Check brutto-to-netto is stable */ + for (unsigned int i = 0; i<10; i++) + { + struct TALER_Amount rval; + + brut.value = i; + brut.fraction = i * TALER_AMOUNT_FRAC_BASE / 10; + GNUNET_assert (GNUNET_SYSERR != + TALER_EXCHANGE_keys_stefan_b2n (&keys, + &brut, + &net)); + GNUNET_assert (GNUNET_SYSERR != + TALER_EXCHANGE_keys_stefan_n2b (&keys, + &net, + &rval)); + if (TALER_amount_is_zero (&net)) + GNUNET_assert (TALER_amount_is_zero (&rval)); + else + { + GNUNET_assert (amount_close (&brut, + &rval)); + TALER_EXCHANGE_keys_stefan_round (&keys, + &rval); + GNUNET_assert (amount_close (&brut, + &rval)); + } + } + + /* Check netto-to-brutto is stable */ + for (unsigned int i = 0; i<10; i++) + { + struct TALER_Amount rval; + + net.value = i; + net.fraction = i * TALER_AMOUNT_FRAC_BASE / 10; + GNUNET_assert (GNUNET_SYSERR != + TALER_EXCHANGE_keys_stefan_n2b (&keys, + &net, + &brut)); + GNUNET_assert (GNUNET_SYSERR != + TALER_EXCHANGE_keys_stefan_b2n (&keys, + &brut, + &rval)); + GNUNET_assert (amount_close (&net, + &rval)); + TALER_EXCHANGE_keys_stefan_round (&keys, + &rval); + GNUNET_assert (amount_close (&net, + &rval)); + } + } + } + return 0; +} -- cgit v1.2.3