summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-12 20:46:18 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-12 20:46:18 +0200
commitc548a400ea1956d55c675e6a21fff46f6271455b (patch)
treee9bd4781ebf75ae9ca96843e2306da5788d49112
parent9e94110b6a2e5fdf51f0d00a129d48ab3213753c (diff)
downloadmerchant-c548a400ea1956d55c675e6a21fff46f6271455b.tar.gz
merchant-c548a400ea1956d55c675e6a21fff46f6271455b.tar.bz2
merchant-c548a400ea1956d55c675e6a21fff46f6271455b.zip
implement #6173
-rw-r--r--ChangeLog4
-rw-r--r--src/backend/taler-merchant-httpd_tip-pickup.c204
-rw-r--r--src/include/taler_merchant_service.h104
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/merchant_api_tip_pickup.c359
-rw-r--r--src/lib/merchant_api_tip_pickup2.c363
-rw-r--r--src/lib/testing_api_cmd_tip_pickup.c219
7 files changed, 772 insertions, 482 deletions
diff --git a/ChangeLog b/ChangeLog
index 4a61c55e..2ab8d28d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;
}