[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] [taler-merchant] 02/02: work on /tip-pickup implemenation (
From: |
gnunet |
Subject: |
[GNUnet-SVN] [taler-merchant] 02/02: work on /tip-pickup implemenation (WiP) |
Date: |
Thu, 02 Nov 2017 14:06:05 +0100 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository merchant.
commit e1ea7e3cefda083e9785d1b223c8d996cd384750
Author: Christian Grothoff <address@hidden>
AuthorDate: Thu Nov 2 14:05:57 2017 +0100
work on /tip-pickup implemenation (WiP)
---
src/backend/taler-merchant-httpd_tip-pickup.c | 448 +++++++++++++++++++-------
1 file changed, 336 insertions(+), 112 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_tip-pickup.c
b/src/backend/taler-merchant-httpd_tip-pickup.c
index 2a49506..c735405 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -22,6 +22,7 @@
#include <microhttpd.h>
#include <jansson.h>
#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_mhd.h"
#include "taler-merchant-httpd_parsing.h"
@@ -38,10 +39,12 @@
*/
struct PlanchetDetail
{
+
/**
- * Hash of the denomination public key.
+ * The complete withdraw request that we are building to sign.
+ * Built incrementally during the processing of the request.
*/
- struct GNUNET_HashCode denom_pub_hash;
+ struct TALER_WithdrawRequestPS wr;
/**
* Blinded coin (see GNUNET_CRYPTO_rsa_blind()). Note: is malloc()'ed!
@@ -52,10 +55,114 @@ struct PlanchetDetail
* Number of bytes in @a coin_ev.
*/
size_t coin_ev_size;
+
+};
+
+
+/**
+ * Information we keep for individual calls
+ * to requests that parse JSON, but keep no other state.
+ */
+struct PickupContext
+{
+
+ /**
+ * This field MUST be first.
+ * FIXME: Explain why!
+ */
+ struct TM_HandlerContext hc;
+
+ /**
+ * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
+ */
+
+ void *json_parse_context;
+
+ /**
+ * URI of the exchange this tip uses.
+ */
+ char *exchange_uri;
+
+ /**
+ * Operation we run to find the exchange (and get its /keys).
+ */
+ struct TMH_EXCHANGES_FindOperation *fo;
+
+ /**
+ * Array of planchets of length @e planchets_len.
+ */
+ struct PlanchetDetail *planchets;
+
+ /**
+ * Tip ID that was supplied by the client.
+ */
+ struct GNUNET_HashCode tip_id;
+
+ /**
+ * Unique identifier for the pickup operation, used to detect
+ * duplicate requests (retries).
+ */
+ struct GNUNET_HashCode pickup_id;
+
+ /**
+ * Total value of the coins we are withdrawing.
+ */
+ struct TALER_Amount total;
+
+ /**
+ * Length of @e planchets.
+ */
+ unsigned int planchets_len;
+
+ /**
+ * Error code, #TALER_EC_NONE as long as all is fine.
+ */
+ enum TALER_ErrorCode ec;
+
+ /**
+ * HTTP status code to return in combination with @e ec
+ * if @e ec is not #TALER_EC_NONE.
+ */
+ unsigned int response_code;
+
+ /**
+ * Human-readable error hint to return.
+ * if @e ec is not #TALER_EC_NONE.
+ */
+ const char *error_hint;
+
};
/**
+ * Custom cleanup routine for a `struct PickupContext`.
+ *
+ * @param hc the `struct PickupContext` to clean up.
+ */
+static void
+pickup_cleanup (struct TM_HandlerContext *hc)
+{
+ struct PickupContext *pc = (struct PickupContext *) hc;
+
+ if (NULL != pc->planchets)
+ {
+ for (unsigned int i=0;i<pc->planchets_len;i++)
+ GNUNET_free_non_null (pc->planchets[i].coin_ev);
+ GNUNET_free (pc->planchets);
+ pc->planchets = NULL;
+ }
+ if (NULL != pc->fo)
+ {
+ TMH_EXCHANGES_find_exchange_cancel (pc->fo);
+ pc->fo = NULL;
+ }
+ TMH_PARSE_post_cleanup_callback (pc->json_parse_context);
+ GNUNET_free_non_null (pc->exchange_uri);
+ GNUNET_free (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
@@ -63,112 +170,223 @@ struct PlanchetDetail
*
* @param connection MHD connection for sending the response
* @param tip_id which tip are we picking up
- * @param planchets what planchets are to be signed blindly
- * @param planchets_len length of the @a planchets array
- * @return #MHD_YES if a response was generated, #MHD_NO if
+ * @param pc pickup context
+ * @return #MHD_YES upon success, #MHD_NO if
* the connection ought to be dropped
*/
static int
-prepare_pickup (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *tip_id,
- const struct PlanchetDetail *planchets,
- unsigned int planchets_len)
+run_pickup (struct MHD_Connection *connection,
+ struct PickupContext *pc)
{
-#if 0
- char *exchange_uri;
- enum TALER_ErrorCode ec;
- struct GNUNET_HashCode pickup_id;
struct TALER_ReservePrivateKeyP reserve_priv;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ enum TALER_ErrorCode ec;
+ json_t *sigs;
- ec = db->lookup_exchange_by_tip (db->cls,
- tip_id,
- &exchange_uri);
- // FIXME: error handling
- // FIXME: obtain exchange handle -- asynchronously!? => API bad!
-
- // Then resolve hashes to DK pubs and amounts
- for (unsigned int i=0;i<planchets_len;i++)
+ if (TALER_EC_NONE != pc->ec)
{
+ return TMH_RESPONSE_reply_rc (connection,
+ pc->response_code,
+ pc->ec,
+ pc->error_hint);
}
- // Total up the amounts & compute pickup_id
-
ec = db->pickup_tip (db->cls,
- &total,
- tip_id,
- &pickup_id,
+ &pc->total,
+ &pc->tip_id,
+ &pc->pickup_id,
&reserve_priv);
- // FIXME: error handling
-
- // build and sign withdraw orders!
-
- // build final response...
-
if (TALER_EC_NONE != ec)
{
+ unsigned int response_code;
+
+ switch (ec)
+ {
+ default:
+ response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
/* FIXME: be more specific in choice of HTTP status code */
- return TMH_RESPONSE_reply_internal_error (connection,
- ec,
- "Database error approving tip");
+ return TMH_RESPONSE_reply_rc (connection,
+ response_code,
+ ec,
+ "Database error approving tip");
+ }
+ sigs = json_array ();
+ 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_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&reserve_priv.eddsa_priv,
+ &pd->wr.purpose,
+ &reserve_sig.eddsa_signature));
+ json_array_append_new (sigs,
+ GNUNET_JSON_from_data_auto (&reserve_sig));
}
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
- "{s:s}",
- "status", "ok");
-
-#endif
-
- return MHD_NO;
+ "{s:o, s:o}",
+ "reserve_pub",
GNUNET_JSON_from_data_auto (&reserve_pub),
+ "reserve_sigs", sigs);
}
/**
- * Information we keep for individual calls
- * to requests that parse JSON, but keep no other state.
- */
-struct TMH_JsonParseContext
-{
-
- /**
- * This field MUST be first.
- * FIXME: Explain why!
- */
- struct TM_HandlerContext hc;
-
- /**
- * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
- */
- void *json_parse_context;
-};
-
-
-/**
- * Custom cleanup routine for a `struct TMH_JsonParseContext`.
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.
*
- * @param hc the `struct TMH_JsonParseContext` to clean up.
+ * @param cls closure with the `struct PickupContext`
+ * @param eh handle to the exchange context
+ * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if
not available
+ * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
*/
static void
-json_parse_cleanup (struct TM_HandlerContext *hc)
+exchange_found_cb (void *cls,
+ struct TALER_EXCHANGE_Handle *eh,
+ const struct TALER_Amount *wire_fee,
+ int exchange_trusted)
{
- struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
+ struct PickupContext *pc = cls;
+ const struct TALER_EXCHANGE_Keys *keys;
+ struct GNUNET_HashContext *hc;
+ struct TALER_Amount total;
+ int ae;
- TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
- GNUNET_free (jpc);
+ if (NULL == eh)
+ {
+ pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_DOWN;
+ pc->error_hint = "failed to contact exchange, check URL";
+ pc->response_code = MHD_HTTP_FAILED_DEPENDENCY;
+ return;
+ }
+ keys = TALER_EXCHANGE_get_keys (eh);
+ if (NULL == keys)
+ {
+ pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_LACKED_KEYS;
+ pc->error_hint = "could not obtain denomination keys from exchange, check
URL";
+ pc->response_code = MHD_HTTP_FAILED_DEPENDENCY;
+ return;
+ }
+
+ ae = GNUNET_NO;
+ memset (&total,
+ 0,
+ sizeof (total));
+ hc = GNUNET_CRYPTO_hash_context_start ();
+ 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;
+
+ dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
+
&pd->wr.h_denomination_pub);
+ if (NULL == dk)
+ {
+ pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_LACKED_KEY;
+ pc->error_hint = "could not find matching denomination key";
+ pc->response_code = MHD_HTTP_NOT_FOUND;
+ GNUNET_CRYPTO_hash_context_abort (hc);
+ TMH_trigger_daemon ();
+ return;
+ }
+ GNUNET_CRYPTO_hash_context_read (hc,
+ &pd->wr.h_denomination_pub,
+ sizeof (struct GNUNET_HashCode));
+ GNUNET_CRYPTO_hash_context_read (hc,
+ pd->coin_ev,
+ pd->coin_ev_size);
+ if (GNUNET_OK !=
+ TALER_amount_add (&amount_with_fee,
+ &dk->value,
+ &dk->fee_withdraw))
+ ae = GNUNET_YES;
+ if (0 == i)
+ {
+ total = amount_with_fee;
+ }
+ else
+ {
+ if (GNUNET_OK !=
+ TALER_amount_add (&total,
+ &total,
+ &amount_with_fee))
+ 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);
+ if (GNUNET_YES == ae)
+ {
+ pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_AMOUNT_OVERFLOW;
+ pc->error_hint = "error computing total value of the tip";
+ pc->response_code = MHD_HTTP_BAD_REQUEST;
+ TMH_trigger_daemon ();
+ return;
+ }
+ pc->total = total;
+ TMH_trigger_daemon ();
}
/**
- * Free the memory used by the planchets in the @a pd array
- * (but not the @a pd array itself).
+ * Prepare (and eventually execute) a pickup. Finds the exchange
+ * handle we need for #run_pickup().
*
- * @param pd array of planchets
- * @param pd_len length of @a pd array
+ * @param connection MHD connection for sending the response
+ * @param tip_id which tip are we picking up
+ * @param pc pickup context
+ * @return #MHD_YES upon success, #MHD_NO if
+ * the connection ought to be dropped
*/
-static void
-free_planchets (struct PlanchetDetail *pd,
- unsigned int pd_len)
+static int
+prepare_pickup (struct MHD_Connection *connection,
+ struct PickupContext *pc)
{
- for (unsigned int i=0;i<pd_len;i++)
- GNUNET_free (pd[i].coin_ev);
+ enum TALER_ErrorCode ec;
+
+ ec = db->lookup_exchange_by_tip (db->cls,
+ &pc->tip_id,
+ &pc->exchange_uri);
+ if (TALER_EC_NONE != ec)
+ {
+ unsigned int response_code;
+
+ switch (ec)
+ {
+ case TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN:
+ response_code = MHD_HTTP_NOT_FOUND;
+ break;
+ default:
+ response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
+ return TMH_RESPONSE_reply_rc (connection,
+ response_code,
+ ec,
+ "Could not determine exchange URI for the
given tip id");
+
+ }
+ pc->fo = TMH_EXCHANGES_find_exchange (pc->exchange_uri,
+ NULL,
+ &exchange_found_cb,
+ pc);
+ if (NULL == pc->fo)
+ {
+ return TMH_RESPONSE_reply_rc (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_INTERNAL_INVARIANT_FAILURE,
+ "consult server logs");
+ }
+ return MHD_YES;
}
@@ -189,7 +407,7 @@ parse_planchet (struct MHD_Connection *connection,
int ret;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
- &pd->denom_pub_hash),
+ &pd->wr.h_denomination_pub),
GNUNET_JSON_spec_varsize ("coin_ev",
(void **) &pd->coin_ev,
&pd->coin_ev_size),
@@ -199,6 +417,13 @@ parse_planchet (struct MHD_Connection *connection,
ret = TMH_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;
}
@@ -229,22 +454,29 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh,
GNUNET_JSON_spec_json ("planchets", &planchets),
GNUNET_JSON_spec_end()
};
- struct TMH_JsonParseContext *ctx;
+ struct PickupContext *pc;
json_t *root;
- unsigned int num_planchets;
if (NULL == *connection_cls)
{
- ctx = GNUNET_new (struct TMH_JsonParseContext);
- ctx->hc.cc = &json_parse_cleanup;
- *connection_cls = ctx;
+ pc = GNUNET_new (struct PickupContext);
+ pc->hc.cc = &pickup_cleanup;
+ *connection_cls = pc;
}
else
{
- ctx = *connection_cls;
+ pc = *connection_cls;
+ }
+ if (NULL != pc->planchets)
+ {
+ /* This actually happens when we are called much later
+ after an exchange /keys' request to obtain the DKs
+ (and not for each request). */
+ return run_pickup (connection,
+ pc);
}
res = TMH_PARSE_post_json (connection,
- &ctx->json_parse_context,
+ &pc->json_parse_context,
upload_data,
upload_data_size,
&root);
@@ -264,42 +496,34 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh,
json_decref (root);
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
- num_planchets = json_array_size (planchets);
- if (num_planchets > 1024)
+ pc->planchets_len = json_array_size (planchets);
+ if (pc->planchets_len > 1024)
{
GNUNET_JSON_parse_free (spec);
json_decref (root);
- /* FIXME: define proper ec for this! */
- return TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s}",
- "status", "too many planchets");
+ return TMH_RESPONSE_reply_rc (connection,
+ MHD_HTTP_BAD_REQUEST,
+
TALER_EC_TIP_PICKUP_EXCHANGE_TOO_MANY_PLANCHETS,
+ "limit of 1024 planchets exceeded by
request");
}
+ pc->planchets = GNUNET_new_array (pc->planchets_len,
+ struct PlanchetDetail);
+ for (unsigned int i=0;i<pc->planchets_len;i++)
{
- struct PlanchetDetail pd[num_planchets];
-
- for (unsigned int i=0;i<num_planchets;i++)
+ if (GNUNET_OK !=
+ (res = parse_planchet (connection,
+ json_array_get (planchets,
+ i),
+ &pc->planchets[i])))
{
- if (GNUNET_OK !=
- (res = parse_planchet (connection,
- json_array_get (planchets,
- i),
- &pd[i])))
- {
- free_planchets (pd,
- i);
- GNUNET_JSON_parse_free (spec);
- json_decref (root);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
- res = prepare_pickup (connection,
- &tip_id,
- pd,
- num_planchets);
- free_planchets (pd,
- num_planchets);
}
+ pc->tip_id = tip_id;
+ res = prepare_pickup (connection,
+ pc);
GNUNET_JSON_parse_free (spec);
json_decref (root);
return res;
--
To stop receiving notification emails like this one, please contact
address@hidden