diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/taler_merchant_service.h | 45 | ||||
-rw-r--r-- | src/lib/merchant_api_tip_pickup.c | 341 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_tip_pickup.c | 74 |
3 files changed, 341 insertions, 119 deletions
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 89f550b7..408020b3 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -3602,6 +3602,44 @@ struct TALER_MERCHANT_TipPickupHandle; /** + * Details about a pickup operation, as returned to the application. + */ +struct TALER_MERCHANT_PickupDetails +{ + /** + * HTTP response data. + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Details about the response. + */ + union + { + /** + * Details if the status is #MHD_HTTP_OK. + */ + struct + { + + /** + * Array of length @e num_sigs with details about each of the coins that + * were picked up. + */ + struct TALER_EXCHANGE_PrivateCoinDetails *pcds; + + /** + * Length of the @e pcds array. + */ + unsigned int num_sigs; + } success; + + } details; + +}; + + +/** * Callback for a POST /tips/$TIP_ID/pickup request. Returns the result of * the operation. * @@ -3613,10 +3651,7 @@ struct TALER_MERCHANT_TipPickupHandle; typedef void (*TALER_MERCHANT_TipPickupCallback) ( void *cls, - const struct TALER_MERCHANT_HttpResponse *hr, - unsigned int num_sigs, - const struct TALER_DenominationSignature sigs[], - const struct TALER_CoinSpendPrivateKeyP coin_privs[]); + const struct TALER_MERCHANT_PickupDetails *pd); /** @@ -3641,6 +3676,7 @@ struct TALER_MERCHANT_PlanchetData * backend that a customer wants to pick up a tip. * * @param ctx execution context + * @param exchange handle to the exchange we are picking up the tip from * @param backend_url base URL of the merchant backend * @param tip_id unique identifier for the tip * @param num_planchets number of planchets provided in @a pds @@ -3651,6 +3687,7 @@ struct TALER_MERCHANT_PlanchetData */ struct TALER_MERCHANT_TipPickupHandle * TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, + struct TALER_EXCHANGE_Handle *exchange, const char *backend_url, const struct TALER_TipIdentifierP *tip_id, unsigned int num_planchets, diff --git a/src/lib/merchant_api_tip_pickup.c b/src/lib/merchant_api_tip_pickup.c index 315f2ba9..b9ca9d59 100644 --- a/src/lib/merchant_api_tip_pickup.c +++ b/src/lib/merchant_api_tip_pickup.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017, 2020 Taler Systems SA + 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 Lesser General Public License as published by the Free Software @@ -52,10 +52,6 @@ struct PlanchetData */ struct TALER_CoinPubHash c_hash; - // FIXME: initialize these: - struct TALER_ExchangeWithdrawValues alg_values; - union TALER_DenominationBlindingKeyP bks; - struct TALER_CoinSpendPrivateKeyP coin_priv; }; @@ -81,19 +77,65 @@ struct TALER_MERCHANT_TipPickupHandle struct TALER_MERCHANT_TipPickup2Handle *tpo2; /** - * Number of planchets/coins used for this operation. + * Array of length @e num_planchets. */ - unsigned int num_planchets; + struct PlanchetData *planchets; /** * Array of length @e num_planchets. */ - struct PlanchetData *planchets; + struct TALER_EXCHANGE_PrivateCoinDetails *pcds; + + /** + * Handle for a /csr request we may optionally need + * to trigger. + */ + struct TALER_EXCHANGE_CsRHandle *csr; + + /** + * Context for making HTTP requests. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * URL of the merchant backend. + */ + char *backend_url; + /** + * ID of the tip we are picking up. + */ + struct TALER_TipIdentifierP tip_id; + + /** + * Number of planchets/coins used for this operation. + */ + unsigned int num_planchets; }; /** + * Fail the pickup operation @a tp, returning @a ec. + * Also cancels @a tp. + * + * @param[in] tp operation to fail + * @param ec reason for the failure + */ +static void +fail_pickup (struct TALER_MERCHANT_TipPickupHandle *tp, + enum TALER_ErrorCode ec) +{ + struct TALER_MERCHANT_PickupDetails pd = { + .hr.ec = ec + }; + + tp->cb (tp->cb_cls, + &pd); + TALER_MERCHANT_tip_pickup_cancel (tp); +} + + +/** * Callback for a /tip-pickup request. Returns the result of the operation. * Note that the client MUST still do the unblinding of the @a blind_sigs. * @@ -109,75 +151,190 @@ pickup_done_cb (void *cls, const struct TALER_BlindedDenominationSignature *blind_sigs) { struct TALER_MERCHANT_TipPickupHandle *tp = cls; + struct TALER_MERCHANT_PickupDetails pd = { + .hr = *hr + }; tp->tpo2 = NULL; if (NULL == blind_sigs) { tp->cb (tp->cb_cls, - hr, - 0, - NULL, - NULL); + &pd); TALER_MERCHANT_tip_pickup_cancel (tp); return; } { - struct TALER_DenominationSignature sigs[num_blind_sigs]; - enum GNUNET_GenericReturnValue ok; + enum GNUNET_GenericReturnValue ok = GNUNET_OK; - ok = GNUNET_OK; - memset (sigs, - 0, - sizeof (sigs)); for (unsigned int i = 0; i<num_blind_sigs; i++) { + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; struct TALER_FreshCoin fc; if (GNUNET_OK != TALER_planchet_to_coin (&tp->planchets[i].pk.key, &blind_sigs[i], - &tp->planchets[i].bks, - &tp->planchets[i].coin_priv, + &pcd->bks, + &pcd->coin_priv, &tp->planchets[i].c_hash, - &tp->planchets[i].alg_values, + &pcd->exchange_vals, &fc)) { ok = GNUNET_SYSERR; break; } - sigs[i] = fc.sig; + pcd->sig = fc.sig; } - if (GNUNET_OK == ok) - { - tp->cb (tp->cb_cls, - hr, - num_blind_sigs, - sigs, - NULL /* FIXME: pass coin_privs! */); - } - else + if (GNUNET_OK != ok) { struct TALER_MERCHANT_HttpResponse hrx = { .reply = hr->reply, - .http_status = 0, .ec = TALER_EC_MERCHANT_TIP_PICKUP_UNBLIND_FAILURE }; + pd.hr = hrx; tp->cb (tp->cb_cls, - &hrx, - 0, - NULL, - NULL); + &pd); + } + else + { + pd.details.success.num_sigs = num_blind_sigs; + pd.details.success.pcds = tp->pcds; + tp->cb (tp->cb_cls, + &pd); } - for (unsigned int i = 0; i<num_blind_sigs; i++) - TALER_denom_sig_free (&sigs[i]); } TALER_MERCHANT_tip_pickup_cancel (tp); } +/** + * We have obtained all of the exchange inputs. Continue the pickup. + * + * @param[in,out] tp operation to continue + */ +static void +pickup_post_csr (struct TALER_MERCHANT_TipPickupHandle *tp) +{ + struct TALER_PlanchetDetail details[tp->num_planchets]; + + for (unsigned int i = 0; i<tp->num_planchets; i++) + { + const struct PlanchetData *pd = &tp->planchets[i]; + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + + TALER_planchet_setup_coin_priv (&pd->ps, + &pcd->exchange_vals, + &pcd->coin_priv); + TALER_planchet_blinding_secret_create (&pd->ps, + &pcd->exchange_vals, + &pcd->bks); + if (GNUNET_OK != + TALER_planchet_prepare (&pd->pk.key, + &pcd->exchange_vals, + &pcd->bks, + &pcd->coin_priv, + &tp->planchets[i].c_hash, + &details[i])) + { + GNUNET_break (0); + for (unsigned int j = 0; j<i; j++) + TALER_planchet_detail_free (&details[j]); + fail_pickup (tp, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); + return; + } + } + tp->tpo2 = TALER_MERCHANT_tip_pickup2 (tp->ctx, + tp->backend_url, + &tp->tip_id, + tp->num_planchets, + details, + &pickup_done_cb, + tp); + for (unsigned int j = 0; j<tp->num_planchets; j++) + TALER_planchet_detail_free (&details[j]); + if (NULL == tp->tpo2) + { + GNUNET_break (0); + fail_pickup (tp, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); + return; + } +} + + +/** + * Callbacks of this type are used to serve the result of submitting a + * CS R request to a exchange. + * + * @param cls a `struct TALER_MERCHANT_TipPickupHandle` + * @param csrr response details + */ +static void +csr_cb (void *cls, + const struct TALER_EXCHANGE_CsRResponse *csrr) +{ + struct TALER_MERCHANT_TipPickupHandle *tp = cls; + + switch (csrr->hr.http_status) + { + case MHD_HTTP_OK: + { + unsigned int off = 0; + + for (unsigned int i = 0; i<tp->num_planchets; i++) + { + const struct PlanchetData *pd = &tp->planchets[i]; + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + + switch (pd->pk.key.cipher) + { + case TALER_DENOMINATION_INVALID: + GNUNET_assert (0); + break; + case TALER_DENOMINATION_RSA: + continue; + case TALER_DENOMINATION_CS: + if (off >= csrr->details.success.alg_values_len) + { + struct TALER_MERCHANT_PickupDetails pd = { + .hr.http_status = 0 + }; + + GNUNET_break (0); + tp->cb (tp->cb_cls, + &pd); + TALER_MERCHANT_tip_pickup_cancel (tp); + return; + } + pcd->exchange_vals = csrr->details.success.alg_values[off]; + off++; + break; + } + } + } + pickup_post_csr (tp); + return; + default: + { + struct TALER_MERCHANT_PickupDetails pd = { + .hr.hint = "/csr failed", + .hr.exchange_http_status = csrr->hr.http_status + }; + + tp->cb (tp->cb_cls, + &pd); + TALER_MERCHANT_tip_pickup_cancel (tp); + return; + } + } +} + + struct TALER_MERCHANT_TipPickupHandle * TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, + struct TALER_EXCHANGE_Handle *exchange, const char *backend_url, const struct TALER_TipIdentifierP *tip_id, unsigned int num_planchets, @@ -186,7 +343,7 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, void *pickup_cb_cls) { struct TALER_MERCHANT_TipPickupHandle *tp; - struct TALER_PlanchetDetail details[GNUNET_NZL (num_planchets)]; + unsigned int num_csr; if (0 == num_planchets) { @@ -194,52 +351,80 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, return NULL; } tp = GNUNET_new (struct TALER_MERCHANT_TipPickupHandle); + tp->cb = pickup_cb; + tp->cb_cls = pickup_cb_cls; + tp->ctx = ctx; + tp->backend_url = GNUNET_strdup (backend_url); + tp->tip_id = *tip_id; GNUNET_array_grow (tp->planchets, tp->num_planchets, num_planchets); + tp->pcds = GNUNET_new_array (num_planchets, + struct TALER_EXCHANGE_PrivateCoinDetails); + num_csr = 0; for (unsigned int i = 0; i<num_planchets; i++) { - tp->planchets[i].ps = pds[i].ps; - if (GNUNET_OK != - TALER_planchet_prepare (&pds[i].pk->key, - &tp->planchets[i].alg_values, - &tp->planchets[i].bks, - &tp->planchets[i].coin_priv, - &tp->planchets[i].c_hash, - &details[i])) + const struct TALER_MERCHANT_PlanchetData *pd = &pds[i]; + const struct TALER_EXCHANGE_DenomPublicKey *pk = pd->pk; + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + + tp->planchets[i].ps = pd->ps; + tp->planchets[i].pk = *pds[i].pk; + TALER_denom_pub_deep_copy (&tp->planchets[i].pk.key, + &pds[i].pk->key); + switch (pk->key.cipher) { + case TALER_DENOMINATION_RSA: + pcd->exchange_vals.cipher = TALER_DENOMINATION_RSA; + break; + case TALER_DENOMINATION_CS: + num_csr++; + break; + default: GNUNET_break (0); - for (unsigned int j = 0; j<i; j++) - TALER_planchet_detail_free (&details[j]); - GNUNET_array_grow (tp->planchets, - tp->num_planchets, - 0); - GNUNET_free (tp); + TALER_MERCHANT_tip_pickup_cancel (tp); return NULL; } } - for (unsigned int i = 0; i<num_planchets; i++) + if (0 == num_csr) { - tp->planchets[i].pk = *pds[i].pk; - TALER_denom_pub_deep_copy (&tp->planchets[i].pk.key, - &pds[i].pk->key); + pickup_post_csr (tp); + return tp; } - tp->cb = pickup_cb; - tp->cb_cls = pickup_cb_cls; - tp->tpo2 = TALER_MERCHANT_tip_pickup2 (ctx, - backend_url, - tip_id, - num_planchets, - details, - &pickup_done_cb, - tp); - for (unsigned int j = 0; j<num_planchets; j++) - TALER_planchet_detail_free (&details[j]); - if (NULL == tp->tpo2) { - GNUNET_break (0); - TALER_MERCHANT_tip_pickup_cancel (tp); - return NULL; + struct TALER_EXCHANGE_NonceKey nks[num_csr]; + unsigned int off = 0; + + for (unsigned int i = 0; i<tp->num_planchets; i++) + { + const struct PlanchetData *pd = &tp->planchets[i]; + + switch (pd->pk.key.cipher) + { + case TALER_DENOMINATION_INVALID: + GNUNET_assert (0); + break; + case TALER_DENOMINATION_RSA: + break; + case TALER_DENOMINATION_CS: + nks[off].pk = &pd->pk; + TALER_cs_withdraw_nonce_derive (&pd->ps, + &nks[off].nonce); + off++; + break; + } + } + tp->csr = TALER_EXCHANGE_csr (exchange, + off, + nks, + &csr_cb, + tp); + if (NULL == tp->csr) + { + GNUNET_break (0); + TALER_MERCHANT_tip_pickup_cancel (tp); + return NULL; + } } return tp; } @@ -249,7 +434,12 @@ void TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupHandle *tp) { for (unsigned int i = 0; i<tp->num_planchets; i++) + { + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i]; + + TALER_denom_sig_free (&pcd->sig); TALER_denom_pub_free (&tp->planchets[i].pk.key); + } GNUNET_array_grow (tp->planchets, tp->num_planchets, 0); @@ -258,6 +448,13 @@ TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupHandle *tp) TALER_MERCHANT_tip_pickup2_cancel (tp->tpo2); tp->tpo2 = NULL; } + if (NULL != tp->csr) + { + TALER_EXCHANGE_csr_cancel (tp->csr); + tp->csr = NULL; + } + GNUNET_free (tp->backend_url); + GNUNET_free (tp->pcds); GNUNET_free (tp); } diff --git a/src/testing/testing_api_cmd_tip_pickup.c b/src/testing/testing_api_cmd_tip_pickup.c index 229ba301..c7dec778 100644 --- a/src/testing/testing_api_cmd_tip_pickup.c +++ b/src/testing/testing_api_cmd_tip_pickup.c @@ -22,7 +22,6 @@ * @brief command to test the tipping. * @author Marcello Stanisci */ - #include "platform.h" #include <taler/taler_exchange_service.h> #include <taler/taler_testing_lib.h> @@ -90,11 +89,6 @@ struct TipPickupState struct TALER_Amount total_amount; /** - * How many coins are involved in the tipping operation. - */ - uint32_t num_coins; - - /** * The array of denomination keys, in the same order of @a * amounts. */ @@ -107,17 +101,15 @@ struct TipPickupState struct TALER_PlanchetMasterSecretP *psa; /** - * The array of coin private keys, in the same order of @a - * amounts. + * Set (by the interpreter) to an array of @a num_coins + * details on coins created from the (successful) tip operation. */ - // FIXME: initialize! - struct TALER_CoinSpendPrivateKeyP *coin_privs; + struct TALER_EXCHANGE_PrivateCoinDetails *pcds; /** - * Set (by the interpreter) to an array of @a num_coins - * signatures created from the (successful) tip operation. + * How many coins are involved in the tipping operation. */ - struct TALER_DenominationSignature *sigs; + uint32_t num_coins; /** * Expected Taler error code (NOTE: this is NOT the HTTP @@ -133,52 +125,48 @@ struct TipPickupState * (and if the status was 200 OK) proceede with the withdrawal. * * @param cls closure - * @param hr HTTP response - * @param num_sigs length of the @a sigs array, - * 0 on error - * @param sigs array of signatures over the coins, NULL on error + * @param pd details about the result of the operation */ static void pickup_cb (void *cls, - const struct TALER_MERCHANT_HttpResponse *hr, - unsigned int num_sigs, - const struct TALER_DenominationSignature *sigs, - const struct TALER_CoinSpendPrivateKeyP *coin_privs) + const struct TALER_MERCHANT_PickupDetails *pd) { struct TipPickupState *tps = cls; tps->tpo = NULL; - if (hr->http_status != tps->http_status) + if (pd->hr.http_status != tps->http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u (%d) to command %s\n", - hr->http_status, - (int) hr->ec, + pd->hr.http_status, + (int) pd->hr.ec, TALER_TESTING_interpreter_get_current_label (tps->is)); TALER_TESTING_FAIL (tps->is); } - if (hr->ec != tps->expected_ec) + if (pd->hr.ec != tps->expected_ec) TALER_TESTING_FAIL (tps->is); /* Safe to go ahead: http status was expected. */ - if ( (MHD_HTTP_OK != hr->http_status) || - (TALER_EC_NONE != hr->ec) ) + if ( (MHD_HTTP_OK != pd->hr.http_status) || + (TALER_EC_NONE != pd->hr.ec) ) { TALER_TESTING_interpreter_next (tps->is); return; } - if (num_sigs != tps->num_coins) + if (pd->details.success.num_sigs != tps->num_coins) TALER_TESTING_FAIL (tps->is); - tps->sigs = GNUNET_new_array (tps->num_coins, - struct TALER_DenominationSignature); - for (unsigned int i = 0; i<num_sigs; i++) - TALER_denom_sig_deep_copy (&tps->sigs[i], - &sigs[i]); - tps->coin_privs - = GNUNET_memdup (coin_privs, - num_sigs - * sizeof (struct TALER_CoinSpendPrivateKeyP)); + tps->pcds = GNUNET_new_array (tps->num_coins, + struct TALER_EXCHANGE_PrivateCoinDetails); + for (unsigned int i = 0; i<tps->num_coins; i++) + { + struct TALER_EXCHANGE_PrivateCoinDetails *pcd = + &pd->details.success.pcds[i]; + + tps->pcds[i] = *pcd; + TALER_denom_sig_deep_copy (&tps->pcds[i].sig, + &pcd->sig); + } TALER_TESTING_interpreter_next (tps->is); } @@ -292,6 +280,7 @@ tip_pickup_run (void *cls, &tip_id)) TALER_TESTING_FAIL (is); tps->tpo = TALER_MERCHANT_tip_pickup (is->ctx, + is->exchange, tps->merchant_url, tip_id, num_planchets, @@ -319,12 +308,11 @@ tip_pickup_cleanup (void *cls, GNUNET_free (tps->amounts_obj); GNUNET_free (tps->dks); GNUNET_free (tps->psa); - GNUNET_free (tps->coin_privs); - if (NULL != tps->sigs) + if (NULL != tps->pcds) { for (unsigned int i = 0; i<tps->num_coins; i++) - TALER_denom_sig_free (&tps->sigs[i]); - GNUNET_free (tps->sigs); + TALER_denom_sig_free (&tps->pcds[i].sig); + GNUNET_free (tps->pcds); } if (NULL != tps->tpo) { @@ -350,11 +338,11 @@ tip_pickup_traits (void *cls, TALER_TESTING_make_trait_planchet_secrets (index, &tps->psa[index]), TALER_TESTING_make_trait_coin_priv (index, - &tps->coin_privs[index]), + &tps->pcds[index].coin_priv), TALER_TESTING_make_trait_denom_pub (index, tps->dks[index]), TALER_TESTING_make_trait_denom_sig (index, - &tps->sigs[index]), + &tps->pcds[index].sig), TALER_TESTING_make_trait_amounts (index, &tps->amounts_obj[index]), TALER_TESTING_make_trait_amount (&tps->total_amount), |