merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit e8f45018d3edf917a234c0841391ed4bc5cedf3e
parent f7baa358b5792bb0443c468171889c485dbf9371
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 19 Jan 2018 01:20:30 +0100

work on #4939: load multiple wire methods per instance

Diffstat:
Msrc/backend/taler-merchant-httpd.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/backend/taler-merchant-httpd.h | 51++++++++++++++++++++++++++++++++++++++-------------
Msrc/backend/taler-merchant-httpd_pay.c | 59+++++++++++++++++++++++++++++++++++++----------------------
Msrc/backend/taler-merchant-httpd_proposal.c | 19++++++++++++++++---
4 files changed, 224 insertions(+), 90 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014-2017 INRIA + (C) 2014-2018 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -317,8 +317,17 @@ hashmap_free (void *cls, void *value) { struct MerchantInstance *mi = value; + struct WireMethod *wm; + + while (NULL != (wm = (mi->wm_head))) + { + GNUNET_CONTAINER_DLL_remove (mi->wm_head, + mi->wm_tail, + wm); + json_decref (wm->j_wire); + GNUNET_free (wm); + } - json_decref (mi->j_wire); GNUNET_free (mi->id); GNUNET_free (mi->keyfile); GNUNET_free (mi->name); @@ -571,29 +580,127 @@ locations_iterator_cb (void *cls, /** + * Closure for the #wireformat_iterator_cb(). + */ +struct WireFormatIteratorContext +{ + /** + * The global iteration context. + */ + struct IterateInstancesCls *iic; + + /** + * The merchant instance we are currently building. + */ + struct MerchantInstance *mi; +}; + + +/** + * Callback that looks for 'merchant-instance-wireformat-*' sections, + * and populates our wire method according to the data + * + * @param cls closure with a `struct WireFormatIteratorContext *` + * @section section name this callback gets + */ +static void +wireformat_iterator_cb (void *cls, + const char *section) +{ + struct WireFormatIteratorContext *wfic = cls; + struct MerchantInstance *mi = wfic->mi; + struct IterateInstancesCls *iic = wfic->iic; + char *instance_wiresection; + struct WireMethod *wm; + json_t *type; + char *emsg; + + GNUNET_asprintf (&instance_wiresection, + "merchant-instance-wireformat-%s", + mi->id); + if (0 != strncmp (section, + instance_wiresection, + strlen (instance_wiresection))) + { + GNUNET_free (instance_wiresection); + return; + } + GNUNET_free (instance_wiresection); + + wm = GNUNET_new (struct WireMethod); + /* FIXME: maybe use sorting to address #4939-12806? */ + GNUNET_CONTAINER_DLL_insert (mi->wm_head, + mi->wm_tail, + wm); + wm->j_wire = iic->plugin->get_wire_details (iic->plugin->cls, + iic->config, + section); + if ( (NULL == (type = json_object_get (wm->j_wire, + "type"))) || + (! json_is_string (type)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed wireformat: lacks type\n"); + iic->ret |= GNUNET_SYSERR; + return; + } + wm->wire_method = json_string_value (type); + + if (TALER_EC_NONE != + iic->plugin->wire_validate (iic->plugin->cls, + wm->j_wire, + NULL, + &emsg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed wireformat: %s\n", + emsg); + GNUNET_free (emsg); + iic->ret |= GNUNET_SYSERR; + return; + } + + if (GNUNET_OK != + TALER_JSON_hash (wm->j_wire, + &wm->h_wire)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to hash wireformat\n"); + iic->ret |= GNUNET_SYSERR; + } + +#define EXTRADEBUG +#ifdef EXTRADEBUGG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found wireformat instance:\n"); + json_dumpf (wm->j_wire, + stdout, + 0); + printf ("\n"); +#endif +} + + +/** * Callback that looks for 'merchant-instance-*' sections, * and populates accordingly each instance's data * - * @param cls closure + * @param cls closure of type `struct IterateInstancesCls` * @section section name this callback gets */ static void instances_iterator_cb (void *cls, const char *section) { - char *substr; + struct IterateInstancesCls *iic = cls; char *token; - char *instance_wiresection; struct MerchantInstance *mi; - struct IterateInstancesCls *iic; struct GNUNET_CRYPTO_EddsaPrivateKey *pk; /* used as hashmap keys */ struct GNUNET_HashCode h_pk; struct GNUNET_HashCode h_id; - json_t *type; - char *emsg; + const char *substr; - iic = cls; substr = strstr (section, "merchant-instance-"); @@ -616,7 +723,6 @@ instances_iterator_cb (void *cls, "Extracted token: %s\n", token + 1); mi = GNUNET_new (struct MerchantInstance); - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (iic->config, section, @@ -709,61 +815,33 @@ instances_iterator_cb (void *cls, GNUNET_free (pk); mi->id = GNUNET_strdup (token + 1); - if (0 == strcmp ("default", mi->id)) + if (0 == strcasecmp ("default", + mi->id)) iic->default_instance = GNUNET_YES; - GNUNET_asprintf (&instance_wiresection, - "merchant-instance-wireformat-%s", - mi->id); - mi->j_wire = iic->plugin->get_wire_details (iic->plugin->cls, - iic->config, - instance_wiresection); - GNUNET_free (instance_wiresection); - if ( (NULL == (type = json_object_get (mi->j_wire, - "type"))) || - (! json_is_string (type)) ) + /* Initialize wireformats */ { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Malformed wireformat: lacks type\n"); - iic->ret |= GNUNET_SYSERR; - } - mi->wire_method = json_string_value (type); + struct WireFormatIteratorContext wfic = { + .iic = iic, + .mi = mi + }; - if (TALER_EC_NONE != - iic->plugin->wire_validate (iic->plugin->cls, - mi->j_wire, - NULL, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Malformed wireformat: %s\n", - emsg); - GNUNET_free (emsg); - iic->ret |= GNUNET_SYSERR; + GNUNET_CONFIGURATION_iterate_sections (iic->config, + &wireformat_iterator_cb, + &wfic); } - if (GNUNET_OK != - TALER_JSON_hash (mi->j_wire, - &mi->h_wire)) + if (NULL == mi->wm_head) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to hash wireformat\n"); + "Failed to load wire formats for instance `%s'\n", + mi->id); iic->ret |= GNUNET_SYSERR; } -#define EXTRADEBUG -#ifdef EXTRADEBUGG - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found wireformat instance:\n"); - json_dumpf (mi->j_wire, stdout, 0); - printf ("\n"); -#endif GNUNET_CRYPTO_hash (mi->id, strlen (mi->id), &h_id); - GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), - &h_pk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (by_id_map, &h_id, @@ -774,6 +852,9 @@ instances_iterator_cb (void *cls, "Failed to put an entry into the 'by_id' hashmap\n"); iic->ret |= GNUNET_SYSERR; } + GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), + &h_pk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (by_kpub_map, &h_pk, @@ -892,7 +973,7 @@ iterate_instances (const struct GNUNET_CONFIGURATION_Handle *config, iic->plugin->library_name = lib_name; GNUNET_CONFIGURATION_iterate_sections (config, &instances_iterator_cb, - (void *) iic); + iic); if (GNUNET_NO == iic->default_instance) { diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 INRIA + Copyright (C) 2014-2018 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -72,6 +72,39 @@ struct IterateInstancesCls /** + * Supported wire method. Kept in a DLL. + */ +struct WireMethod +{ + /** + * Next entry in DLL. + */ + struct WireMethod *next; + + /** + * Previous entry in DLL. + */ + struct WireMethod *prev; + + /** + * Which wire method is @e j_wire using? Points into @e j_wire. + */ + const char *wire_method; + + /** + * Wire details for this instance + */ + struct json_t *j_wire; + + /** + * Hash of our wire format details as given in #j_wire. + */ + struct GNUNET_HashCode h_wire; + +}; + + +/** * Information that defines a merchant "instance". Tha4673t way, a single * backend can account for several merchants, as used to do in donation * shops @@ -96,23 +129,15 @@ struct MerchantInstance */ char *keyfile; - /* NOTE: the *_wire-fields should eventually be moved into a DLL - once we implement #4939 */ - /** - * Which wire method is @e j_wire using? + * Next entry in DLL. */ - const char *wire_method; - - /** - * Wire details for this instance - */ - struct json_t *j_wire; + struct WireMethod *wm_head; /** - * Hash of our wire format details as given in #j_wire. + * Previous entry in DLL. */ - struct GNUNET_HashCode h_wire; + struct WireMethod *wm_tail; /** * Merchant's private key diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c @@ -169,6 +169,12 @@ struct PayContext struct MerchantInstance *mi; /** + * What wire method (of the @e mi) was selected by the wallet? + * Set in #parse_pay(). + */ + struct WireMethod *wm; + + /** * Proposal data for the proposal that is being * payed for in this context. */ @@ -1078,7 +1084,8 @@ process_pay_with_exchange (void *cls, dc->refund_fee = denom_details->fee_refund; dc->wire_fee = *wire_fee; - GNUNET_assert (NULL != pc->mi->j_wire); + GNUNET_assert (NULL != pc->wm); + GNUNET_assert (NULL != pc->wm->j_wire); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timing for this payment, wire_deadline: %llu, refund_deadline: %llu\n", (unsigned long long) pc->wire_transfer_deadline.abs_value_us, @@ -1086,7 +1093,7 @@ process_pay_with_exchange (void *cls, dc->dh = TALER_EXCHANGE_deposit (mh, &dc->amount_with_fee, pc->wire_transfer_deadline, - pc->mi->j_wire, + pc->wm->j_wire, &pc->h_contract_terms, &dc->coin_pub, &dc->ub_sig, @@ -1133,7 +1140,7 @@ find_next_exchange (struct PayContext *pc) { pc->current_exchange = dc->exchange_url; pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange, - pc->mi->wire_method, + pc->wm->wire_method, &process_pay_with_exchange, pc); if (NULL == pc->fo) @@ -1298,6 +1305,7 @@ parse_pay (struct MHD_Connection *connection, GNUNET_JSON_spec_end() }; enum GNUNET_DB_QueryStatus qs; + const char *session_id; res = TMH_PARSE_json_data (connection, root, @@ -1308,11 +1316,10 @@ parse_pay (struct MHD_Connection *connection, return res; } - const char *session_id = json_string_value (json_object_get (root, - "session_id")); - if (NULL != session_id) { + session_id = json_string_value (json_object_get (root, + "session_id")); + if (NULL != session_id) pc->session_id = GNUNET_strdup (session_id); - } pc->order_id = GNUNET_strdup (order_id); GNUNET_assert (NULL == pc->contract_terms); qs = db->find_contract_terms (db->cls, @@ -1446,17 +1453,24 @@ parse_pay (struct MHD_Connection *connection, } } - /* NOTE: In the future, iterate over all wire hashes - available to a given instance here! (#4939) */ - if (0 != memcmp (&pc->h_wire, - &pc->mi->h_wire, - sizeof (struct GNUNET_HashCode))) + /* find wire method */ { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return TMH_RESPONSE_reply_internal_error (connection, - TALER_EC_PAY_WIRE_HASH_UNKNOWN, - "Did not find matching wire details"); + struct WireMethod *wm; + + wm = pc->mi->wm_head; + while (0 != memcmp (&pc->h_wire, + &wm->h_wire, + sizeof (struct GNUNET_HashCode))) + wm = wm->next; + if (NULL == wm) + { + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_PAY_WIRE_HASH_UNKNOWN, + "Did not find matching wire details"); + } + pc->wm = wm; } /* parse optional details */ @@ -1866,12 +1880,13 @@ begin_transaction (struct PayContext *pc) "hint", "Merchant database error")); return; } + if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && - ( (0 != memcmp (&h_xwire, - &pc->mi->h_wire, - sizeof (struct GNUNET_HashCode))) || - (xtimestamp.abs_value_us != pc->timestamp.abs_value_us) || + ( (xtimestamp.abs_value_us != pc->timestamp.abs_value_us) || (xrefund.abs_value_us != pc->refund_deadline.abs_value_us) || + (0 != memcmp (&h_xwire, + &pc->h_wire, + sizeof (struct GNUNET_HashCode))) || (0 != TALER_amount_cmp (&xtotal_amount, &pc->amount) ) ) ) { @@ -1917,7 +1932,7 @@ begin_transaction (struct PayContext *pc) qs_st = db->store_transaction (db->cls, &pc->h_contract_terms, &pc->mi->pubkey, - &pc->mi->h_wire, + &pc->h_wire, pc->timestamp, pc->refund_deadline, &pc->amount); diff --git a/src/backend/taler-merchant-httpd_proposal.c b/src/backend/taler-merchant-httpd_proposal.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014, 2015, 2016, 2017 INRIA + (C) 2014, 2015, 2016, 2018 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -157,6 +157,7 @@ proposal_put (struct MHD_Connection *connection, }; enum GNUNET_DB_QueryStatus qs; const char *instance; + struct WireMethod *wm; /* Add order_id if it doesn't exist. */ if (NULL == @@ -387,12 +388,24 @@ proposal_put (struct MHD_Connection *connection, json_object_set (order, "auditors", j_auditors); + /* TODO (#4939-12806): add proper mechanism for selection of wire method(s) by merchant! */ + wm = mi->wm_head; + + if (NULL == wm) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No wire method available for specified instance\n"); + GNUNET_JSON_parse_free (spec); + return TMH_RESPONSE_reply_not_found (connection, + TALER_EC_CONTRACT_INSTANCE_UNKNOWN, + "No wire method configured for instance"); + } json_object_set_new (order, "H_wire", - GNUNET_JSON_from_data_auto (&mi->h_wire)); + GNUNET_JSON_from_data_auto (&wm->h_wire)); json_object_set_new (order, "wire_method", - json_string (mi->wire_method)); + json_string (wm->wire_method)); json_object_set_new (order, "merchant_pub", GNUNET_JSON_from_data_auto (&mi->pubkey));