exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 66c969bdb0ec6c02f97780e86bb9323bccd4269a
parent 4a83e23f67e2f5ca7acc242137e2bea94d6c4c00
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue,  3 Mar 2026 23:44:49 +0100

post-withdraw code cleanup

Diffstat:
Asrc/include/taler/taler-exchange/post-withdraw_blinded.h | 336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/exchange_api_post-withdraw.c | 43++++++-------------------------------------
Msrc/lib/exchange_api_post-withdraw_blinded.c | 311+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/testing/test-taler-exchange-aggregator-postgres.conf | 2++
Msrc/testing/test-taler-exchange-wirewatch-postgres.conf | 2++
Msrc/testing/test_exchange_api_keys_cherry_picking.conf | 3++-
6 files changed, 479 insertions(+), 218 deletions(-)

diff --git a/src/include/taler/taler-exchange/post-withdraw_blinded.h b/src/include/taler/taler-exchange/post-withdraw_blinded.h @@ -0,0 +1,336 @@ +/* + This file is part of TALER + Copyright (C) 2014-2026 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file include/taler/taler-exchange/post-withdraw_blinded.h + * @brief low-level C interface for POST /withdraw + * @author Christian Grothoff + */ +#ifndef _TALER_EXCHANGE__POST_WITHDRAW_BLINDED_H +#define _TALER_EXCHANGE__POST_WITHDRAW_BLINDED_H + +#include <taler/taler-exchange/common.h> +#include <taler/taler-exchange/post-withdraw.h> + + +/** + * @brief Information needed to withdraw coins (low-level, pre-blinded). + */ +struct TALER_EXCHANGE_WithdrawBlindedCoinInput +{ + /** + * The denomination of the coin. + */ + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + + /** + * Blinded planchet for a single coin. + */ + struct TALER_PlanchetDetail planchet_details; +}; + +/** + * @brief Information needed to withdraw age-restricted coins (low-level, pre-blinded). + */ +struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput +{ + /** + * The denomination of the coin. MUST support age restriction. + */ + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + + /** + * Tuple of length kappa of planchet candidates for a single coin. + */ + struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA]; +}; + + +/** + * Handle for an operation to POST /withdraw (low-level, pre-blinded variant). + */ +struct TALER_EXCHANGE_PostWithdrawBlindedHandle; + + +/** + * Set up POST /withdraw operation (low-level variant with pre-blinded + * planchets). + * Note that you must explicitly start the operation after setup. + * + * This variant does not do the blinding/unblinding and only + * fetches the blind signatures on the already blinded planchets. + * + * For age-restricted coins requiring a proof, pass @a blinded_input as NULL + * and supply the age-restricted input via the + * #TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_WITH_AGE_PROOF option. + * + * @param curl_ctx The curl context to use + * @param keys The /keys material from the exchange + * @param exchange_url The base-URL of the exchange + * @param reserve_priv private key of the reserve to withdraw from + * @param blinding_seed seed used for blinding of CS denominations, might be NULL + * @param num_input number of entries in the @a blinded_input array + * @param blinded_input array of planchet details to withdraw, or NULL if using WITH_AGE_PROOF option + * @return handle to operation, NULL on error + */ +struct TALER_EXCHANGE_PostWithdrawBlindedHandle * +TALER_EXCHANGE_post_withdraw_blinded_create ( + struct GNUNET_CURL_Context *curl_ctx, + struct TALER_EXCHANGE_Keys *keys, + const char *exchange_url, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_BlindingMasterSeedP *blinding_seed, + size_t num_input, + const struct TALER_EXCHANGE_WithdrawBlindedCoinInput *blinded_input); + + +/** + * Possible options we can set for the POST /withdraw request + * (low-level, pre-blinded variant). + */ +enum TALER_EXCHANGE_PostWithdrawBlindedOption +{ + /** + * End of list of options. + */ + TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_END = 0, + + /** + * Upgrade to an age-proof withdrawal for age-restricted coins, requiring + * an additional call to POST /reveal-withdraw. + * The @e details.with_age_proof.max_age field gives the maximum age to + * (provably) commit to. + * The @e details.with_age_proof.input field gives the KAPPA planchet + * candidates per coin. + */ + TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_WITH_AGE_PROOF + +}; + + +/** + * Value for an option we can set for the POST /withdraw request + * (low-level, pre-blinded variant). + */ +struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue +{ + /** + * Type of the option being set. + */ + enum TALER_EXCHANGE_PostWithdrawBlindedOption option; + + /** + * Specific option value. + */ + union + { + /** + * Value if @e option is + * #TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_WITH_AGE_PROOF. + */ + struct + { + /** + * The maximum age to commit to. + */ + uint8_t max_age; + + /** + * Array of KAPPA planchet candidates per coin, length matches + * the @e num_input given to _create(). + */ + const struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput *input; + + } with_age_proof; + + } details; + +}; + + +/** + * Terminate the list of options. + * + * @return the terminating object + */ +#define TALER_EXCHANGE_post_withdraw_blinded_option_end_() \ + (const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue) \ + { \ + .option = TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_END \ + } + +/** + * Upgrade to an age-proof withdrawal for age-restricted coins. + * + * @param age the maximum age to commit to + * @param inp pointer to array of KAPPA planchet candidates per coin + * @return representation of the option + */ +#define TALER_EXCHANGE_post_withdraw_blinded_option_with_age_proof(age, inp) \ + (const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue) \ + { \ + .option = TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_WITH_AGE_PROOF, \ + .details.with_age_proof.max_age = (age), \ + .details.with_age_proof.input = (inp) \ + } + + +/** + * Set the requested options for the operation. + * + * If any option fails, other options may or may not be applied. + * + * @param pwbh the request to set the options for + * @param num_options length of the @a options array + * @param options an array of options + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_post_withdraw_blinded_set_options_ ( + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh, + unsigned int num_options, + const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue options[]); + + +/** + * Set the requested options for the operation. + * + * If any option fails, other options may or may not be applied. + * + * @param pwbh the request to set the options for + * @param ... the list of the options, each created by a + * TALER_EXCHANGE_post_withdraw_blinded_option_NAME(VALUE) macro + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +#define TALER_EXCHANGE_post_withdraw_blinded_set_options(pwbh,...) \ + TALER_EXCHANGE_post_withdraw_blinded_set_options_ ( \ + pwbh, \ + TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE, \ + ((const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue[]) \ + {__VA_ARGS__, \ + TALER_EXCHANGE_post_withdraw_blinded_option_end_ ()} \ + )) + + +/** + * Response from a POST /withdraw request (low-level, pre-blinded variant). + */ +struct TALER_EXCHANGE_PostWithdrawBlindedResponse +{ + /** + * HTTP response data. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details about the response + */ + union + { + /** + * Details if the status is #MHD_HTTP_OK + */ + struct + { + /** + * Number of signatures returned. + */ + unsigned int num_sigs; + + /** + * Array of @e num_sigs blinded denomination signatures, giving each + * coin its value and validity. The array gives these coins in the same + * order (and should have the same length) in which the original + * withdraw request specified the respective denomination keys. + */ + const struct TALER_BlindedDenominationSignature *blinded_denom_sigs; + + /** + * The commitment of the withdraw request, needed for the later calls to /recoup + */ + struct TALER_HashBlindedPlanchetsP planchets_h; + + } ok; + + /** + * Details if the status is MHD_HTTP_CREATED, i.e. in case of + * age-restriction. The response is input to prepare the required + * follow-up call to /reveal-withdraw. + */ + struct TALER_EXCHANGE_WithdrawCreated created; + + /** + * Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS. + */ + struct TALER_EXCHANGE_KycNeededRedirect unavailable_for_legal_reasons; + + } details; +}; + + +#ifndef TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_PostWithdrawBlindedCallback. + */ +#define TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE void +#endif /* TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE */ + +/** + * Type of the function that receives the result of a + * POST /withdraw request (low-level, pre-blinded variant). + * + * @param cls closure + * @param result result returned by the HTTP server + */ +typedef void +(*TALER_EXCHANGE_PostWithdrawBlindedCallback)( + TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_PostWithdrawBlindedResponse *result); + + +/** + * Start POST /withdraw operation (low-level, pre-blinded variant). + * + * @param[in,out] pwbh operation to start + * @param cb function to call with the exchange's result + * @param cb_cls closure for @a cb + * @return status code, #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TALER_EXCHANGE_post_withdraw_blinded_start ( + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh, + TALER_EXCHANGE_PostWithdrawBlindedCallback cb, + TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE *cb_cls); + + +/** + * Cancel POST /withdraw operation (low-level, pre-blinded variant). This + * function must not be called by clients after the + * TALER_EXCHANGE_PostWithdrawBlindedCallback has been invoked (as in those + * cases it'll be called internally by the implementation already). + * + * @param[in] pwbh operation to cancel + */ +void +TALER_EXCHANGE_post_withdraw_blinded_cancel ( + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh); + + +#endif /* _TALER_EXCHANGE__POST_WITHDRAW_H */ diff --git a/src/lib/exchange_api_post-withdraw.c b/src/lib/exchange_api_post-withdraw.c @@ -429,7 +429,6 @@ copy_results_with_age_proof ( }; wh->withdraw_blinded_handle = NULL; - switch (wbr->hr.http_status) { case MHD_HTTP_OK: @@ -565,6 +564,7 @@ call_withdraw_blinded ( wh); if (TALER_EC_NONE != ec) { + TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle); wh->withdraw_blinded_handle = NULL; return ec; } @@ -941,11 +941,8 @@ ERROR: if (0 < cs_num) { GNUNET_free (wh->bp_nonces); - wh->bp_nonces = NULL; GNUNET_free (wh->bp_coins); - wh->bp_coins = NULL; GNUNET_free (wh->bp_nonce_keys); - wh->bp_nonce_keys = NULL; wh->num_bp_coins = 0; wh->num_bp_nonces = 0; wh->num_bp_nonce_keys = 0; @@ -955,34 +952,6 @@ ERROR: } -/** - * Allocate and set up the common fields of a PostWithdrawHandle. - * - * @param curl_ctx The curl context to use - * @param keys The keys from the exchange - * @param exchange_url The base url to the exchange - * @param reserve_priv The private key of the reserve - * @return the handle - */ -static struct TALER_EXCHANGE_PostWithdrawHandle * -setup_withdraw_handle ( - struct GNUNET_CURL_Context *curl_ctx, - struct TALER_EXCHANGE_Keys *keys, - const char *exchange_url, - const struct TALER_ReservePrivateKeyP *reserve_priv) -{ - struct TALER_EXCHANGE_PostWithdrawHandle *wh; - - wh = GNUNET_new (struct TALER_EXCHANGE_PostWithdrawHandle); - wh->exchange_url = exchange_url; - wh->keys = TALER_EXCHANGE_keys_incref (keys); - wh->curl_ctx = curl_ctx; - wh->reserve_priv = reserve_priv; - - return wh; -} - - struct TALER_EXCHANGE_PostWithdrawHandle * TALER_EXCHANGE_post_withdraw_create ( struct GNUNET_CURL_Context *curl_ctx, @@ -996,11 +965,11 @@ TALER_EXCHANGE_post_withdraw_create ( { struct TALER_EXCHANGE_PostWithdrawHandle *wh; - wh = setup_withdraw_handle (curl_ctx, - keys, - exchange_url, - reserve_priv); - GNUNET_assert (NULL != wh); + wh = GNUNET_new (struct TALER_EXCHANGE_PostWithdrawHandle); + wh->exchange_url = exchange_url; + wh->keys = TALER_EXCHANGE_keys_incref (keys); + wh->curl_ctx = curl_ctx; + wh->reserve_priv = reserve_priv; wh->seed = *seed; wh->max_age = opaque_max_age; wh->init_num_coins = num_coins; diff --git a/src/lib/exchange_api_post-withdraw_blinded.c b/src/lib/exchange_api_post-withdraw_blinded.c @@ -456,16 +456,76 @@ handle_withdraw_blinded_finished ( } -/** - * Runs the actual withdraw operation with the blinded planchets. - * - * @param[in,out] wbh withdraw blinded handle - * @return #TALER_EC_NONE on success, error code on failure - */ -static enum TALER_ErrorCode -perform_withdraw_protocol ( - struct TALER_EXCHANGE_PostWithdrawBlindedHandle *wbh) +struct TALER_EXCHANGE_PostWithdrawBlindedHandle * +TALER_EXCHANGE_post_withdraw_blinded_create ( + struct GNUNET_CURL_Context *curl_ctx, + struct TALER_EXCHANGE_Keys *keys, + const char *exchange_url, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_BlindingMasterSeedP *blinding_seed, + size_t num_input, + const struct TALER_EXCHANGE_WithdrawBlindedCoinInput *blinded_input) +{ + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *wbh = + GNUNET_new (struct TALER_EXCHANGE_PostWithdrawBlindedHandle); + + wbh->keys = TALER_EXCHANGE_keys_incref (keys); + wbh->curl_ctx = curl_ctx; + wbh->reserve_priv = reserve_priv; + wbh->request_url = TALER_url_join (exchange_url, + "withdraw", + NULL); + GNUNET_CRYPTO_eddsa_key_get_public ( + &wbh->reserve_priv->eddsa_priv, + &wbh->reserve_pub.eddsa_pub); + wbh->num_input = num_input; + wbh->blinded.input = blinded_input; + wbh->blinding_seed = blinding_seed; + + return wbh; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_post_withdraw_blinded_set_options_ ( + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh, + unsigned int num_options, + const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue options[]) { + for (unsigned int i = 0; i < num_options; i++) + { + const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue *opt = + &options[i]; + switch (opt->option) + { + case TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_END: + return GNUNET_OK; + case TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_WITH_AGE_PROOF: + pwbh->with_age_proof = true; + pwbh->max_age = opt->details.with_age_proof.max_age; + pwbh->blinded.with_age_proof_input = opt->details.with_age_proof.input; + break; + } + } + return GNUNET_OK; +} + + +enum TALER_ErrorCode +TALER_EXCHANGE_post_withdraw_blinded_start ( + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh, + TALER_EXCHANGE_PostWithdrawBlindedCallback cb, + TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE *cb_cls) +{ + json_t *j_denoms = NULL; + json_t *j_planchets = NULL; + json_t *j_request_body = NULL; + CURL *curlh = NULL; + struct GNUNET_HashContext *coins_hctx = NULL; + struct TALER_BlindedCoinHashP bch; + + pwbh->callback = cb; + pwbh->callback_cls = cb_cls; #define FAIL_IF(cond) \ do { \ if ((cond)) \ @@ -475,60 +535,53 @@ perform_withdraw_protocol ( } \ } while (0) - json_t * j_denoms = NULL; - json_t *j_planchets = NULL; - json_t *j_request_body = NULL; - CURL *curlh = NULL; - struct GNUNET_HashContext *coins_hctx = NULL; - struct TALER_BlindedCoinHashP bch; - - GNUNET_assert (0 < wbh->num_input); + GNUNET_assert (0 < pwbh->num_input); FAIL_IF (GNUNET_OK != - TALER_amount_set_zero (wbh->keys->currency, - &wbh->amount)); + TALER_amount_set_zero (pwbh->keys->currency, + &pwbh->amount)); FAIL_IF (GNUNET_OK != - TALER_amount_set_zero (wbh->keys->currency, - &wbh->fee)); + TALER_amount_set_zero (pwbh->keys->currency, + &pwbh->fee)); /* Accumulate total value with fees */ - for (size_t i = 0; i < wbh->num_input; i++) + for (size_t i = 0; i < pwbh->num_input; i++) { const struct TALER_EXCHANGE_DenomPublicKey *dpub = - wbh->with_age_proof ? - wbh->blinded.with_age_proof_input[i].denom_pub : - wbh->blinded.input[i].denom_pub; + pwbh->with_age_proof ? + pwbh->blinded.with_age_proof_input[i].denom_pub : + pwbh->blinded.input[i].denom_pub; FAIL_IF (0 > - TALER_amount_add (&wbh->amount, - &wbh->amount, + TALER_amount_add (&pwbh->amount, + &pwbh->amount, &dpub->value)); FAIL_IF (0 > - TALER_amount_add (&wbh->fee, - &wbh->fee, + TALER_amount_add (&pwbh->fee, + &pwbh->fee, &dpub->fees.withdraw)); if (GNUNET_CRYPTO_BSA_CS == dpub->key.bsign_pub_key->cipher) - GNUNET_assert (NULL != wbh->blinding_seed); + GNUNET_assert (NULL != pwbh->blinding_seed); } - if (wbh->with_age_proof || wbh->max_age > 0) + if (pwbh->with_age_proof || pwbh->max_age > 0) { - wbh->age_mask = - wbh->blinded.with_age_proof_input[0].denom_pub->key.age_mask; + pwbh->age_mask = + pwbh->blinded.with_age_proof_input[0].denom_pub->key.age_mask; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Attempting to withdraw from reserve %s with maximum age %d to proof\n", - TALER_B2S (&wbh->reserve_pub), - wbh->max_age); + TALER_B2S (&pwbh->reserve_pub), + pwbh->max_age); } else { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Attempting to withdraw from reserve %s\n", - TALER_B2S (&wbh->reserve_pub)); + TALER_B2S (&pwbh->reserve_pub)); } coins_hctx = GNUNET_CRYPTO_hash_context_start (); @@ -539,19 +592,19 @@ perform_withdraw_protocol ( FAIL_IF ((NULL == j_denoms) || (NULL == j_planchets)); - for (size_t i = 0; i< wbh->num_input; i++) + for (size_t i = 0; i< pwbh->num_input; i++) { /* Build the denomination array */ const struct TALER_EXCHANGE_DenomPublicKey *denom_pub = - wbh->with_age_proof ? - wbh->blinded.with_age_proof_input[i].denom_pub : - wbh->blinded.input[i].denom_pub; + pwbh->with_age_proof ? + pwbh->blinded.with_age_proof_input[i].denom_pub : + pwbh->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 (wbh->with_age_proof && - (wbh->age_mask.bits != denom_pub->key.age_mask.bits)); + FAIL_IF (pwbh->with_age_proof && + (pwbh->age_mask.bits != denom_pub->key.age_mask.bits)); jdenom = GNUNET_JSON_from_data_auto (denom_h); FAIL_IF (NULL == jdenom); @@ -561,12 +614,12 @@ perform_withdraw_protocol ( /* Build the planchet array and calculate the hash over all planchets. */ - if (! wbh->with_age_proof) + if (! pwbh->with_age_proof) { - for (size_t i = 0; i< wbh->num_input; i++) + for (size_t i = 0; i< pwbh->num_input; i++) { const struct TALER_PlanchetDetail *planchet = - &wbh->blinded.input[i].planchet_details; + &pwbh->blinded.input[i].planchet_details; json_t *jc = GNUNET_JSON_PACK ( TALER_JSON_pack_blinded_planchet ( NULL, @@ -599,10 +652,10 @@ perform_withdraw_protocol ( batch_ctx = GNUNET_CRYPTO_hash_context_start (); FAIL_IF (NULL == batch_ctx); - for (size_t i = 0; i< wbh->num_input; i++) + for (size_t i = 0; i< pwbh->num_input; i++) { const struct TALER_PlanchetDetail *planchet = - &wbh->blinded.with_age_proof_input[i].planchet_details[k]; + &pwbh->blinded.with_age_proof_input[i].planchet_details[k]; json_t *jc = GNUNET_JSON_PACK ( TALER_JSON_pack_blinded_planchet ( NULL, @@ -634,48 +687,46 @@ perform_withdraw_protocol ( } } - /* Build the hash of the planchets */ GNUNET_CRYPTO_hash_context_finish ( coins_hctx, - &wbh->planchets_h.hash); + &pwbh->planchets_h.hash); coins_hctx = NULL; - /* Sign the request */ TALER_wallet_withdraw_sign ( - &wbh->amount, - &wbh->fee, - &wbh->planchets_h, - wbh->blinding_seed, - wbh->with_age_proof ? &wbh->age_mask: NULL, - wbh->with_age_proof ? wbh->max_age : 0, - wbh->reserve_priv, - &wbh->reserve_sig); + &pwbh->amount, + &pwbh->fee, + &pwbh->planchets_h, + pwbh->blinding_seed, + pwbh->with_age_proof ? &pwbh->age_mask: NULL, + pwbh->with_age_proof ? pwbh->max_age : 0, + pwbh->reserve_priv, + &pwbh->reserve_sig); /* Initiate the POST-request */ j_request_body = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("cipher", "ED25519"), GNUNET_JSON_pack_data_auto ("reserve_pub", - &wbh->reserve_pub), + &pwbh->reserve_pub), GNUNET_JSON_pack_array_steal ("denoms_h", j_denoms), GNUNET_JSON_pack_array_steal ("coin_evs", j_planchets), GNUNET_JSON_pack_allow_null ( - wbh->with_age_proof + pwbh->with_age_proof ? GNUNET_JSON_pack_int64 ("max_age", - wbh->max_age) + pwbh->max_age) : GNUNET_JSON_pack_string ("max_age", NULL) ), GNUNET_JSON_pack_data_auto ("reserve_sig", - &wbh->reserve_sig)); + &pwbh->reserve_sig)); FAIL_IF (NULL == j_request_body); - if (NULL != wbh->blinding_seed) + if (NULL != pwbh->blinding_seed) { json_t *j_seed = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("blinding_seed", - wbh->blinding_seed)); + pwbh->blinding_seed)); GNUNET_assert (NULL != j_seed); GNUNET_assert (0 == json_object_update_new ( @@ -683,25 +734,24 @@ perform_withdraw_protocol ( j_seed)); } - curlh = TALER_EXCHANGE_curl_easy_get_ (wbh->request_url); + curlh = TALER_EXCHANGE_curl_easy_get_ (pwbh->request_url); FAIL_IF (NULL == curlh); FAIL_IF (GNUNET_OK != TALER_curl_easy_post ( - &wbh->post_ctx, + &pwbh->post_ctx, curlh, j_request_body)); json_decref (j_request_body); j_request_body = NULL; - wbh->job = GNUNET_CURL_job_add2 ( - wbh->curl_ctx, + pwbh->job = GNUNET_CURL_job_add2 ( + pwbh->curl_ctx, curlh, - wbh->post_ctx.headers, + pwbh->post_ctx.headers, &handle_withdraw_blinded_finished, - wbh); - FAIL_IF (NULL == wbh->job); + pwbh); + FAIL_IF (NULL == pwbh->job); - /* No errors, return */ return TALER_EC_NONE; ERROR: @@ -720,120 +770,21 @@ ERROR: } -/** - * @brief Prepare the handler for blinded withdraw - * - * Allocates the handler struct and prepares all fields of the handler - * except the blinded planchets, - * which depend on them being age-restricted or not. - * - * @param curl_ctx the context for curl - * @param keys the exchange keys - * @param exchange_url the url to the exchange - * @param reserve_priv the reserve's private key - * @return the handler, NULL on error - */ -static struct TALER_EXCHANGE_PostWithdrawBlindedHandle * -setup_handler_common ( - struct GNUNET_CURL_Context *curl_ctx, - struct TALER_EXCHANGE_Keys *keys, - const char *exchange_url, - const struct TALER_ReservePrivateKeyP *reserve_priv) -{ - struct TALER_EXCHANGE_PostWithdrawBlindedHandle *wbh = - GNUNET_new (struct TALER_EXCHANGE_PostWithdrawBlindedHandle); - - wbh->keys = TALER_EXCHANGE_keys_incref (keys); - wbh->curl_ctx = curl_ctx; - wbh->reserve_priv = reserve_priv; - wbh->request_url = TALER_url_join (exchange_url, - "withdraw", - NULL); - GNUNET_CRYPTO_eddsa_key_get_public ( - &wbh->reserve_priv->eddsa_priv, - &wbh->reserve_pub.eddsa_pub); - - return wbh; -} - - -struct TALER_EXCHANGE_PostWithdrawBlindedHandle * -TALER_EXCHANGE_post_withdraw_blinded_create ( - struct GNUNET_CURL_Context *curl_ctx, - struct TALER_EXCHANGE_Keys *keys, - const char *exchange_url, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_BlindingMasterSeedP *blinding_seed, - size_t num_input, - const struct TALER_EXCHANGE_WithdrawBlindedCoinInput *blinded_input) -{ - struct TALER_EXCHANGE_PostWithdrawBlindedHandle *wbh = - setup_handler_common (curl_ctx, - keys, - exchange_url, - reserve_priv); - - wbh->with_age_proof = false; - wbh->num_input = num_input; - wbh->blinded.input = blinded_input; - wbh->blinding_seed = blinding_seed; - - return wbh; -} - - -enum GNUNET_GenericReturnValue -TALER_EXCHANGE_post_withdraw_blinded_set_options_ ( - struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh, - unsigned int num_options, - const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue options[]) -{ - for (unsigned int i = 0; i < num_options; i++) - { - const struct TALER_EXCHANGE_PostWithdrawBlindedOptionValue *opt = - &options[i]; - switch (opt->option) - { - case TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_END: - return GNUNET_OK; - case TALER_EXCHANGE_POST_WITHDRAW_BLINDED_OPTION_WITH_AGE_PROOF: - pwbh->with_age_proof = true; - pwbh->max_age = opt->details.with_age_proof.max_age; - pwbh->blinded.with_age_proof_input = opt->details.with_age_proof.input; - break; - } - } - return GNUNET_OK; -} - - -enum TALER_ErrorCode -TALER_EXCHANGE_post_withdraw_blinded_start ( - struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh, - TALER_EXCHANGE_PostWithdrawBlindedCallback cb, - TALER_EXCHANGE_POST_WITHDRAW_BLINDED_RESULT_CLOSURE *cb_cls) -{ - pwbh->callback = cb; - pwbh->callback_cls = cb_cls; - return perform_withdraw_protocol (pwbh); -} - - void TALER_EXCHANGE_post_withdraw_blinded_cancel ( - struct TALER_EXCHANGE_PostWithdrawBlindedHandle *wbh) + struct TALER_EXCHANGE_PostWithdrawBlindedHandle *pwbh) { - if (NULL == wbh) + if (NULL == pwbh) return; - if (NULL != wbh->job) + if (NULL != pwbh->job) { - GNUNET_CURL_job_cancel (wbh->job); - wbh->job = NULL; + GNUNET_CURL_job_cancel (pwbh->job); + pwbh->job = NULL; } - GNUNET_free (wbh->request_url); - TALER_EXCHANGE_keys_decref (wbh->keys); - TALER_curl_easy_post_finished (&wbh->post_ctx); - GNUNET_free (wbh); + GNUNET_free (pwbh->request_url); + TALER_EXCHANGE_keys_decref (pwbh->keys); + TALER_curl_easy_post_finished (&pwbh->post_ctx); + GNUNET_free (pwbh); } diff --git a/src/testing/test-taler-exchange-aggregator-postgres.conf b/src/testing/test-taler-exchange-aggregator-postgres.conf @@ -15,6 +15,7 @@ DURATION = 14 days [exchange] CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 +TINY_AMOUNT = EUR:0.01 AML_THRESHOLD = EUR:1000000 DB = postgres PORT = 8081 @@ -24,6 +25,7 @@ BASE_URL = "http://localhost:8081/" [auditor] BASE_URL = "http://auditor.example.com/" PORT = 8083 +TINY_AMOUNT = EUR:0.01 [auditordb-postgres] CONFIG = "postgres:///talercheck" diff --git a/src/testing/test-taler-exchange-wirewatch-postgres.conf b/src/testing/test-taler-exchange-wirewatch-postgres.conf @@ -14,6 +14,7 @@ DURATION = 14 days [exchange] CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 +TINY_AMOUNT = EUR:0.01 AML_THRESHOLD = EUR:1000000 DB = postgres PORT = 8081 @@ -30,6 +31,7 @@ CONFIG = "postgres:///talercheck" [auditor] BASE_URL = "http://localhost:8083/" PORT = 8083 +TINY_AMOUNT = EUR:0.01 [auditordb-postgres] CONFIG = "postgres:///talercheck" diff --git a/src/testing/test_exchange_api_keys_cherry_picking.conf b/src/testing/test_exchange_api_keys_cherry_picking.conf @@ -7,7 +7,7 @@ TALER_TEST_HOME = test_exchange_api_keys_cherry_picking_home/ [auditor] BASE_URL = "http://localhost:8083/" PORT = 8083 - +TINY_AMOUNT = EUR:0.01 [taler-exchange-secmod-eddsa] OVERLAP_DURATION = 1 s @@ -17,6 +17,7 @@ LOOKAHEAD_SIGN = 20 s [exchange] CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 +TINY_AMOUNT = EUR:0.01 AML_THRESHOLD = EUR:1000000 PORT = 8081 MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG