merchant

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

commit 6d0eb81a357adccd185b631b2793a840d779c162
parent daa8104795567076745f8062a0aaffa0d1eceb0f
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Mon, 26 Sep 2016 16:50:57 +0200

Merge branch 'receiver'

Conflicts:
	src/backend/taler-merchant-httpd.c

Diffstat:
Msrc/backend/taler-merchant-httpd.c | 167++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/backend/taler-merchant-httpd.h | 4++--
Msrc/backend/taler-merchant-httpd_contract.c | 1+
Msrc/backend/taler-merchant-httpd_pay.c | 4++++
Msrc/backend/taler-merchant-httpd_track-transaction.c | 19+++++++++++--------
Msrc/lib/merchant_api_track_transaction.c | 5+++--
Msrc/lib/test_merchant_api.c | 37++++++++++++++++++++++---------------
Msrc/lib/test_merchant_api.conf | 7++-----
Asrc/lib/tor_merchant.priv | 2++
9 files changed, 161 insertions(+), 85 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -46,10 +46,18 @@ #define UNIX_BACKLOG 500 /** - * NULL-terminated array of all merchants instances known - * by this backend + * Hashmap pointing at merchant instances by 'id'. An 'id' is + * just a string that identifies a merchant instance. When a frontend + * needs to specify an instance to the backend, it does so by 'id' */ -struct MerchantInstance **instances; +struct GNUNET_CONTAINER_MultiHashMap *by_id_map; + +/** + * Hashmap pointing at merchant instances by public key. This map + * is mainly used to check whether there is more than one instance + * using the same key + */ +struct GNUNET_CONTAINER_MultiHashMap *by_kpub_map; /** * The port we are running on @@ -225,6 +233,22 @@ url_handler (void *cls, /** + * Callback that frees all the elements in the hashmap + * + * @param cls closure + * @param key current key + * @param value current value + */ +int +hashmap_free (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + GNUNET_free (value); + return GNUNET_YES; +} + +/** * Shutdown task (magically invoked when the application is being * quit) * @@ -250,17 +274,14 @@ do_shutdown (void *cls) } TMH_EXCHANGES_done (); TMH_AUDITORS_done (); - if (NULL != instances) - { - unsigned int i; - for (i=0; NULL != instances[i]; i++) - { - json_decref (instances[i]->j_wire); - GNUNET_free (instances[i]->id); - GNUNET_free (instances[i]); - } - } + GNUNET_CONTAINER_multihashmap_iterate (by_id_map, + &hashmap_free, + NULL); + if (NULL != by_id_map) + GNUNET_CONTAINER_multihashmap_destroy (by_id_map); + if (NULL != by_kpub_map) + GNUNET_CONTAINER_multihashmap_destroy (by_kpub_map); } @@ -403,6 +424,9 @@ instances_iterator_cb (void *cls, 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; iic = cls; substr = strstr (section, "merchant-instance-"); @@ -454,7 +478,11 @@ instances_iterator_cb (void *cls, &mi->pubkey.eddsa_pub); GNUNET_free (pk); - /** To free or not to free **/ + /** + * FIXME: 'token' must NOT be freed, as it is handled by the + * gnunet_configuration facility. OTOH mi->id does need to be freed, + * because it is a duplicate. + */ mi->id = GNUNET_strdup (token + 1); if (0 == strcmp ("default", mi->id)) iic->default_instance = GNUNET_YES; @@ -485,14 +513,52 @@ instances_iterator_cb (void *cls, "Failed to hash wireformat\n"); iic->ret |= GNUNET_SYSERR; } - #if EXTRADEBUG + #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_array_append (instances, iic->current_index, mi); + 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, + mi, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to put an entry into the 'by_id' hashmap\n"); + iic->ret |= GNUNET_SYSERR; + } + #ifdef EXTRADEBUG + else { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Added element at %p, by by-id key %s of '%s' in hashmap\n", + mi, + GNUNET_h2s (&h_id), + mi->id); + GNUNET_assert (NULL != GNUNET_CONTAINER_multihashmap_get (by_id_map, + &h_id)); + } + #endif + + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (by_kpub_map, + &h_pk, + mi, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to put an entry into the 'by_kpub_map' hashmap\n"); + iic->ret |= GNUNET_SYSERR; + } } /** @@ -511,16 +577,29 @@ instances_iterator_cb (void *cls, struct MerchantInstance * get_instance (struct json_t *json) { - unsigned int i; struct json_t *receiver; + const char *receiver_str; + struct GNUNET_HashCode h_receiver; + struct MerchantInstance *ret; + /*FIXME who decrefs receiver?*/ if (NULL == (receiver = json_object_get (json, "receiver"))) receiver = json_string ("default"); - for (i=0; NULL != instances[i]; i++) - if (0 == strcmp (json_string_value (receiver), instances[i]->id)) - return instances[i]; - return NULL; + receiver_str = json_string_value (receiver); + GNUNET_CRYPTO_hash (receiver_str, + strlen (receiver_str), + &h_receiver); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Looking for by-id key %s of '%s' in hashmap\n", + GNUNET_h2s (&h_receiver), + receiver_str); + /* We're fine if that returns NULL, the calling routine knows how + to handle that */ + ret = GNUNET_CONTAINER_multihashmap_get (by_id_map, + &h_receiver); + GNUNET_break (NULL != ret); + return ret; } /** @@ -581,38 +660,6 @@ iterate_instances (const struct GNUNET_CONFIGURATION_Handle *config, GNUNET_PLUGIN_unload (lib_name, iic->plugin); GNUNET_free (lib_name); - GNUNET_array_append (instances, iic->current_index, NULL); - #if EXTRADEBUG - unsigned int i; - for (i=0; NULL != instances[i]; i++) - { - char *hash; - char *priv; - char *pub; - - hash = - GNUNET_STRINGS_data_to_string_alloc (&instances[i]->h_wire, - sizeof (struct GNUNET_HashCode)); - priv = - GNUNET_STRINGS_data_to_string_alloc (&instances[i]->privkey.eddsa_priv, - sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); - pub = - GNUNET_STRINGS_data_to_string_alloc (&instances[i]->pubkey.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "instances[%d]: id=%s,\nj_wire=%s,\nj_hash=%s,\npriv=%s,\npub=%s\n", - i, - instances[i]->id, - json_dumps (instances[i]->j_wire, JSON_INDENT (2)), - hash, - priv, - pub); - - GNUNET_free (hash); - GNUNET_free (priv); - GNUNET_free (pub); - } - #endif GNUNET_free (iic); return GNUNET_OK; @@ -666,6 +713,20 @@ run (void *cls, return; } + if (NULL == + (by_id_map = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO))) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (NULL == + (by_kpub_map = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO))) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (config, "merchant", diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h @@ -46,8 +46,8 @@ struct IterateInstancesCls { /** * Current index in the global array of #MerchantInstance - * types. Used by the callback in order to properly place - * the instance it is parsing + * types. Used by the callback in order to know which index + * is associated to the element being processed. */ unsigned int current_index; diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c @@ -259,6 +259,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh, GNUNET_assert (GNUNET_OK == TALER_JSON_hash (jcontract, &contract.h_contract)); + contract.merchant_pub = mi->pubkey; GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, &contract.purpose, &contract_sig); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c @@ -922,6 +922,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh, return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } pc->mi = get_instance (root); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "/pay: picked instance %s\n", + pc->mi->id); if (NULL == pc->mi) { @@ -946,6 +949,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, TALER_amount_hton (&cp.max_fee, &pc->max_fee); cp.h_contract = pc->h_contract; + cp.merchant_pub = pc->mi->pubkey; if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_CONTRACT, &cp.purpose, diff --git a/src/backend/taler-merchant-httpd_track-transaction.c b/src/backend/taler-merchant-httpd_track-transaction.c @@ -33,6 +33,11 @@ /** + * Map containing all the known merchant instances + */ +extern struct GNUNET_CONTAINER_MultiHashMap *by_id_map; + +/** * How long to wait before giving up processing with the exchange? */ #define TRACK_TIMEOUT (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)) @@ -767,8 +772,6 @@ coin_cb (void *cls, tcc)); } -extern struct MerchantInstance **instances; - /** * Handle a "/track/transaction" request. * @@ -790,8 +793,8 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, unsigned long long transaction_id; const char *str; const char *receiver; - unsigned int i; int ret; + struct GNUNET_HashCode h_receiver; if (NULL == *connection_cls) { @@ -847,12 +850,12 @@ MH_handler_track_transaction (struct TMH_RequestHandler *rh, "receiver"); if (NULL == receiver) receiver = "default"; - + GNUNET_CRYPTO_hash (receiver, + strlen (receiver), + &h_receiver); tctx->mi = NULL; - for (i=0; NULL != instances[i]; i++) - if (0 == strcmp (receiver, instances[i]->id)) - tctx->mi = instances[i]; - + GNUNET_assert (NULL != (tctx->mi = GNUNET_CONTAINER_multihashmap_get (by_id_map, + &h_receiver))); if (NULL == tctx->mi) return TMH_RESPONSE_reply_bad_request (connection, "unknown receiver"); diff --git a/src/lib/merchant_api_track_transaction.c b/src/lib/merchant_api_track_transaction.c @@ -252,9 +252,10 @@ TALER_MERCHANT_track_transaction (struct GNUNET_CURL_Context *ctx, tdo->cb = track_transaction_cb; tdo->cb_cls = track_transaction_cb_cls; GNUNET_asprintf (&tdo->url, - "%s/track/transaction?id=%llu", + "%s/track/transaction?id=%llu&receiver=%s", backend_uri, - (unsigned long long) transaction_id); + (unsigned long long) transaction_id, + receiver); eh = curl_easy_init (); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c @@ -1670,12 +1670,12 @@ interpreter_run (void *cls) GNUNET_assert (NULL != ref); cmd->details.track_transfer.tdo = TALER_MERCHANT_track_transfer (ctx, - MERCHANT_URI, - receiver, - &ref->details.check_bank_transfer.wtid, - EXCHANGE_URI, - &track_transfer_cb, - is); + MERCHANT_URI, + receiver, + &ref->details.check_bank_transfer.wtid, + EXCHANGE_URI, + &track_transfer_cb, + is); return; case OC_TRACK_TRANSACTION: ref = find_command (is, @@ -2177,9 +2177,6 @@ int main (int argc, char * const *argv) { - /* Value from "gnunet-ecc -p test_merchant.priv" */ - const char *merchant_pub_str - = "5TRNSWAWHKBJ7G4T3PKRCQA6MCB3MX82F4M2XXS1653KE1V8RFPG"; struct GNUNET_OS_Process *proc; struct GNUNET_OS_Process *exchanged; struct GNUNET_OS_Process *merchantd; @@ -2187,6 +2184,9 @@ main (int argc, struct GNUNET_CONFIGURATION_Handle *cfg; unsigned int cnt; struct GNUNET_SIGNAL_Context *shc_chld; + char *instance_section; + char *keyfile; + struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv; unsetenv ("XDG_DATA_HOME"); unsetenv ("XDG_CONFIG_HOME"); @@ -2204,7 +2204,20 @@ main (int argc, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using non default receiver '%s'\n", receiver); + GNUNET_asprintf (&instance_section, + "merchant-instance-%s", + receiver); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, + instance_section, + "KEYFILE", + &keyfile)); + kpriv = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile); + GNUNET_CRYPTO_eddsa_key_get_public (kpriv, &merchant_pub.eddsa_pub); + + GNUNET_free (keyfile); + GNUNET_free (instance_section); db = TALER_MERCHANTDB_plugin_load (cfg); if (NULL == db) { @@ -2221,12 +2234,6 @@ main (int argc, TALER_MERCHANTDB_plugin_unload (db); GNUNET_CONFIGURATION_destroy (cfg); - - GNUNET_assert (GNUNET_OK == - GNUNET_STRINGS_string_to_data (merchant_pub_str, - strlen (merchant_pub_str), - &merchant_pub, - sizeof (merchant_pub))); proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, diff --git a/src/lib/test_merchant_api.conf b/src/lib/test_merchant_api.conf @@ -22,9 +22,6 @@ PORT = 8082 # FIXME: is this one used? HOSTNAME = localhost -# Where is our private key? -KEYFILE = test_merchant.priv - # How quickly do we want the exchange to send us our money? # Used only if the frontend does not specify a value. # FIXME: EDATE is a bit short, 'execution_delay'? @@ -39,7 +36,7 @@ WIREFORMAT = test # If set, this option will drive the testcase so that # `INSTANCE' will be the used merchant instance. Otherwise # we use the 'default' instance -INSTANCE = tor +INSTANCE = tor [merchant-exchange-test] URI = http://localhost:8081/ @@ -52,7 +49,7 @@ KEYFILE = test_merchant.priv TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json [merchant-instance-tor] -KEYFILE = test_merchant.priv +KEYFILE = tor_merchant.priv [tor-wireformat] TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json diff --git a/src/lib/tor_merchant.priv b/src/lib/tor_merchant.priv @@ -0,0 +1 @@ +=¨³‚WÁË#K®-…ËzNÌ;qr®“o<{ënNºý +\ No newline at end of file