diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-04-12 20:46:18 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-04-12 20:46:18 +0200 |
commit | c548a400ea1956d55c675e6a21fff46f6271455b (patch) | |
tree | e9bd4781ebf75ae9ca96843e2306da5788d49112 | |
parent | 9e94110b6a2e5fdf51f0d00a129d48ab3213753c (diff) |
implement #6173
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_tip-pickup.c | 204 | ||||
-rw-r--r-- | src/include/taler_merchant_service.h | 104 | ||||
-rw-r--r-- | src/lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/lib/merchant_api_tip_pickup.c | 359 | ||||
-rw-r--r-- | src/lib/merchant_api_tip_pickup2.c | 363 | ||||
-rw-r--r-- | src/lib/testing_api_cmd_tip_pickup.c | 219 |
7 files changed, 772 insertions, 482 deletions
@@ -1,3 +1,7 @@ +Sun 12 Apr 2020 08:45:11 PM CEST + Changed /tip-pickup API to withdraw directly from the exchange + and return blind signatures, instead of having the wallet do it (#6173). -CG + Fri 10 Apr 2020 09:01:22 PM CEST Changing refund API to have the merchant backend make the /refund request to the exchange instead of having the wallet do it (#5299). -CG diff --git a/src/backend/taler-merchant-httpd_tip-pickup.c b/src/backend/taler-merchant-httpd_tip-pickup.c index 34c50f08..51dd8121 100644 --- a/src/backend/taler-merchant-httpd_tip-pickup.c +++ b/src/backend/taler-merchant-httpd_tip-pickup.c @@ -30,6 +30,12 @@ /** + * Information we keep per tip pickup request. + */ +struct PickupContext; + + +/** * Details about a planchet that the customer wants to obtain * a withdrawal authorization. This is the information that * will need to be sent to the exchange to obtain the blind @@ -39,10 +45,24 @@ struct PlanchetDetail { /** - * The complete withdraw request that we are building to sign. - * Built incrementally during the processing of the request. + * Hash of the denomination public key requested for this planchet. + */ + struct GNUNET_HashCode h_denom_pub; + + /** + * Pickup context this planchet belongs to. + */ + struct PickupContext *pc; + + /** + * Handle to withdraw operation with the exchange. + */ + struct TALER_EXCHANGE_Withdraw2Handle *wh; + + /** + * Blind signature to return, or NULL if not available. */ - struct TALER_WithdrawRequestPS wr; + json_t *blind_sig; /** * Blinded coin (see GNUNET_CRYPTO_rsa_blind()). Note: is malloc()'ed! @@ -58,8 +78,7 @@ struct PlanchetDetail /** - * Information we keep for individual calls - * to requests that parse JSON, but keep no other state. + * Information we keep per tip pickup request. */ struct PickupContext { @@ -96,6 +115,11 @@ struct PickupContext struct TMH_EXCHANGES_FindOperation *fo; /** + * Handle to the exchange (set after exchange_found_cb()). + */ + struct TALER_EXCHANGE_Handle *eh; + + /** * Array of planchets of length @e planchets_len. */ struct PlanchetDetail *planchets; @@ -187,7 +211,20 @@ pickup_cleanup (struct TM_HandlerContext *hc) if (NULL != pc->planchets) { for (unsigned int i = 0; i<pc->planchets_len; i++) - GNUNET_free_non_null (pc->planchets[i].coin_ev); + { + struct PlanchetDetail *pd = &pc->planchets[i]; + GNUNET_free_non_null (pd->coin_ev); + if (NULL != pd->wh) + { + TALER_EXCHANGE_withdraw2_cancel (pd->wh); + pd->wh = NULL; + } + if (NULL != pd->blind_sig) + { + json_decref (pd->blind_sig); + pd->blind_sig = NULL; + } + } GNUNET_free (pc->planchets); pc->planchets = NULL; } @@ -203,7 +240,6 @@ pickup_cleanup (struct TM_HandlerContext *hc) MHD_destroy_response (pc->response); pc->response = NULL; } - GNUNET_free (pc); } @@ -216,6 +252,16 @@ pickup_cleanup (struct TM_HandlerContext *hc) static void resume_pc (struct PickupContext *pc) { + for (unsigned int i = 0; i<pc->planchets_len; i++) + { + struct PlanchetDetail *pd = &pc->planchets[i]; + + if (NULL != pd->wh) + { + TALER_EXCHANGE_withdraw2_cancel (pc->planchets[i].wh); + pc->planchets[i].wh = NULL; + } + } GNUNET_assert (GNUNET_YES == pc->suspended); GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, @@ -226,6 +272,68 @@ resume_pc (struct PickupContext *pc) /** + * Function called with the result of our attempt to withdraw + * the coin for a tip. + * + * @param cls closure + * @param hr HTTP response data + * @param blind_sig blind signature over the coin, NULL on error + */ +static void +withdraw_cb (void *cls, + const struct TALER_EXCHANGE_HttpResponse *hr, + const struct GNUNET_CRYPTO_RsaSignature *blind_sig) +{ + struct PlanchetDetail *pd = cls; + struct PickupContext *pc = pd->pc; + json_t *ja; + + pd->wh = NULL; + if (NULL == blind_sig) + { + pc->response_code = MHD_HTTP_FAILED_DEPENDENCY; + pc->response = TALER_MHD_make_json_pack ( + (NULL != hr->reply) + ? "{s:s, s:I, s:I, s:I, s:O}" + : "{s:s, s:I, s:I, s:I}", + "hint", "failed to withdraw coin from exchange", + "code", (json_int_t) TALER_EC_TIP_PICKUP_WITHDRAW_FAILED_AT_EXCHANGE, + "exchange_http_status", (json_int_t) hr->http_status, + "exchange_code", (json_int_t) hr->ec, + "exchange_reply", hr->reply); + resume_pc (pc); + return; + } + /* FIXME: persisit blind_sig in our database!? + (or at least _all_ of them once we have them all?) */ + pd->blind_sig = GNUNET_JSON_from_rsa_signature (blind_sig); + GNUNET_assert (NULL != pd->blind_sig); + for (unsigned int i = 0; i<pc->planchets_len; i++) + if (NULL != pc->planchets[i].wh) + return; + /* All done, build final response */ + ja = json_array (); + GNUNET_assert (NULL != ja); + for (unsigned int i = 0; i<pc->planchets_len; i++) + { + struct PlanchetDetail *pd = &pc->planchets[i]; + + GNUNET_assert (0 == + json_array_append_new (ja, + json_pack ("{s:o}", + "blind_sig", + pd->blind_sig))); + pd->blind_sig = NULL; + } + pc->response_code = MHD_HTTP_OK; + pc->response = TALER_MHD_make_json_pack ("{s:o}", + "blind_sigs", + ja); + resume_pc (pc); +} + + +/** * Prepare (and eventually execute) a pickup. Computes * the "pickup ID" (by hashing the planchets and denomination keys), * resolves the denomination keys and calculates the total @@ -237,9 +345,7 @@ static void run_pickup (struct PickupContext *pc) { struct TALER_ReservePrivateKeyP reserve_priv; - struct TALER_ReservePublicKeyP reserve_pub; enum TALER_ErrorCode ec; - json_t *sigs; db->preflight (db->cls); ec = db->pickup_tip_TR (db->cls, @@ -271,50 +377,30 @@ run_pickup (struct PickupContext *pc) resume_pc (pc); return; } - sigs = json_array (); - if (NULL == sigs) - { - GNUNET_break (0); - pc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - pc->response = TALER_MHD_make_error (TALER_EC_JSON_ALLOCATION_FAILURE, - "could not create JSON array"); - resume_pc (pc); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); for (unsigned int i = 0; i<pc->planchets_len; i++) { struct PlanchetDetail *pd = &pc->planchets[i]; - struct TALER_ReserveSignatureP reserve_sig; - - pd->wr.reserve_pub = reserve_pub; - GNUNET_CRYPTO_eddsa_sign (&reserve_priv.eddsa_priv, - &pd->wr, - &reserve_sig.eddsa_signature); - if (0 != - json_array_append_new (sigs, - json_pack ("{s:o}", - "reserve_sig", - GNUNET_JSON_from_data_auto ( - &reserve_sig)))) + struct TALER_PlanchetDetail pdx = { + .denom_pub_hash = pd->h_denom_pub, + .coin_ev = pd->coin_ev, + .coin_ev_size = pd->coin_ev_size, + }; + + pd->wh = TALER_EXCHANGE_withdraw2 (pc->eh, + &pdx, + &reserve_priv, + &withdraw_cb, + pd); + if (NULL == pd->wh) { GNUNET_break (0); - json_decref (sigs); pc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - pc->response = TALER_MHD_make_error (TALER_EC_JSON_ALLOCATION_FAILURE, - "could not add element to JSON array"); + pc->response = TALER_MHD_make_error (TALER_EC_TIP_PICKUP_WITHDRAW_FAILED, + "could not inititate withdrawal"); resume_pc (pc); return; } } - pc->response_code = MHD_HTTP_OK; - pc->response = TALER_MHD_make_json_pack ("{s:o, s:o}", - "reserve_pub", - GNUNET_JSON_from_data_auto ( - &reserve_pub), - "reserve_sigs", sigs); - resume_pc (pc); } @@ -387,12 +473,11 @@ exchange_found_cb (void *cls, for (unsigned int i = 0; i<pc->planchets_len; i++) { struct PlanchetDetail *pd = &pc->planchets[i]; - const struct TALER_EXCHANGE_DenomPublicKey *dk; struct TALER_Amount amount_with_fee; + const struct TALER_EXCHANGE_DenomPublicKey *dk; dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys, - &pd->wr. - h_denomination_pub); + &pd->h_denom_pub); if (NULL == dk) { pc->response_code = MHD_HTTP_NOT_FOUND; @@ -408,7 +493,7 @@ exchange_found_cb (void *cls, return; } GNUNET_CRYPTO_hash_context_read (hc, - &pd->wr.h_denomination_pub, + &pd->h_denom_pub, sizeof (struct GNUNET_HashCode)); GNUNET_CRYPTO_hash_context_read (hc, pd->coin_ev, @@ -434,10 +519,6 @@ exchange_found_cb (void *cls, ae = GNUNET_YES; } } - TALER_amount_hton (&pd->wr.withdraw_fee, - &dk->fee_withdraw); - TALER_amount_hton (&pd->wr.amount_with_fee, - &amount_with_fee); } GNUNET_CRYPTO_hash_context_finish (hc, &pc->pickup_id); @@ -455,6 +536,7 @@ exchange_found_cb (void *cls, resume_pc (pc); return; } + pc->eh = eh; pc->total = total; run_pickup (pc); } @@ -545,32 +627,23 @@ prepare_pickup (struct PickupContext *pc) * @return #GNUNET_OK upon success, #GNUNET_NO if a response * was generated, #GNUNET_SYSERR to drop the connection */ -static int +static enum GNUNET_GenericReturnValue parse_planchet (struct MHD_Connection *connection, const json_t *planchet, struct PlanchetDetail *pd) { - int ret; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &pd->wr.h_denomination_pub), + &pd->h_denom_pub), GNUNET_JSON_spec_varsize ("coin_ev", (void **) &pd->coin_ev, &pd->coin_ev_size), GNUNET_JSON_spec_end () }; - ret = TALER_MHD_parse_json_data (connection, - planchet, - spec); - if (GNUNET_OK != ret) - return ret; - pd->wr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); - pd->wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); - GNUNET_CRYPTO_hash (pd->coin_ev, - pd->coin_ev_size, - &pd->wr.h_coin_envelope); - return ret; + return TALER_MHD_parse_json_data (connection, + planchet, + spec); } @@ -674,6 +747,7 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh, struct PlanchetDetail); for (unsigned int i = 0; i<pc->planchets_len; i++) { + pc->planchets[i].pc = pc; if (GNUNET_OK != (res = parse_planchet (connection, json_array_get (planchets, diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 7298b8bd..71b69f54 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -1156,33 +1156,47 @@ struct TALER_MERCHANT_TipPickupOperation; /** - * Callback for a /tip-pickup request. Returns the result of - * the operation. + * Callback for a /tip-pickup request. Returns the result of the operation. * * @param cls closure * @param hr HTTP response details - * @param reserve_pub public key of the reserve that made the @a reserve_sigs, NULL on error - * @param num_reserve_sigs length of the @a reserve_sigs array, 0 on error - * @param reserve_sigs array of signatures authorizing withdrawals, NULL on error + * @param num_sigs length of the @a reserve_sigs array, 0 on error + * @param sigs array of signatures over the coins, NULL on error */ typedef void (*TALER_MERCHANT_TipPickupCallback) ( void *cls, const struct TALER_MERCHANT_HttpResponse *hr, - const struct TALER_ReservePublicKeyP *reserve_pub, - unsigned int num_reserve_sigs, - const struct TALER_ReserveSignatureP *reserve_sigs); + unsigned int num_sigs, + const struct TALER_DenominationSignature *sigs); /** + * Information per planchet. + */ +struct TALER_MERCHANT_PlanchetData +{ + /** + * Planchet secrets. + */ + struct TALER_PlanchetSecretsP ps; + + /** + * Denomination key desired. + */ + const struct TALER_EXCHANGE_DenomPublicKey *pk; + +}; + +/** * Issue a /tip-pickup request to the backend. Informs the backend * that a customer wants to pick up a tip. * * @param ctx execution context * @param backend_url base URL of the merchant backend * @param tip_id unique identifier for the tip - * @param num_planches number of planchets provided in @a planchets - * @param planchets array of planchets to be signed into existence for the tip + * @param num_planches number of planchets provided in @a pds + * @param pds array of planchet secrets to be signed into existence for the tip * @param pickup_cb callback which will work the response gotten from the backend * @param pickup_cb_cls closure to pass to @a pickup_cb * @return handle for this operation, NULL upon errors @@ -1192,7 +1206,7 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, const char *backend_url, const struct GNUNET_HashCode *tip_id, unsigned int num_planchets, - struct TALER_PlanchetDetail *planchets, + const struct TALER_MERCHANT_PlanchetData *pds, TALER_MERCHANT_TipPickupCallback pickup_cb, void *pickup_cb_cls); @@ -1206,6 +1220,74 @@ void TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupOperation *tp); +/** + * Handle for a low-level /tip-pickup operation (without unblinding). + */ +struct TALER_MERCHANT_TipPickup2Operation; + +/** + * A blind signature returned via tipping API. + */ + +struct TALER_MERCHANT_BlindSignature +{ + /** + * We use RSA. + */ + const struct GNUNET_CRYPTO_RsaSignature *blind_sig; +}; + + +/** + * 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. + * + * @param cls closure + * @param hr HTTP response details + * @param num_blind_sigs length of the @a blind_sigs array, 0 on error + * @param blind_sigs array of blind signatures over the planchets, NULL on error + */ +typedef void +(*TALER_MERCHANT_TipPickup2Callback) ( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + unsigned int num_blind_sigs, + const struct TALER_MERCHANT_BlindSignature *blind_sigs); + + +/** + * Issue a /tip-pickup request to the backend. Informs the backend + * that a customer wants to pick up a tip. + * + * @param ctx execution context + * @param backend_url base URL of the merchant backend + * @param tip_id unique identifier for the tip + * @param num_planches number of planchets provided in @a planchets + * @param planchets array of planchets to be signed into existence for the tip + * @param pickup_cb callback which will work the response gotten from the backend + * @param pickup_cb_cls closure to pass to @a pickup_cb + * @return handle for this operation, NULL upon errors + */ +struct TALER_MERCHANT_TipPickup2Operation * +TALER_MERCHANT_tip_pickup2 (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct GNUNET_HashCode *tip_id, + unsigned int num_planchets, + struct TALER_PlanchetDetail *planchets, + TALER_MERCHANT_TipPickup2Callback pickup_cb, + void *pickup_cb_cls); + + +/** + * Cancel a pending /tip-pickup request. + * + * @param tp handle from the operation to cancel + */ +void +TALER_MERCHANT_tip_pickup2_cancel ( + struct TALER_MERCHANT_TipPickup2Operation *tp); + + /* ********************** /check-payment ************************* */ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 3d427771..1561f57d 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -31,6 +31,7 @@ libtalermerchant_la_SOURCES = \ merchant_api_refund_increase.c \ merchant_api_tip_authorize.c \ merchant_api_tip_pickup.c \ + merchant_api_tip_pickup2.c \ merchant_api_tip_query.c \ merchant_api_track_transaction.c \ merchant_api_track_transfer.c diff --git a/src/lib/merchant_api_tip_pickup.c b/src/lib/merchant_api_tip_pickup.c index f402209f..6e48f169 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 Taler Systems SA + Copyright (C) 2014-2017, 2020 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 @@ -33,25 +33,32 @@ /** - * @brief A handle for tracking transactions. + * Data we keep per planchet. */ -struct TALER_MERCHANT_TipPickupOperation +struct PlanchetData { - /** - * The url for this request. + * Secrets of the planchet. */ - char *url; + struct TALER_PlanchetSecretsP ps; /** - * Minor context that holds body and headers. + * Denomination key we are withdrawing. */ - struct TALER_CURL_PostContext post_ctx; + struct TALER_EXCHANGE_DenomPublicKey pk; /** - * Handle for the request. + * Hash of the public key of the coin we are signing. */ - struct GNUNET_CURL_Job *job; + struct GNUNET_HashCode c_hash; +}; + + +/** + * Handle for a /tip-pickup operation. + */ +struct TALER_MERCHANT_TipPickupOperation +{ /** * Function to call with the result. @@ -64,162 +71,99 @@ struct TALER_MERCHANT_TipPickupOperation void *cb_cls; /** - * Reference to the execution context. + * Handle for the actual (internal) withdraw operation. */ - struct GNUNET_CURL_Context *ctx; + struct TALER_MERCHANT_TipPickup2Operation *tpo2; /** - * Expected number of planchets. + * Number of planchets/coins used for this operation. */ unsigned int num_planchets; + + /** + * Array of length @e num_planchets. + */ + struct PlanchetData *planchets; + }; /** - * We got a 200 response back from the exchange (or the merchant). - * Now we need to parse the response and if it is well-formed, - * call the callback (and set it to NULL afterwards). + * 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. * - * @param tpo handle of the original authorization operation - * @param json cryptographic proof returned by the exchange/merchant - * @return #GNUNET_OK if response is valid + * @param cls closure, a `struct TALER_MERCHANT_TipPickupOperation *` + * @param hr HTTP response details + * @param num_blind_sigs length of the @a reserve_sigs array, 0 on error + * @param blind_sigs array of blind signatures over the planchets, NULL on error */ -static int -check_ok (struct TALER_MERCHANT_TipPickupOperation *tpo, - const json_t *json) +static void +pickup_done_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + unsigned int num_blind_sigs, + const struct TALER_MERCHANT_BlindSignature *blind_sigs) { - struct TALER_ReservePublicKeyP reserve_pub; - json_t *ja; - unsigned int ja_len; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_pub", &reserve_pub), - GNUNET_JSON_spec_json ("reserve_sigs", &ja), - GNUNET_JSON_spec_end () - }; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = MHD_HTTP_OK, - .reply = json - }; + struct TALER_MERCHANT_TipPickupOperation *tp = cls; - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - ja_len = json_array_size (ja); - if (ja_len != tpo->num_planchets) + tp->tpo2 = NULL; + if (NULL == blind_sigs) { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; + tp->cb (tp->cb_cls, + hr, + 0, + NULL); + TALER_MERCHANT_tip_pickup_cancel (tp); + return; } { - struct TALER_ReserveSignatureP reserve_sigs[ja_len]; - - for (unsigned int i = 0; i<ja_len; i++) + struct TALER_DenominationSignature sigs[num_blind_sigs]; + int ok; + + ok = GNUNET_OK; + memset (sigs, + 0, + sizeof (sigs)); + for (unsigned int i = 0; i<num_blind_sigs; i++) { - json_t *pj = json_array_get (ja, i); - - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_sig", &reserve_sigs[i]), - GNUNET_JSON_spec_end () - }; + struct TALER_FreshCoin fc; if (GNUNET_OK != - GNUNET_JSON_parse (pj, - ispec, - NULL, NULL)) + TALER_planchet_to_coin (&tp->planchets[i].pk.key, + blind_sigs[i].blind_sig, + &tp->planchets[i].ps, + &tp->planchets[i].c_hash, + &fc)) { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; + ok = GNUNET_SYSERR; + break; } + sigs[i] = fc.sig; } - tpo->cb (tpo->cb_cls, - &hr, - &reserve_pub, - ja_len, - reserve_sigs); - tpo->cb = NULL; /* do not call twice */ - } - GNUNET_JSON_parse_free (spec); - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /track/transaction request. - * - * @param cls the `struct TALER_MERCHANT_TipPickupOperation` - * @param response_code HTTP response code, 0 on error - * @param json response body, NULL if not in JSON - */ -static void -handle_tip_pickup_finished (void *cls, - long response_code, - const void *response) -{ - struct TALER_MERCHANT_TipPickupOperation *tpo = cls; - const json_t *json = response; - struct TALER_MERCHANT_HttpResponse hr = { - .http_status = (unsigned int) response_code, - .reply = json - }; - - tpo->job = NULL; - switch (response_code) - { - case MHD_HTTP_OK: - if (GNUNET_OK != check_ok (tpo, - json)) + if (GNUNET_OK == ok) { - GNUNET_break_op (0); - hr.http_status = 0; - hr.ec = TALER_EC_INVALID_RESPONSE; + tp->cb (tp->cb_cls, + hr, + num_blind_sigs, + sigs); } - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_CONFLICT: - /* legal, can happen if we pickup a tip twice... */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - case MHD_HTTP_NOT_FOUND: - /* legal, can happen if tip ID is unknown */ - hr.ec = TALER_JSON_get_error_code (json); - hr.hint = TALER_JSON_get_error_hint (json); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - TALER_MERCHANT_parse_error_details_ (json, - response_code, - &hr); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) hr.ec); - break; - } - if (NULL != tpo->cb) - { - tpo->cb (tpo->cb_cls, - &hr, - NULL, - 0, - NULL); - tpo->cb = NULL; + else + { + struct TALER_MERCHANT_HttpResponse hrx = { + .reply = hr->reply, + .http_status = 0, + .ec = TALER_EC_TIP_PICKUP_UNBLIND_FAILURE + }; + + tp->cb (tp->cb_cls, + &hrx, + 0, + NULL); + } + for (unsigned int i = 0; i<num_blind_sigs; i++) + if (NULL != sigs[i].rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature); } - TALER_MERCHANT_tip_pickup_cancel (tpo); + TALER_MERCHANT_tip_pickup_cancel (tp); } @@ -230,8 +174,8 @@ handle_tip_pickup_finished (void *cls, * @param ctx execution context * @param backend_url base URL of the merchant backend * @param tip_id unique identifier for the tip - * @param num_planches number of planchets provided in @a planchets - * @param planchets array of planchets to be signed into existence for the tip + * @param num_planches number of planchets provided in @a pds + * @param pds array of planchet secrets to be signed into existence for the tip * @param pickup_cb callback which will work the response gotten from the backend * @param pickup_cb_cls closure to pass to @a pickup_cb * @return handle for this operation, NULL upon errors @@ -241,122 +185,83 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx, const char *backend_url, const struct GNUNET_HashCode *tip_id, unsigned int num_planchets, - struct TALER_PlanchetDetail *planchets, + const struct TALER_MERCHANT_PlanchetData *pds, TALER_MERCHANT_TipPickupCallback pickup_cb, void *pickup_cb_cls) { - struct TALER_MERCHANT_TipPickupOperation *tpo; - CURL *eh; - json_t *pa; - json_t *tp_obj; + struct TALER_MERCHANT_TipPickupOperation *tp; + struct TALER_PlanchetDetail details[GNUNET_NZL (num_planchets)]; if (0 == num_planchets) { GNUNET_break (0); return NULL; } - pa = json_array (); + tp = GNUNET_new (struct TALER_MERCHANT_TipPickupOperation); + GNUNET_array_grow (tp->planchets, + tp->num_planchets, + num_planchets); for (unsigned int i = 0; i<num_planchets; i++) { - const struct TALER_PlanchetDetail *planchet = &planchets[i]; - json_t *p; - - p = json_pack ("{" - " s:o," /* denom_pub_hash */ - " s:o," /* coin_ev */ - "}", - "denom_pub_hash", GNUNET_JSON_from_data_auto ( - &planchet->denom_pub_hash), - "coin_ev", GNUNET_JSON_from_data (planchet->coin_ev, - planchet->coin_ev_size)); - if (NULL == p) + tp->planchets[i].ps = pds[i].ps; + if (GNUNET_OK != + TALER_planchet_prepare (&pds[i].pk->key, + &tp->planchets[i].ps, + &tp->planchets[i].c_hash, + &details[i])) { GNUNET_break (0); - json_decref (pa); + GNUNET_array_grow (tp->planchets, + tp->num_planchets, + 0); + GNUNET_free (tp); return NULL; } - if (0 != - json_array_append_new (pa, - p)) - { - GNUNET_break (0); - json_decref (pa); - return NULL; - } - } - tp_obj = json_pack ("{" - " s:o," /* tip_id */ - " s:o," /* planchets */ - "}", - "tip_id", GNUNET_JSON_from_data_auto (tip_id), - "planchets", pa); - if (NULL == tp_obj) - { - GNUNET_break (0); - return NULL; } - tpo = GNUNET_new (struct TALER_MERCHANT_TipPickupOperation); - tpo->num_planchets = num_planchets; - tpo->ctx = ctx; - tpo->cb = pickup_cb; - tpo->cb_cls = pickup_cb_cls; - - tpo->url = TALER_url_join (backend_url, - "tip-pickup", - NULL); - if (NULL == tpo->url) + for (unsigned int i = 0; i<num_planchets; i++) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not construct request URL.\n"); - json_decref (tp_obj); - GNUNET_free (tpo); - return NULL; + tp->planchets[i].pk = *pds[i].pk; + tp->planchets[i].pk.key.rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_dup (pds[i].pk->key.rsa_public_key); } - eh = curl_easy_init (); - if (GNUNET_OK != TALER_curl_easy_post (&tpo->post_ctx, - eh, - tp_obj)) + 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); + if (NULL == tp->tpo2) { GNUNET_break (0); - json_decref (tp_obj); - GNUNET_free (tpo); + TALER_MERCHANT_tip_pickup_cancel (tp); return NULL; } - json_decref (tp_obj); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Requesting URL '%s'\n", - tpo->url); - - GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, - CURLOPT_URL, - tpo->url)); - tpo->job = GNUNET_CURL_job_add2 (ctx, - eh, - tpo->post_ctx.headers, - &handle_tip_pickup_finished, - tpo); - return tpo; + return tp; } /** - * Cancel a /track/transaction request. This function cannot be used - * on a request handle if a response is already served for it. + * Cancel a pending /tip-pickup request * - * @param tpo handle to the tracking operation being cancelled + * @param tp handle from the operation to cancel */ void -TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupOperation *tpo) +TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupOperation *tp) { - if (NULL != tpo->job) + for (unsigned int i = 0; i<tp->num_planchets; i++) + GNUNET_CRYPTO_rsa_public_key_dup (tp->planchets[i].pk.key.rsa_public_key); + GNUNET_array_grow (tp->planchets, + tp->num_planchets, + 0); + if (NULL != tp->tpo2) { - GNUNET_CURL_job_cancel (tpo->job); - tpo->job = NULL; + TALER_MERCHANT_tip_pickup2_cancel (tp->tpo2); + tp->tpo2 = NULL; } - TALER_curl_easy_post_finished (&tpo->post_ctx); - GNUNET_free (tpo->url); - GNUNET_free (tpo); + GNUNET_free (tp); } diff --git a/src/lib/merchant_api_tip_pickup2.c b/src/lib/merchant_api_tip_pickup2.c new file mode 100644 index 00000000..751d1267 --- /dev/null +++ b/src/lib/merchant_api_tip_pickup2.c @@ -0,0 +1,363 @@ +/* + This file is part of TALER + Copyright (C) 2014-2017, 2020 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 + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file lib/merchant_api_tip_pickup2.c + * @brief Implementation of the /tip-pickup request of the merchant's HTTP API + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> +#include <taler/taler_curl_lib.h> + + +/** + * @brief A handle for tracking transactions. + */ +struct TALER_MERCHANT_TipPickup2Operation +{ + + /** + * 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_MERCHANT_TipPickup2Callback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Expected number of planchets. + */ + unsigned int num_planchets; +}; + + +/** + * We got a 200 response back from the exchange (or the merchant). + * Now we need to parse the response and if it is well-formed, + * call the callback (and set it to NULL afterwards). + * + * @param tpo handle of the original authorization operation + * @param json cryptographic proof returned by the exchange/merchant + * @return #GNUNET_OK if response is valid + */ +static int +check_ok (struct TALER_MERCHANT_TipPickup2Operation *tpo, + const json_t *json) +{ + json_t *ja; + unsigned int ja_len; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("blind_sigs", &ja), + GNUNET_JSON_spec_end () + }; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = MHD_HTTP_OK, + .reply = json + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + ja_len = json_array_size (ja); + if (ja_len != tpo->num_planchets) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + { + struct TALER_MERCHANT_BlindSignature mblind_sigs[ja_len]; + struct GNUNET_CRYPTO_RsaSignature *blind_sigs[ja_len]; + + for (unsigned int i = 0; i<ja_len; i++) + { + json_t *pj = json_array_get (ja, i); + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_rsa_signature ("blind_sig", &blind_sigs[i]), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (pj, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + mblind_sigs[i].blind_sig = blind_sigs[i]; + } + tpo->cb (tpo->cb_cls, + &hr, + ja_len, + mblind_sigs); + for (unsigned int i = 0; i<ja_len; i++) + GNUNET_CRYPTO_rsa_signature_free (blind_sigs[i]); + tpo->cb = NULL; /* do not call twice */ + } + GNUNET_JSON_parse_free (spec); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /track/transaction request. + * + * @param cls the `struct TALER_MERCHANT_TipPickupOperation` + * @param response_code HTTP response code, 0 on error + * @param json response body, NULL if not in JSON + */ +static void +handle_tip_pickup_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_TipPickup2Operation *tpo = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tpo->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + if (GNUNET_OK != check_ok (tpo, + json)) + { + GNUNET_break_op (0); + hr.http_status = 0; + hr.ec = TALER_EC_INVALID_RESPONSE; + } + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + /* legal, can happen if we pickup a tip twice... */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_NOT_FOUND: + /* legal, can happen if tip ID is unknown */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + if (NULL != tpo->cb) + { + tpo->cb (tpo->cb_cls, + &hr, + 0, + NULL); + tpo->cb = NULL; + } + TALER_MERCHANT_tip_pickup2_cancel (tpo); +} + + +/** + * Issue a /tip-pickup request to the backend. Informs the backend + * that a customer wants to pick up a tip. + * + * @param ctx execution context + * @param backend_url base URL of the merchant backend + * @param tip_id unique identifier for the tip + * @param num_planches number of planchets provided in @a planchets + * @param planchets array of planchets to be signed into existence for the tip + * @param pickup_cb callback which will work the response gotten from the backend + * @param pickup_cb_cls closure to pass to @a pickup_cb + * @return handle for this operation, NULL upon errors + */ +struct TALER_MERCHANT_TipPickup2Operation * +TALER_MERCHANT_tip_pickup2 (struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct GNUNET_HashCode *tip_id, + unsigned int num_planchets, + struct TALER_PlanchetDetail *planchets, + TALER_MERCHANT_TipPickup2Callback pickup_cb, + void *pickup_cb_cls) +{ + struct TALER_MERCHANT_TipPickup2Operation *tpo; + CURL *eh; + json_t *pa; + json_t *tp_obj; + + if (0 == num_planchets) + { + GNUNET_break (0); + return NULL; + } + pa = json_array (); + for (unsigned int i = 0; i<num_planchets; i++) + { + const struct TALER_PlanchetDetail *planchet = &planchets[i]; + json_t *p; + + p = json_pack ("{" + " s:o," /* denom_pub_hash */ + " s:o," /* coin_ev */ + "}", + "denom_pub_hash", GNUNET_JSON_from_data_auto ( + &planchet->denom_pub_hash), + "coin_ev", GNUNET_JSON_from_data (planchet->coin_ev, + planchet->coin_ev_size)); + if (NULL == p) + { + GNUNET_break (0); + json_decref (pa); + return NULL; + } + if (0 != + json_array_append_new (pa, + p)) + { + GNUNET_break (0); + json_decref (pa); + return NULL; + } + } + tp_obj = json_pack ("{" + " s:o," /* tip_id */ + " s:o," /* planchets */ + "}", + "tip_id", GNUNET_JSON_from_data_auto (tip_id), + "planchets", pa); + if (NULL == tp_obj) + { + GNUNET_break (0); + return NULL; + } + tpo = GNUNET_new (struct TALER_MERCHANT_TipPickup2Operation); + tpo->num_planchets = num_planchets; + tpo->ctx = ctx; + tpo->cb = pickup_cb; + tpo->cb_cls = pickup_cb_cls; + + tpo->url = TALER_url_join (backend_url, + "tip-pickup", + NULL); + if (NULL == tpo->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (tp_obj); + GNUNET_free (tpo); + return NULL; + } + eh = curl_easy_init (); + if (GNUNET_OK != TALER_curl_easy_post (&tpo->post_ctx, + eh, + tp_obj)) + { + GNUNET_break (0); + json_decref (tp_obj); + GNUNET_free (tpo); + return NULL; + } + json_decref (tp_obj); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tpo->url); + + GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, + CURLOPT_URL, + tpo->url)); + tpo->job = GNUNET_CURL_job_add2 (ctx, + eh, + tpo->post_ctx.headers, + &handle_tip_pickup_finished, + tpo); + return tpo; +} + + +/** + * Cancel a /track/transaction request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param tpo handle to the tracking operation being cancelled + */ +void +TALER_MERCHANT_tip_pickup2_cancel ( + struct TALER_MERCHANT_TipPickup2Operation *tpo) +{ + if (NULL != tpo->job) + { + GNUNET_CURL_job_cancel (tpo->job); + tpo->job = NULL; + } + TALER_curl_easy_post_finished (&tpo->post_ctx); + GNUNET_free (tpo->url); + GNUNET_free (tpo); +} + + +/* end of merchant_api_tip_pickup2.c */ diff --git a/src/lib/testing_api_cmd_tip_pickup.c b/src/lib/testing_api_cmd_tip_pickup.c index 21045a22..dd963520 100644 --- a/src/lib/testing_api_cmd_tip_pickup.c +++ b/src/lib/testing_api_cmd_tip_pickup.c @@ -102,12 +102,6 @@ struct TipPickupState struct TALER_PlanchetSecretsP *psa; /** - * Temporary data structure of @e num_coins entries for the - * withdraw operations. - */ - struct WithdrawHandle *withdraws; - - /** * Set (by the interpreter) to an array of @a num_coins * signatures created from the (successful) tip operation. */ @@ -122,108 +116,21 @@ struct TipPickupState /** - * Internal withdraw handle used when withdrawing tips. - */ -struct WithdrawHandle -{ - /** - * Withdraw operation this handle represents. - */ - struct TALER_EXCHANGE_WithdrawHandle *wsh; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Offset of this withdraw operation in the current - * @e is command. - */ - unsigned int off; - - /** - * Internal state of the "pickup" CMD. - */ - struct TipPickupState *tps; -}; - - -/** - * This callback handles the response of a withdraw operation - * from the exchange, that is the final step in getting the tip. - * - * @param cls closure, a `struct WithdrawHandle *` - * @param hr HTTP response details - * @param sig signature over the coin, NULL on error - */ -static void -pickup_withdraw_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_DenominationSignature *sig) -{ - struct WithdrawHandle *wh = cls; - struct TALER_TESTING_Interpreter *is = wh->is; - - struct TipPickupState *tps = wh->tps; - - wh->wsh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Withdraw operation %u completed with %u (%d)\n", - wh->off, - hr->http_status, - hr->ec); - GNUNET_assert (wh->off < tps->num_coins); - if ( (MHD_HTTP_OK != hr->http_status) || - (TALER_EC_NONE != hr->ec) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s when withdrawing\n", - hr->http_status, - hr->ec, - TALER_TESTING_interpreter_get_current_label (is)); - TALER_TESTING_interpreter_fail (is); - return; - } - if (NULL == tps->sigs) - tps->sigs = GNUNET_new_array - (tps->num_coins, struct TALER_DenominationSignature); - - GNUNET_assert (NULL == tps->sigs[wh->off].rsa_signature); - tps->sigs[wh->off].rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - - for (unsigned int i = 0; i<tps->num_coins; i++) - if (NULL != tps->withdraws[wh->off].wsh) - return; - /* still some ops ongoing */ - - GNUNET_free (tps->withdraws); - tps->withdraws = NULL; - TALER_TESTING_interpreter_next (is); -} - - -/** * Callback for a /tip-pickup request, it mainly checks if * values returned from the backend are as expected, and if so * (and if the status was 200 OK) proceede with the withdrawal. * * @param cls closure * @param hr HTTP response - * @param reserve_pub public key of the reserve that made the - * @a reserve_sigs, NULL on error - * @param num_reserve_sigs length of the @a reserve_sigs array, + * @param num_sigs length of the @a sigs array, * 0 on error - * @param reserve_sigs array of signatures authorizing withdrawals, - * NULL on error + * @param sigs array of signatures over the coins, NULL on error */ static void pickup_cb (void *cls, const struct TALER_MERCHANT_HttpResponse *hr, - const struct TALER_ReservePublicKeyP *reserve_pub, - unsigned int num_reserve_sigs, - const struct TALER_ReserveSignatureP *reserve_sigs) + unsigned int num_sigs, + const struct TALER_DenominationSignature *sigs) { struct TipPickupState *tps = cls; @@ -248,40 +155,14 @@ pickup_cb (void *cls, TALER_TESTING_interpreter_next (tps->is); return; } - if (num_reserve_sigs != tps->num_coins) + if (num_sigs != tps->num_coins) TALER_TESTING_FAIL (tps->is); - - /* pickup successful, now withdraw! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Obtained %u signatures for withdrawal from picking up a tip\n", - num_reserve_sigs); - - GNUNET_assert (NULL == tps->withdraws); - tps->withdraws = GNUNET_new_array (num_reserve_sigs, - struct WithdrawHandle); - - for (unsigned int i = 0; i<num_reserve_sigs; i++) - { - struct WithdrawHandle *wh = &tps->withdraws[i]; - - wh->off = i; - wh->is = tps->is; - wh->tps = tps; - GNUNET_assert ( (NULL == wh->wsh) && - ( (NULL == tps->sigs) || - (NULL == tps->sigs[wh->off].rsa_signature) ) ); - wh->wsh = TALER_EXCHANGE_withdraw2 (tps->is->exchange, - tps->dks[i], - &reserve_sigs[i], - reserve_pub, - &tps->psa[i], - &pickup_withdraw_cb, - wh); - if (NULL == wh->wsh) - TALER_TESTING_FAIL (tps->is); - } - if (0 == num_reserve_sigs) - TALER_TESTING_interpreter_next (tps->is); + tps->sigs = GNUNET_new_array (tps->num_coins, + struct TALER_DenominationSignature); + for (unsigned int i = 0; i<num_sigs; i++) + tps->sigs[i].rsa_signature + = GNUNET_CRYPTO_rsa_signature_dup (sigs[i].rsa_signature); + TALER_TESTING_interpreter_next (tps->is); } @@ -341,7 +222,7 @@ tip_pickup_run (void *cls, tps->num_coins = num_planchets; { - struct TALER_PlanchetDetail planchets[num_planchets]; + struct TALER_MERCHANT_PlanchetData planchets[num_planchets]; tps->psa = GNUNET_new_array (num_planchets, struct TALER_PlanchetSecretsP); @@ -349,7 +230,6 @@ tip_pickup_run (void *cls, const struct TALER_EXCHANGE_DenomPublicKey *); tps->amounts_obj = GNUNET_new_array (num_planchets, struct TALER_Amount); - for (unsigned int i = 0; i<num_planchets; i++) { if (NULL == replay_cmd) @@ -379,12 +259,8 @@ tip_pickup_run (void *cls, TALER_TESTING_FAIL (is); tps->psa[i] = *ps; } - - if (GNUNET_OK != - TALER_planchet_prepare (&tps->dks[i]->key, - &tps->psa[i], - &planchets[i])) - TALER_TESTING_FAIL (is); + planchets[i].pk = tps->dks[i]; + planchets[i].ps = tps->psa[i]; } if (GNUNET_OK != TALER_TESTING_get_trait_tip_id (authorize_cmd, @@ -399,12 +275,6 @@ tip_pickup_run (void *cls, planchets, &pickup_cb, tps); - for (unsigned int i = 0; i<num_planchets; i++) - { - GNUNET_free (planchets[i].coin_ev); - planchets[i].coin_ev = NULL; - planchets[i].coin_ev_size = 0; - } GNUNET_assert (NULL != tps->tpo); } } @@ -426,7 +296,6 @@ tip_pickup_cleanup (void *cls, GNUNET_free_non_null (tps->amounts_obj); GNUNET_free_non_null (tps->dks); GNUNET_free_non_null (tps->psa); - GNUNET_free_non_null (tps->withdraws); if (NULL != tps->sigs) { for (unsigned int i = 0; i<tps->num_coins; i++) @@ -488,8 +357,7 @@ tip_pickup_traits (void *cls, /** - * Define a /tip-pickup CMD, equipped with the expected error - * code. + * Define a /tip-pickup CMD. * * @param label the command label * @param merchant_url base URL of the backend which will serve @@ -499,17 +367,13 @@ tip_pickup_traits (void *cls, * that offers a tip id to pick up. * @param amounts array of string-defined amounts that specifies * which denominations will be accepted for tipping. - * @param exchange connection handle to the exchange that will - * eventually serve the withdraw operation. - * @param ec expected Taler error code. */ struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_pickup_with_ec (const char *label, - const char *merchant_url, - unsigned int http_status, - const char *authorize_reference, - const char **amounts, - enum TALER_ErrorCode ec) +TALER_TESTING_cmd_tip_pickup (const char *label, + const char *merchant_url, + unsigned int http_status, + const char *authorize_reference, + const char **amounts) { struct TipPickupState *tps; @@ -518,7 +382,6 @@ TALER_TESTING_cmd_tip_pickup_with_ec (const char *label, tps->authorize_reference = authorize_reference; tps->amounts = amounts; tps->http_status = http_status; - tps->expected_ec = ec; { struct TALER_TESTING_Command cmd = { .cls = tps, @@ -534,7 +397,8 @@ TALER_TESTING_cmd_tip_pickup_with_ec (const char *label, /** - * Define a /tip-pickup CMD. + * Define a /tip-pickup CMD, equipped with the expected error + * code. * * @param label the command label * @param merchant_url base URL of the backend which will serve @@ -544,32 +408,29 @@ TALER_TESTING_cmd_tip_pickup_with_ec (const char *label, * that offers a tip id to pick up. * @param amounts array of string-defined amounts that specifies * which denominations will be accepted for tipping. + * @param exchange connection handle to the exchange that will + * eventually serve the withdraw operation. + * @param ec expected Taler error code. */ struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_pickup (const char *label, - const char *merchant_url, - unsigned int http_status, - const char *authorize_reference, - const char **amounts) +TALER_TESTING_cmd_tip_pickup_with_ec (const char *label, + const char *merchant_url, + unsigned int http_status, + const char *authorize_reference, + const char **amounts, + enum TALER_ErrorCode ec) { + struct TALER_TESTING_Command cmd; struct TipPickupState *tps; - tps = GNUNET_new (struct TipPickupState); - tps->merchant_url = merchant_url; - tps->authorize_reference = authorize_reference; - tps->amounts = amounts; - tps->http_status = http_status; - { - struct TALER_TESTING_Command cmd = { - .cls = tps, - .label = label, - .run = &tip_pickup_run, - .cleanup = &tip_pickup_cleanup, - .traits = &tip_pickup_traits - }; - - return cmd; - } + cmd = TALER_TESTING_cmd_tip_pickup (label, + merchant_url, + http_status, + authorize_reference, + amounts); + tps = cmd.cls; + tps->expected_ec = ec; + return cmd; } |