From 899f2b4070ec04af0e0040f6b7fa62d3d860ffee Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 14 Sep 2015 15:29:40 +0200 Subject: implement /wire api (#3947) --- src/mint-lib/Makefile.am | 1 + src/mint-lib/mint_api_wire.c | 611 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 612 insertions(+) create mode 100644 src/mint-lib/mint_api_wire.c (limited to 'src/mint-lib') diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index 0cb83dac..2729177f 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -22,6 +22,7 @@ libtalermint_la_SOURCES = \ mint_api_deposit.c \ mint_api_refresh.c \ mint_api_refresh_link.c \ + mint_api_wire.c \ mint_api_withdraw.c libtalermint_la_LIBADD = \ diff --git a/src/mint-lib/mint_api_wire.c b/src/mint-lib/mint_api_wire.c new file mode 100644 index 00000000..7641af7a --- /dev/null +++ b/src/mint-lib/mint_api_wire.c @@ -0,0 +1,611 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + 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 + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_wire.c + * @brief Implementation of the /wire request of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_common.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/** + * @brief A Wire Handle + */ +struct TALER_MINT_WireHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_WireResultCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + + /** + * Set to the "methods" JSON array returned by the + * /wire request. + */ + json_t *methods; + + /** + * Current iteration offset in the @e methods array. + */ + unsigned int methods_off; + +}; + + +/** + * Verify that the signature on the "200 OK" response + * for /wire/sepa from the mint is valid. + * + * @param wh wire handle + * @param json json reply with the signature + * @return #GNUNET_SYSERR if @a json is invalid, + * #GNUNET_NO if the method is unknown, + * #GNUNET_OK if the json is valid + */ +static int +verify_wire_sepa_signature_ok (const struct TALER_MINT_WireHandle *wh, + json_t *json) +{ + struct TALER_MasterSignatureP mint_sig; + struct TALER_MasterWireSepaDetailsPS mp; + const char *receiver_name; + const char *iban; + const char *bic; + const struct TALER_MINT_Keys *key_state; + struct GNUNET_HashContext *hc; + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("sig", &mint_sig), + MAJ_spec_string ("receiver_name", &receiver_name), + MAJ_spec_string ("iban", &iban), + MAJ_spec_string ("bic", &bic), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + key_state = TALER_MINT_get_keys (wh->mint); + mp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS); + mp.purpose.size = htonl (sizeof (struct TALER_MasterWireSepaDetailsPS)); + hc = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hc, + receiver_name, + strlen (receiver_name) + 1); + GNUNET_CRYPTO_hash_context_read (hc, + iban, + strlen (iban) + 1); + GNUNET_CRYPTO_hash_context_read (hc, + bic, + strlen (bic) + 1); + GNUNET_CRYPTO_hash_context_finish (hc, + &mp.h_sepa_details); + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SEPA_DETAILS, + &mp.purpose, + &mint_sig.eddsa_signature, + &key_state->master_pub.eddsa_pub)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; + } + MAJ_parse_free (spec); + return GNUNET_OK; +} + + +/** + * Verify that the signature on the "200 OK" response + * for /wire/METHOD from the mint is valid. + * + * @param wh wire handle with key material + * @param method method to verify the reply for + * @param json json reply with the signature + * @return #GNUNET_SYSERR if @a json is invalid, + * #GNUNET_NO if the method is unknown, + * #GNUNET_OK if the json is valid + */ +static int +verify_wire_method_signature_ok (const struct TALER_MINT_WireHandle *wh, + const char *method, + json_t *json) +{ + struct + { + /** + * Name fo the method. + */ + const char *method; + + /** + * Handler to invoke to verify signature. + * + * @param wh wire handle with key material + * @param json json reply with signature to verify + */ + int (*handler)(const struct TALER_MINT_WireHandle *wh, + json_t *json); + } handlers[] = { + { "sepa", &verify_wire_sepa_signature_ok }, + { NULL, NULL } + }; + unsigned int i; + + for (i=0;NULL != handlers[i].method; i++) + if (0 == strcasecmp (handlers[i].method, + method)) + return handlers[i].handler (wh, + json); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Wire transfer method `%s' not supported\n", + method); + return GNUNET_NO; +} + + +/** + * Perform the next /wire/method request or signal + * the end of the iteration. + * + * @param wh the wire handle + * @return a handle for this request + */ +static void +request_wire_method (struct TALER_MINT_WireHandle *wh); + + +/** + * Function called when we're done processing the + * HTTP /wire/METHOD request. + * + * @param cls the `struct TALER_MINT_WireHandle` + * @param eh the curl request handle + */ +static void +handle_wire_method_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_WireHandle *wh = cls; + long response_code; + json_t *json; + + wh->job = NULL; + json = MAC_download_get_result (&wh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + { + const char *method; + + method = json_string_value (json_array_get (wh->methods, + wh->methods_off - 1)); + if (GNUNET_OK != + verify_wire_method_signature_ok (wh, + method, + json)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + break; + } + case MHD_HTTP_FOUND: + /* /wire/test returns a 302 redirect, we should just give + this information back to the callback below */ + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (0 == response_code) + { + /* signal end of iteration */ + wh->cb (wh->cb_cls, + 0, + NULL, + NULL); + json_decref (json); + TALER_MINT_wire_cancel (wh); + } + /* pass on successful reply */ + wh->cb (wh->cb_cls, + response_code, + NULL, + json); + /* trigger request for the next /wire/method */ + request_wire_method (wh); +} + + +/** + * Perform the next /wire/method request or signal + * the end of the iteration. + * + * @param wh the wire handle + * @return a handle for this request + */ +static void +request_wire_method (struct TALER_MINT_WireHandle *wh) +{ + struct TALER_MINT_Context *ctx; + CURL *eh; + char *path; + + if (json_array_size (wh->methods) <= wh->methods_off) + { + /* we are done, signal end of iteration */ + wh->cb (wh->cb_cls, + 0, + NULL, + NULL); + TALER_MINT_wire_cancel (wh); + return; + } + GNUNET_free_non_null (wh->db.buf); + wh->db.buf = NULL; + wh->db.buf_size = 0; + wh->db.eno = 0; + GNUNET_free_non_null (wh->url); + GNUNET_asprintf (&path, + "/wire/%s", + json_string_value (json_array_get (wh->methods, + wh->methods_off++))); + wh->url = MAH_path_to_url (wh->mint, + path); + GNUNET_free (path); + + eh = curl_easy_init (); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wh->db)); + ctx = MAH_handle_to_context (wh->mint); + wh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_wire_method_finished, + wh); +} + + +/** + * Verify that the signature on the "200 OK" response + * for /wire from the mint is valid. + * + * @param wh wire handle + * @param json json reply with the signature + * @return NULL if @a json is invalid, otherwise the + * "methods" array (with an RC of 1) + */ +static json_t * +verify_wire_signature_ok (const struct TALER_MINT_WireHandle *wh, + json_t *json) +{ + struct TALER_MintSignatureP mint_sig; + struct TALER_MintPublicKeyP mint_pub; + struct TALER_MintWireSupportMethodsPS mp; + json_t *methods; + const struct TALER_MINT_Keys *key_state; + struct GNUNET_HashContext *hc; + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("sig", &mint_sig), + MAJ_spec_fixed_auto ("pub", &mint_pub), + MAJ_spec_json ("methods", &methods), + MAJ_spec_end + }; + unsigned int i; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return NULL; + } + if (! json_is_array (methods)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return NULL; + } + + key_state = TALER_MINT_get_keys (wh->mint); + if (GNUNET_OK != + TALER_MINT_test_signing_key (key_state, + &mint_pub)) + { + GNUNET_break_op (0); + return NULL; + } + hc = GNUNET_CRYPTO_hash_context_start (); + for (i=0;ijob = NULL; + json = MAC_download_get_result (&wh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + { + json_t *methods; + + if (NULL == + (methods = verify_wire_signature_ok (wh, + json))) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + wh->methods = methods; + request_wire_method (wh); + return; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (0 != response_code) + { + /* pass on successful reply */ + wh->cb (wh->cb_cls, + response_code, + NULL, + json); + } + /* signal end of iteration */ + wh->cb (wh->cb_cls, + 0, + NULL, + NULL); + json_decref (json); + TALER_MINT_wire_cancel (wh); +} + + +/** + * Obtain information about a mint's wire instructions. + * A mint may provide wire instructions for creating + * a reserve. The wire instructions also indicate + * which wire formats merchants may use with the mint. + * This API is typically used by a wallet for wiring + * funds, and possibly by a merchant to determine + * supported wire formats. + * + * Note that while we return the (main) response verbatim to the + * caller for further processing, we do already verify that the + * response is well-formed (i.e. that signatures included in the + * response are all valid). If the mint's reply is not well-formed, + * we return an HTTP status code of zero to @a cb. + * + * @param mint the mint handle; the mint must be ready to operate + * @param wire_cb the callback to call when a reply for this request is available + * @param wire_cb_cls closure for the above callback + * @return a handle for this request + */ +struct TALER_MINT_WireHandle * +TALER_MINT_wire (struct TALER_MINT_Handle *mint, + TALER_MINT_WireResultCallback wire_cb, + void *wire_cb_cls) +{ + struct TALER_MINT_WireHandle *wh; + struct TALER_MINT_Context *ctx; + CURL *eh; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + wh = GNUNET_new (struct TALER_MINT_WireHandle); + wh->mint = mint; + wh->cb = wire_cb; + wh->cb_cls = wire_cb_cls; + wh->url = MAH_path_to_url (mint, "/wire"); + + eh = curl_easy_init (); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wh->db)); + ctx = MAH_handle_to_context (mint); + wh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_wire_finished, + wh); + return wh; +} + + +/** + * Cancel a wire information request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param wire the wire information request handle + */ +void +TALER_MINT_wire_cancel (struct TALER_MINT_WireHandle *wire) +{ + if (NULL != wire->job) + { + MAC_job_cancel (wire->job); + wire->job = NULL; + } + if (NULL != wire->methods) + { + json_decref (wire->methods); + wire->methods = NULL; + } + GNUNET_free_non_null (wire->db.buf); + GNUNET_free (wire->url); + GNUNET_free (wire); +} + + +/* end of mint_api_wire.c */ -- cgit v1.2.3 From 1b0f8609901eb8184194144cc20a194534c81a4a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 15 Sep 2015 10:00:21 +0200 Subject: support for /wire testing --- src/mint-lib/test_mint_api.c | 110 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 5 deletions(-) (limited to 'src/mint-lib') diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 8a9d09ce..fdadcd2c 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -97,7 +97,12 @@ enum OpCode * Verify mint's /refresh/link by linking original private key to * results from #OC_REFRESH_REVEAL step. */ - OC_REFRESH_LINK + OC_REFRESH_LINK, + + /** + * Verify the mint's /wire-method. + */ + OC_WIRE }; @@ -439,6 +444,18 @@ struct Command } refresh_link; + /** + * Information for the /wire command. + */ + struct { + + /** + * Handle to the wire request. + */ + struct TALER_MINT_WireHandle *wh; + + } wire; + } details; }; @@ -1027,7 +1044,7 @@ link_cb (void *cls, for (j=0;jdetails.refresh_reveal.fresh_coins[j]; if ( (0 == memcmp (&coin_privs[i], &fc->coin_priv, @@ -1048,8 +1065,8 @@ link_cb (void *cls, found, num_coins); GNUNET_break (0); - fail (is); - return; + fail (is); + return; } break; default: @@ -1115,6 +1132,76 @@ find_pk (const struct TALER_MINT_Keys *keys, } +/** + * Callbacks called with the result(s) of a + * wire format inquiry request to the mint. + * + * The callback is invoked multiple times, once for each supported @a + * method. Finally, it is invoked one more time with cls/0/NULL/NULL + * to indicate the end of the iteration. If any request fails to + * generate a valid response from the mint, @a http_status will also + * be zero and the iteration will also end. Thus, the iteration + * always ends with a final call with an @a http_status of 0. If the + * @a http_status is already 0 on the first call, then the response to + * the /wire request was invalid. Later, clients can tell the + * difference between @a http_status of 0 indicating a failed + * /wire/method request and a regular end of the iteration by @a + * method being non-NULL. If the mint simply correctly asserts that + * it does not support any methods, @a method will be NULL but the @a + * http_status will be #MHD_HTTP_OK for the first call (followed by a + * cls/0/NULL/NULL call to signal the end of the iteration). + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request; + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param method wire format method supported, i.e. "test" or "sepa", or NULL + * if already the /wire request failed. + * @param obj the received JSON reply, if successful this should be the wire + * format details as provided by /wire/METHOD/, or NULL if the + * reply was not in JSON format (in this case, the client might + * want to do an HTTP request to /wire/METHOD/ with a browser to + * provide more information to the user about the @a method). + */ +static void +wire_cb (void *cls, + unsigned int http_status, + const char *method, + json_t *obj) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + if (0 == http_status) + { + /* 0 always signals the end of the iteration */ + cmd->details.wire.wh = NULL; + } + if (cmd->expected_response_code != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s/%s\n", + http_status, + cmd->label, + method); + json_dumpf (obj, stderr, 0); + fail (is); + return; + } + if (0 == http_status) + { + /* end of iteration, move to next command */ + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); + return; + } + /* For now, we only support to be called only once + with a "positive" result; so we switch to an + expected value of 0 for the 2nd iteration */ + cmd->expected_response_code = 0; +} + + /** * Run the main interpreter loop that performs mint operations. * @@ -1574,6 +1661,12 @@ interpreter_run (void *cls, } trigger_context_task (); return; + case OC_WIRE: + cmd->details.wire.wh = TALER_MINT_wire (mint, + &wire_cb, + is); + trigger_context_task (); + return; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -1714,6 +1807,13 @@ do_shutdown (void *cls, cmd->details.refresh_link.rlh = NULL; } break; + case OC_WIRE: + if (NULL != cmd->details.wire.wh) + { + TALER_MINT_wire_cancel (cmd->details.wire.wh); + cmd->details.wire.wh = NULL; + } + break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown instruction %d at %u (%s)\n", @@ -2024,7 +2124,7 @@ run (void *cls, .details.refresh_melt.melted_coins = melt_coins_1, .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 }, - // FIXME: also test with coin that was already melted + // FIXME: also test with coin that was already melted // (signature differs from coin that was deposited...) /* *************** end of /refresh testing ************** */ -- cgit v1.2.3 From e68446e0a8a4a54b99ba5773a9f7ff17837a393b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 15 Sep 2015 17:27:02 +0200 Subject: adding tests for /wire --- contrib/mint-template/config/mint-common.conf | 8 +++- src/mint-lib/mint_api_context.c | 23 ++++++++++ src/mint-lib/mint_api_wire.c | 9 +++- .../test-mint-home/config/mint-common.conf | 15 +++++-- src/mint-lib/test-mint-home/sepa.json | 6 +++ src/mint-lib/test_mint_api.c | 35 +++++++++++++-- src/mint/taler-mint-httpd.c | 2 +- src/mint/taler-mint-httpd_wire.c | 52 +++++++++++++++------- 8 files changed, 125 insertions(+), 25 deletions(-) create mode 100644 src/mint-lib/test-mint-home/sepa.json (limited to 'src/mint-lib') diff --git a/contrib/mint-template/config/mint-common.conf b/contrib/mint-template/config/mint-common.conf index e222a36a..958763b2 100644 --- a/contrib/mint-template/config/mint-common.conf +++ b/contrib/mint-template/config/mint-common.conf @@ -2,7 +2,7 @@ # Currency supported by the mint (can only be one) CURRENCY = EUR -# Wire format supproted by the mint (currently only SEPA is implemented) +# Wire format supported by the mint (currently only SEPA is implemented) WIREFORMAT = SEPA # HTTP port the mint listens to @@ -19,3 +19,9 @@ TESTRUN = YES [mintdb-postgres] DB_CONN_STR = "postgres:///talercheck" + +[mint-wire-sepa] +SEPA_RESPONSE_FILE = "sepa.json" + +[mint-wire-test] +REDIRECT_URL = "http://test/" diff --git a/src/mint-lib/mint_api_context.c b/src/mint-lib/mint_api_context.c index 9beeef14..5a14b4a4 100644 --- a/src/mint-lib/mint_api_context.c +++ b/src/mint-lib/mint_api_context.c @@ -435,6 +435,29 @@ MAC_download_get_result (struct MAC_DownloadBuffer *db, { json_t *json; json_error_t error; + char *ct; + + if ( (CURLE_OK != + curl_easy_getinfo (eh, + CURLINFO_CONTENT_TYPE, + &ct)) || + (NULL == ct) || + (0 != strcasecmp (ct, + "application/json")) ) + { + /* No content type or explicitly not JSON, refuse to parse + (but keep response code) */ + if (CURLE_OK != + curl_easy_getinfo (eh, + CURLINFO_RESPONSE_CODE, + response_code)) + { + /* unexpected error... */ + GNUNET_break (0); + *response_code = 0; + } + return NULL; + } json = NULL; if (0 == db->eno) diff --git a/src/mint-lib/mint_api_wire.c b/src/mint-lib/mint_api_wire.c index 7641af7a..f1bbb099 100644 --- a/src/mint-lib/mint_api_wire.c +++ b/src/mint-lib/mint_api_wire.c @@ -284,6 +284,7 @@ handle_wire_method_finished (void *cls, NULL); json_decref (json); TALER_MINT_wire_cancel (wh); + return; } /* pass on successful reply */ wh->cb (wh->cb_cls, @@ -345,6 +346,11 @@ request_wire_method (struct TALER_MINT_WireHandle *wh) curl_easy_setopt (eh, CURLOPT_WRITEDATA, &wh->db)); + /* The default is 'disabled', but let's be sure */ + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_FOLLOWLOCATION, + (long) 0)); ctx = MAH_handle_to_context (wh->mint); wh->job = MAC_job_add (ctx, eh, @@ -514,7 +520,8 @@ handle_wire_finished (void *cls, 0, NULL, NULL); - json_decref (json); + if (NULL != json) + json_decref (json); TALER_MINT_wire_cancel (wh); } diff --git a/src/mint-lib/test-mint-home/config/mint-common.conf b/src/mint-lib/test-mint-home/config/mint-common.conf index 1b8aa421..eb2f7e90 100644 --- a/src/mint-lib/test-mint-home/config/mint-common.conf +++ b/src/mint-lib/test-mint-home/config/mint-common.conf @@ -2,9 +2,12 @@ # Currency supported by the mint (can only be one) CURRENCY = EUR -# Wire format supproted by the mint -# TEST is used for testing... (what a shock) -WIREFORMAT = TEST +# Wire format supported by the mint +# We use 'test' for testing, in principle we should +# run tests for all supported wire formats... +# (we should first implement support for a mint running +# with multiple formats at the same time). +WIREFORMAT = test # HTTP port the mint listens to PORT = 8081 @@ -21,3 +24,9 @@ TESTRUN = YES [mintdb-postgres] DB_CONN_STR = "postgres:///talercheck" + +[mint-wire-sepa] +SEPA_RESPONSE_FILE = "test-mint-home/sepa.json" + +[mint-wire-test] +REDIRECT_URL = "http://www.taler.net/" diff --git a/src/mint-lib/test-mint-home/sepa.json b/src/mint-lib/test-mint-home/sepa.json new file mode 100644 index 00000000..36d12f66 --- /dev/null +++ b/src/mint-lib/test-mint-home/sepa.json @@ -0,0 +1,6 @@ +{ + "receiver_name": "Max Mustermann", + "iban": "DE89370400440532013000", + "bic": "COBADEFF370", + "sig": "8M5YJXM68PRAXKH76HYEBCJW657B23JA0RFGNDMZK2379YZMT626H1BN89KC0M1KJBWGYEN5Z763Q0Y7MCTZQ6BPPT7D9KFCTW60C10" +} \ No newline at end of file diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index fdadcd2c..cd1a094c 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -26,6 +26,15 @@ #include #include +/** + * Is the configuration file is set to include wire format 'test'? + */ +#define WIRE_TEST 1 + +/** + * Is the configuration file is set to include wire format 'sepa'? + */ +#define WIRE_SEPA 0 /** * Main execution context for the main loop. @@ -1357,7 +1366,6 @@ interpreter_run (void *cls, &coin_pub.eddsa_pub); cmd->details.withdraw_sign.blinding_key.rsa_blinding_key = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.withdraw_sign.pk->key.rsa_public_key)); - cmd->details.withdraw_sign.wsh = TALER_MINT_withdraw_sign (mint, cmd->details.withdraw_sign.pk, @@ -1442,7 +1450,6 @@ interpreter_run (void *cls, fail (is); return; } - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); @@ -1464,6 +1471,7 @@ interpreter_run (void *cls, { struct TALER_DepositRequestPS dr; + memset (&dr, 0, sizeof (dr)); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.h_contract = h_contract; @@ -1482,7 +1490,6 @@ interpreter_run (void *cls, GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv, &dr.purpose, &coin_sig.eddsa_signature)); - } cmd->details.deposit.dh = TALER_MINT_deposit (mint, @@ -1980,6 +1987,26 @@ run (void *cls, }; static struct Command commands[] = { + /* *************** start of /wire testing ************** */ + +#if WIRE_TEST + { .oc = OC_WIRE, + .label = "wire", + /* /wire/test replies with a 302 redirect */ + .expected_response_code = MHD_HTTP_FOUND }, +#endif +#if WIRE_SEPA + { .oc = OC_WIRE, + .label = "wire", + /* /wire/sepa replies with a 200 redirect */ + .expected_response_code = MHD_HTTP_OK }, +#endif + /* *************** end of /wire testing ************** */ + +#if WIRE_TEST + /* None of this works if 'test' is not allowed as we do + /admin/add/incoming with format 'test' */ + /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */ { .oc = OC_ADMIN_ADD_INCOMING, .label = "create-reserve-1", @@ -2126,8 +2153,8 @@ run (void *cls, // FIXME: also test with coin that was already melted // (signature differs from coin that was deposited...) - /* *************** end of /refresh testing ************** */ +#endif { .oc = OC_END } }; diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index 1a5e5a2f..e68cd242 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -177,7 +177,7 @@ handle_mhd_request (void *cls, { "/wire/test", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, - &TMH_WIRE_handler_wire_test, MHD_HTTP_OK }, + &TMH_WIRE_handler_wire_test, MHD_HTTP_FOUND }, { "/wire/test", NULL, "text/plain", "Only GET is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, diff --git a/src/mint/taler-mint-httpd_wire.c b/src/mint/taler-mint-httpd_wire.c index ee3b4ff0..0e28db79 100644 --- a/src/mint/taler-mint-httpd_wire.c +++ b/src/mint/taler-mint-httpd_wire.c @@ -56,14 +56,14 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh, &sig); methods = json_array (); /* NOTE: for now, we only support *ONE* wire format per - mint instance; if we supply multiple, we need to + mint instance; if we supply multiple, we need to add the strings for each type separately here -- and hash the 0-terminated strings above differently as well... */ json_array_append_new (methods, json_string (TMH_expected_wire_format)); return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, - "{s:s, s:o, s:o}", + "{s:o, s:o, s:o}", "methods", methods, "sig", TALER_json_from_data (&sig, sizeof (sig)), @@ -100,26 +100,30 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh, GNUNET_break (0); return MHD_NO; } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint-wire-test", - "REDIRECT_URL", - &wire_test_redirect)) + if (0 != strcasecmp ("test", + TMH_expected_wire_format)) { + /* Return 501: not implemented */ ret = MHD_queue_response (connection, MHD_HTTP_NOT_IMPLEMENTED, response); MHD_destroy_response (response); return ret; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint-wire-test", + "REDIRECT_URL", + &wire_test_redirect)) + { + /* oopsie, configuration error */ + return TMH_RESPONSE_reply_internal_error (connection, + "REDIRECT_URL not configured"); + } MHD_add_response_header (response, MHD_HTTP_HEADER_LOCATION, wire_test_redirect); GNUNET_free (wire_test_redirect); - if (NULL != rh->mime_type) - (void) MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - rh->mime_type); ret = MHD_queue_response (connection, rh->response_code, response); @@ -151,11 +155,29 @@ TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh, int fd; struct stat sbuf; + if (0 != strcasecmp ("sepa", + TMH_expected_wire_format)) + { + /* Return 501: not implemented */ + response = MHD_create_response_from_buffer (0, NULL, + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + ret = MHD_queue_response (connection, + MHD_HTTP_NOT_IMPLEMENTED, + response); + MHD_destroy_response (response); + return ret; + } + /* Fetch reply */ if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint-wire-sepa", - "SEPA_RESPONSE_FILE", - &sepa_wire_file)) + GNUNET_CONFIGURATION_get_value_filename (cfg, + "mint-wire-sepa", + "SEPA_RESPONSE_FILE", + &sepa_wire_file)) { return TMH_RESPONSE_reply_internal_error (connection, "SEPA_RESPONSE_FILE not configured"); -- cgit v1.2.3 From e44f0309ee3899eade0c55d9229d1b153e0106b4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 15 Sep 2015 17:28:57 +0200 Subject: reference #3972 --- src/mint-lib/test_mint_api.c | 1 + src/mint/taler-mint-httpd_wire.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/mint-lib') diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index cd1a094c..96a15207 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -33,6 +33,7 @@ /** * Is the configuration file is set to include wire format 'sepa'? + * Needs #3972 to be solved first. */ #define WIRE_SEPA 0 diff --git a/src/mint/taler-mint-httpd_wire.c b/src/mint/taler-mint-httpd_wire.c index 0e28db79..13f23e8e 100644 --- a/src/mint/taler-mint-httpd_wire.c +++ b/src/mint/taler-mint-httpd_wire.c @@ -58,7 +58,8 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh, /* NOTE: for now, we only support *ONE* wire format per mint instance; if we supply multiple, we need to add the strings for each type separately here -- and - hash the 0-terminated strings above differently as well... */ + hash the 0-terminated strings above differently as well... + See #3972. */ json_array_append_new (methods, json_string (TMH_expected_wire_format)); return TMH_RESPONSE_reply_json_pack (connection, -- cgit v1.2.3 From 7fbae8f69f35b2bb2ace2c9131cd3ce67e9bc943 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Sep 2015 16:11:31 +0200 Subject: implementing retrieval of auditor information from /keys in mint API (with updated specification) - #3847 --- src/include/taler_mint_service.h | 32 +++++++- src/include/taler_signatures.h | 72 +++++++++++++++-- src/mint-lib/mint_api_handle.c | 167 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 258 insertions(+), 13 deletions(-) (limited to 'src/mint-lib') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index 3f89b074..8d569d4b 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -149,6 +149,11 @@ struct TALER_MINT_DenomPublicKey */ struct TALER_DenominationPublicKey key; + /** + * The hash of the public key. + */ + struct GNUNET_HashCode h_key; + /** * Timestamp indicating when the denomination key becomes valid */ @@ -165,6 +170,15 @@ struct TALER_MINT_DenomPublicKey */ struct GNUNET_TIME_Absolute deposit_valid_until; + /** + * When do signatures with this denomination key become invalid? + * After this point, these signatures cannot be used in (legal) + * disputes anymore, as the Mint is then allowed to destroy its side + * of the evidence. @e expire_legal is expected to be significantly + * larger than @e expire_spend (by a year or more). + */ + struct GNUNET_TIME_Absolute expire_legal; + /** * The value of this denomination */ @@ -204,6 +218,8 @@ struct TALER_MINT_AuditorInformation * that website. We expect that in practice software is going to * often ship with an initial list of accepted auditors, just like * browsers ship with a CA root store. + * + * This field may be NULL. (#3987). */ const char *auditor_url; @@ -218,7 +234,7 @@ struct TALER_MINT_AuditorInformation * elements point to the same locations as the entries * in the key's main `denom_keys` array. */ - struct TALER_MINT_DenomPublicKey *const*denom_keys; + const struct TALER_MINT_DenomPublicKey **denom_keys; }; @@ -246,7 +262,7 @@ struct TALER_MINT_Keys /** * Array of the keys of the auditors of the mint. */ - struct TALER_AuditorPublicKeyP *auditors; + struct TALER_MINT_AuditorInformation *auditors; /** * Length of the @e sign_keys array. @@ -353,6 +369,18 @@ TALER_MINT_get_denomination_key (const struct TALER_MINT_Keys *keys, const struct TALER_DenominationPublicKey *pk); +/** + * Obtain the denomination key details from the mint. + * + * @param keys the mint's key set + * @param hc hash of the public key of the denomination to lookup + * @return details about the given denomination key + */ +const struct TALER_MINT_DenomPublicKey * +TALER_MINT_get_denomination_key_by_hash (const struct TALER_MINT_Keys *keys, + const struct GNUNET_HashCode *hc); + + /* ********************* /wire *********************** */ diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index ffbc9fd4..3bdc4eee 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -653,13 +653,73 @@ struct TALER_MintKeyValidityPS struct TALER_MasterPublicKeyP master; /** - * Array of hash(es) of the mint's denomination keys. - * Specifically, this is the hash over the - * `struct TALER_DenominationKeyValidityPS`, not just - * the public key (as the auditor needs to check against - * the correct valuations and fee structure). + * Start time of the validity period for this key. + */ + struct GNUNET_TIME_AbsoluteNBO start; + + /** + * The mint will sign fresh coins between @e start and this time. + * @e expire_withdraw will be somewhat larger than @e start to + * ensure a sufficiently large anonymity set, while also allowing + * the Mint to limit the financial damage in case of a key being + * compromised. Thus, mints with low volume are expected to have a + * longer withdraw period (@e expire_withdraw - @e start) than mints + * with high transaction volume. The period may also differ between + * types of coins. A mint may also have a few denomination keys + * with the same value with overlapping validity periods, to address + * issues such as clock skew. + */ + struct GNUNET_TIME_AbsoluteNBO expire_withdraw; + + /** + * Coins signed with the denomination key must be spent or refreshed + * between @e start and this expiration time. After this time, the + * mint will refuse transactions involving this key as it will + * "drop" the table with double-spending information (shortly after) + * this time. Note that wallets should refresh coins significantly + * before this time to be on the safe side. @e expire_spend must be + * significantly larger than @e expire_withdraw (by months or even + * years). + */ + struct GNUNET_TIME_AbsoluteNBO expire_spend; + + /** + * When do signatures with this denomination key become invalid? + * After this point, these signatures cannot be used in (legal) + * disputes anymore, as the Mint is then allowed to destroy its side + * of the evidence. @e expire_legal is expected to be significantly + * larger than @e expire_spend (by a year or more). */ - /* struct GNUNET_HashCode h_dks; */ + struct GNUNET_TIME_AbsoluteNBO expire_legal; + + /** + * The value of the coins signed with this denomination key. + */ + struct TALER_AmountNBO value; + + /** + * The fee the mint charges when a coin of this type is withdrawn. + * (can be zero). + */ + struct TALER_AmountNBO fee_withdraw; + + /** + * The fee the mint charges when a coin of this type is deposited. + * (can be zero). + */ + struct TALER_AmountNBO fee_deposit; + + /** + * The fee the mint charges when a coin of this type is refreshed. + * (can be zero). + */ + struct TALER_AmountNBO fee_refresh; + + /** + * Hash code of the denomination public key. (Used to avoid having + * the variable-size RSA key in this struct.) + */ + struct GNUNET_HashCode denom_hash GNUNET_PACKED; }; diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c index f5e26ff1..9eaa6171 100644 --- a/src/mint-lib/mint_api_handle.c +++ b/src/mint-lib/mint_api_handle.c @@ -325,9 +325,11 @@ parse_json_denomkey (struct TALER_MINT_DenomPublicKey *denom_key, &denom_key_issue.denom_hash, sizeof (struct GNUNET_HashCode)); denom_key->key.rsa_public_key = pk; + denom_key->h_key = denom_key_issue.denom_hash; denom_key->valid_from = valid_from; denom_key->withdraw_valid_until = withdraw_valid_until; denom_key->deposit_valid_until = deposit_valid_until; + denom_key->expire_legal = expire_legal; denom_key->value = value; denom_key->fee_withdraw = fee_withdraw; denom_key->fee_deposit = fee_deposit; @@ -340,6 +342,116 @@ parse_json_denomkey (struct TALER_MINT_DenomPublicKey *denom_key, } +/** + * Parse a mint's auditor information encoded in JSON. + * + * @param[out] auditor where to return the result + * @param[in] auditor_obj json to parse + * @param key_data information about denomination keys + * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is + * invalid or the json malformed. + */ +static int +parse_json_auditor (struct TALER_MINT_AuditorInformation *auditor, + json_t *auditor_obj, + const struct TALER_MINT_Keys *key_data) +{ + json_t *keys; + json_t *key; + unsigned int len; + unsigned int off; + unsigned int i; + struct TALER_MintKeyValidityPS kv; + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("auditor_pub", + &auditor->auditor_pub), + MAJ_spec_json ("denomination_keys", + &keys), + MAJ_spec_end + }; + + auditor->auditor_url = NULL; /* #3987 */ + if (GNUNET_OK != + MAJ_parse_json (auditor_obj, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + kv.purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_MINT_KEYS); + kv.purpose.size = htonl (sizeof (struct TALER_MintKeyValidityPS)); + kv.master = key_data->master_pub; + len = json_array_size (keys); + auditor->denom_keys = GNUNET_new_array (len, + const struct TALER_MINT_DenomPublicKey *); + i = 0; + off = 0; + json_array_foreach (keys, i, key) { + struct TALER_AuditorSignatureP auditor_sig; + struct GNUNET_HashCode denom_h; + const struct TALER_MINT_DenomPublicKey *dk; + unsigned int j; + struct MAJ_Specification spec[] = { + MAJ_spec_fixed_auto ("denom_pub_h", + &denom_h), + MAJ_spec_fixed_auto ("auditor_sig", + &auditor_sig), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (key, + spec)) + { + GNUNET_break_op (0); + continue; + } + dk = NULL; + for (j=0;jnum_denom_keys;j++) + { + if (0 == memcmp (&denom_h, + &key_data->denom_keys[j].h_key, + sizeof (struct GNUNET_HashCode))) + { + dk = &key_data->denom_keys[j]; + break; + } + } + if (NULL == dk) + { + GNUNET_break_op (0); + continue; + } + kv.start = GNUNET_TIME_absolute_hton (dk->valid_from); + kv.expire_withdraw = GNUNET_TIME_absolute_hton (dk->withdraw_valid_until); + kv.expire_spend = GNUNET_TIME_absolute_hton (dk->deposit_valid_until); + kv.expire_legal = GNUNET_TIME_absolute_hton (dk->expire_legal); + TALER_amount_hton (&kv.value, + &dk->value); + TALER_amount_hton (&kv.fee_withdraw, + &dk->fee_withdraw); + TALER_amount_hton (&kv.fee_deposit, + &dk->fee_deposit); + TALER_amount_hton (&kv.fee_refresh, + &dk->fee_refresh); + kv.denom_hash = dk->h_key; + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_AUDITOR_MINT_KEYS, + &kv.purpose, + &auditor_sig.eddsa_sig, + &auditor->auditor_pub.eddsa_pub)) + { + GNUNET_break_op (0); + continue; + } + auditor->denom_keys[off] = dk; + off++; + } + auditor->num_denom_keys = off; + return GNUNET_OK; +} + + /** * Decode the JSON in @a resp_obj from the /keys response and store the data * in the @a key_data. @@ -394,8 +506,8 @@ decode_keys_json (json_t *resp_obj, EXITIF (0 == (key_data->num_sign_keys = json_array_size (sign_keys_array))); key_data->sign_keys - = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey) - * key_data->num_sign_keys); + = GNUNET_new_array (key_data->num_sign_keys, + struct TALER_MINT_SigningPublicKey); index = 0; json_array_foreach (sign_keys_array, index, sign_key_obj) { EXITIF (GNUNET_SYSERR == @@ -415,8 +527,8 @@ decode_keys_json (json_t *resp_obj, json_object_get (resp_obj, "denoms"))); EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); EXITIF (0 == (key_data->num_denom_keys = json_array_size (denom_keys_array))); - key_data->denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey) - * key_data->num_denom_keys); + key_data->denom_keys = GNUNET_new_array (key_data->num_denom_keys, + struct TALER_MINT_DenomPublicKey); index = 0; json_array_foreach (denom_keys_array, index, denom_key_obj) { EXITIF (GNUNET_SYSERR == @@ -427,7 +539,30 @@ decode_keys_json (json_t *resp_obj, } } - /* FIXME: parse the auditor keys (#3847) */ + /* parse the auditor information */ + { + json_t *auditors_array; + json_t *auditor_info; + unsigned int len; + unsigned int index; + + EXITIF (NULL == (auditors_array = + json_object_get (resp_obj, "auditors"))); + EXITIF (JSON_ARRAY != json_typeof (auditors_array)); + len = json_array_size (auditors_array); + if (0 != len) + { + key_data->auditors = GNUNET_new_array (len, + struct TALER_MINT_AuditorInformation); + index = 0; + json_array_foreach (auditors_array, index, auditor_info) { + EXITIF (GNUNET_SYSERR == + parse_json_auditor (&key_data->auditors[index], + auditor_info, + key_data)); + } + } + } /* Validate signature... */ ks.purpose.size = htonl (sizeof (ks)); @@ -708,6 +843,28 @@ TALER_MINT_get_denomination_key (const struct TALER_MINT_Keys *keys, } +/** + * Obtain the denomination key details from the mint. + * + * @param keys the mint's key set + * @param hc hash of the public key of the denomination to lookup + * @return details about the given denomination key + */ +const struct TALER_MINT_DenomPublicKey * +TALER_MINT_get_denomination_key_by_hash (const struct TALER_MINT_Keys *keys, + const struct GNUNET_HashCode *hc) +{ + unsigned int i; + + for (i=0;inum_denom_keys;i++) + if (0 == memcmp (hc, + &keys->denom_keys[i].h_key, + sizeof (struct GNUNET_HashCode))) + return &keys->denom_keys[i]; + return NULL; +} + + /** * Obtain the keys from the mint. * -- cgit v1.2.3 From 1eadd66ae0c4abe6867321bcac0ad2f9832a0baf Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Sep 2015 22:08:49 +0200 Subject: renaming /withdraw to /reserve (#3968) --- src/include/taler_mint_service.h | 64 +- src/mint-lib/Makefile.am | 4 +- src/mint-lib/mint_api_handle.c | 2 +- src/mint-lib/mint_api_handle.h | 2 +- src/mint-lib/mint_api_reserve.c | 927 +++++++++++++++++++++++++++++ src/mint-lib/mint_api_withdraw.c | 927 ----------------------------- src/mint-lib/test_mint_api.c | 178 +++--- src/mint/Makefile.am | 2 +- src/mint/taler-mint-httpd.c | 14 +- src/mint/taler-mint-httpd_db.c | 30 +- src/mint/taler-mint-httpd_db.h | 20 +- src/mint/taler-mint-httpd_keystate.h | 2 +- src/mint/taler-mint-httpd_reserve.c | 185 ++++++ src/mint/taler-mint-httpd_reserve.h | 73 +++ src/mint/taler-mint-httpd_responses.c | 12 +- src/mint/taler-mint-httpd_responses.h | 14 +- src/mint/taler-mint-httpd_withdraw.c | 180 ------ src/mint/taler-mint-httpd_withdraw.h | 73 --- src/mintdb/perf_taler_mintdb.c | 12 +- src/mintdb/perf_taler_mintdb_interpreter.h | 2 +- src/mintdb/plugin_mintdb_postgres.c | 10 +- 21 files changed, 1369 insertions(+), 1364 deletions(-) create mode 100644 src/mint-lib/mint_api_reserve.c delete mode 100644 src/mint-lib/mint_api_withdraw.c create mode 100644 src/mint/taler-mint-httpd_reserve.c create mode 100644 src/mint/taler-mint-httpd_reserve.h delete mode 100644 src/mint/taler-mint-httpd_withdraw.c delete mode 100644 src/mint/taler-mint-httpd_withdraw.h (limited to 'src/mint-lib') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index 8d569d4b..e36dcf69 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -547,13 +547,13 @@ void TALER_MINT_deposit_cancel (struct TALER_MINT_DepositHandle *deposit); -/* ********************* /withdraw/status *********************** */ +/* ********************* /reserve/status *********************** */ /** - * @brief A /withdraw/status Handle + * @brief A /reserve/status Handle */ -struct TALER_MINT_WithdrawStatusHandle; +struct TALER_MINT_ReserveStatusHandle; /** @@ -623,12 +623,12 @@ struct TALER_MINT_ReserveHistory * @param history detailed transaction history, NULL on error */ typedef void -(*TALER_MINT_WithdrawStatusResultCallback) (void *cls, - unsigned int http_status, - json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_MINT_ReserveHistory *history); +(*TALER_MINT_ReserveStatusResultCallback) (void *cls, + unsigned int http_status, + json_t *json, + const struct TALER_Amount *balance, + unsigned int history_length, + const struct TALER_MINT_ReserveHistory *history); /** @@ -647,11 +647,11 @@ typedef void * @return a handle for this request; NULL if the inputs are invalid (i.e. * signatures fail to verify). In this case, the callback is not called. */ -struct TALER_MINT_WithdrawStatusHandle * -TALER_MINT_withdraw_status (struct TALER_MINT_Handle *mint, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_MINT_WithdrawStatusResultCallback cb, - void *cb_cls); +struct TALER_MINT_ReserveStatusHandle * +TALER_MINT_reserve_status (struct TALER_MINT_Handle *mint, + const struct TALER_ReservePublicKeyP *reserve_pub, + TALER_MINT_ReserveStatusResultCallback cb, + void *cb_cls); /** @@ -661,16 +661,16 @@ TALER_MINT_withdraw_status (struct TALER_MINT_Handle *mint, * @param wsh the withdraw status request handle */ void -TALER_MINT_withdraw_status_cancel (struct TALER_MINT_WithdrawStatusHandle *wsh); +TALER_MINT_reserve_status_cancel (struct TALER_MINT_ReserveStatusHandle *wsh); -/* ********************* /withdraw/sign *********************** */ +/* ********************* /reserve/withdraw *********************** */ /** - * @brief A /withdraw/sign Handle + * @brief A /reserve/withdraw Handle */ -struct TALER_MINT_WithdrawSignHandle; +struct TALER_MINT_ReserveWithdrawHandle; /** @@ -684,14 +684,14 @@ struct TALER_MINT_WithdrawSignHandle; * @param full_response full response from the mint (for logging, in case of errors) */ typedef void -(*TALER_MINT_WithdrawSignResultCallback) (void *cls, - unsigned int http_status, - const struct TALER_DenominationSignature *sig, - json_t *full_response); +(*TALER_MINT_ReserveWithdrawResultCallback) (void *cls, + unsigned int http_status, + const struct TALER_DenominationSignature *sig, + json_t *full_response); /** - * Withdraw a coin from the mint using a /withdraw/sign request. This + * Withdraw a coin from the mint using a /reserve/withdraw request. This * API is typically used by a wallet. Note that to ensure that no * money is lost in case of hardware failures, the caller must have * committed (most of) the arguments to disk before calling, and be @@ -711,14 +711,14 @@ typedef void * if the inputs are invalid (i.e. denomination key not with this mint). * In this case, the callback is not called. */ -struct TALER_MINT_WithdrawSignHandle * -TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint, - const struct TALER_MINT_DenomPublicKey *pk, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKey *blinding_key, - TALER_MINT_WithdrawSignResultCallback res_cb, - void *res_cb_cls); +struct TALER_MINT_ReserveWithdrawHandle * +TALER_MINT_reserve_withdraw (struct TALER_MINT_Handle *mint, + const struct TALER_MINT_DenomPublicKey *pk, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + const struct TALER_DenominationBlindingKey *blinding_key, + TALER_MINT_ReserveWithdrawResultCallback res_cb, + void *res_cb_cls); /** @@ -728,7 +728,7 @@ TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint, * @param sign the withdraw sign request handle */ void -TALER_MINT_withdraw_sign_cancel (struct TALER_MINT_WithdrawSignHandle *sign); +TALER_MINT_reserve_withdraw_cancel (struct TALER_MINT_ReserveWithdrawHandle *sign); /* ********************* /refresh/melt+reveal ***************************** */ diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index 2729177f..ccea4ec5 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -22,8 +22,8 @@ libtalermint_la_SOURCES = \ mint_api_deposit.c \ mint_api_refresh.c \ mint_api_refresh_link.c \ - mint_api_wire.c \ - mint_api_withdraw.c + mint_api_reserve.c \ + mint_api_wire.c libtalermint_la_LIBADD = \ -lgnunetutil \ diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c index 9eaa6171..61291389 100644 --- a/src/mint-lib/mint_api_handle.c +++ b/src/mint-lib/mint_api_handle.c @@ -686,7 +686,7 @@ MAH_handle_is_ready (struct TALER_MINT_Handle *h) * Obtain the URL to use for an API request. * * @param h the mint handle to query - * @param path Taler API path (i.e. "/withdraw/sign") + * @param path Taler API path (i.e. "/reserve/withdraw") * @return the full URI to use with cURL */ char * diff --git a/src/mint-lib/mint_api_handle.h b/src/mint-lib/mint_api_handle.h index aeaeeb59..fae30a30 100644 --- a/src/mint-lib/mint_api_handle.h +++ b/src/mint-lib/mint_api_handle.h @@ -48,7 +48,7 @@ MAH_handle_is_ready (struct TALER_MINT_Handle *h); * Obtain the URL to use for an API request. * * @param h the mint handle to query - * @param path Taler API path (i.e. "/withdraw/sign") + * @param path Taler API path (i.e. "/reserve/withdraw") * @return the full URI to use with cURL */ char * diff --git a/src/mint-lib/mint_api_reserve.c b/src/mint-lib/mint_api_reserve.c new file mode 100644 index 00000000..57e8552c --- /dev/null +++ b/src/mint-lib/mint_api_reserve.c @@ -0,0 +1,927 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + 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 + Foundation; either version 3, 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_reserve.c + * @brief Implementation of the /reserve requests of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/* ********************** /reserve/status ********************** */ + +/** + * @brief A Withdraw Status Handle + */ +struct TALER_MINT_ReserveStatusHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_ReserveStatusResultCallback cb; + + /** + * Public key of the reserve we are querying. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Parse history given in JSON format and return it in binary + * format. + * + * @param[in] history JSON array with the history + * @param reserve_pub public key of the reserve to inspect + * @param currency currency we expect the balance to be in + * @param[out] balance final balance + * @param history_length number of entries in @a history + * @param[out] rhistory array of length @a history_length, set to the + * parsed history entries + * @return #GNUNET_OK if history was valid and @a rhistory and @a balance + * were set, + * #GNUNET_SYSERR if there was a protocol violation in @a history + */ +static int +parse_reserve_history (json_t *history, + const struct TALER_ReservePublicKeyP *reserve_pub, + const char *currency, + struct TALER_Amount *balance, + unsigned int history_length, + struct TALER_MINT_ReserveHistory *rhistory) +{ + struct GNUNET_HashCode uuid[history_length]; + unsigned int uuid_off; + struct TALER_Amount total_in; + struct TALER_Amount total_out; + size_t off; + + TALER_amount_get_zero (currency, + &total_in); + TALER_amount_get_zero (currency, + &total_out); + uuid_off = 0; + for (off=0;offeddsa_pub), + MAJ_spec_end + }; + unsigned int i; + + rhistory[off].type = TALER_MINT_RTT_WITHDRAWAL; + if (GNUNET_OK != + MAJ_parse_json (transaction, + withdraw_spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Check that the signature actually signed a withdraw request */ + if ( (ntohl (purpose->purpose) != TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW) || + (ntohl (purpose->size) != sizeof (struct TALER_WithdrawRequestPS)) ) + { + GNUNET_break_op (0); + MAJ_parse_free (withdraw_spec); + return GNUNET_SYSERR; + } + withdraw_purpose = (const struct TALER_WithdrawRequestPS *) purpose; + TALER_amount_ntoh (&amount_from_purpose, + &withdraw_purpose->amount_with_fee); + if (0 != TALER_amount_cmp (&amount, + &amount_from_purpose)) + { + GNUNET_break_op (0); + MAJ_parse_free (withdraw_spec); + return GNUNET_SYSERR; + } + rhistory[off].details.out_authorization_sig = json_object_get (transaction, + "signature"); + /* Check check that the same withdraw transaction + isn't listed twice by the mint. We use the + "uuid" array to remember the hashes of all + purposes, and compare the hashes to find + duplicates. */ + GNUNET_CRYPTO_hash (withdraw_purpose, + ntohl (withdraw_purpose->purpose.size), + &uuid[uuid_off]); + for (i=0;ijob = NULL; + json = MAC_download_get_result (&wsh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + { + /* TODO: move into separate function... */ + json_t *history; + unsigned int len; + struct TALER_Amount balance; + struct TALER_Amount balance_from_history; + struct MAJ_Specification spec[] = { + MAJ_spec_amount ("balance", &balance), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + history = json_object_get (json, + "history"); + if (NULL == history) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + len = json_array_size (history); + { + struct TALER_MINT_ReserveHistory rhistory[len]; + + if (GNUNET_OK != + parse_reserve_history (history, + &wsh->reserve_pub, + balance.currency, + &balance_from_history, + len, + rhistory)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + if (0 != + TALER_amount_cmp (&balance_from_history, + &balance)) + { + /* mint cannot add up balances!? */ + GNUNET_break_op (0); + response_code = 0; + break; + } + wsh->cb (wsh->cb_cls, + response_code, + json, + &balance, + len, + rhistory); + wsh->cb = NULL; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != wsh->cb) + wsh->cb (wsh->cb_cls, + response_code, + json, + NULL, + 0, NULL); + json_decref (json); + TALER_MINT_reserve_status_cancel (wsh); +} + + +/** + * Submit a request to obtain the transaction history of a reserve + * from the mint. Note that while we return the full response to the + * caller for further processing, we do already verify that the + * response is well-formed (i.e. that signatures included in the + * response are all valid and add up to the balance). If the mint's + * reply is not well-formed, we return an HTTP status code of zero to + * @a cb. + * + * @param mint the mint handle; the mint must be ready to operate + * @param reserve_pub public key of the reserve to inspect + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_MINT_ReserveStatusHandle * +TALER_MINT_reserve_status (struct TALER_MINT_Handle *mint, + const struct TALER_ReservePublicKeyP *reserve_pub, + TALER_MINT_ReserveStatusResultCallback cb, + void *cb_cls) +{ + struct TALER_MINT_ReserveStatusHandle *wsh; + struct TALER_MINT_Context *ctx; + CURL *eh; + char *pub_str; + char *arg_str; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + pub_str = GNUNET_STRINGS_data_to_string_alloc (reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)); + GNUNET_asprintf (&arg_str, + "/reserve/status?reserve_pub=%s", + pub_str); + GNUNET_free (pub_str); + wsh = GNUNET_new (struct TALER_MINT_ReserveStatusHandle); + wsh->mint = mint; + wsh->cb = cb; + wsh->cb_cls = cb_cls; + wsh->reserve_pub = *reserve_pub; + wsh->url = MAH_path_to_url (mint, + arg_str); + GNUNET_free (arg_str); + + eh = curl_easy_init (); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wsh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wsh->db)); + ctx = MAH_handle_to_context (mint); + wsh->job = MAC_job_add (ctx, + eh, + GNUNET_NO, + &handle_reserve_status_finished, + wsh); + return wsh; +} + + +/** + * Cancel a withdraw status request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param wsh the withdraw status request handle + */ +void +TALER_MINT_reserve_status_cancel (struct TALER_MINT_ReserveStatusHandle *wsh) +{ + if (NULL != wsh->job) + { + MAC_job_cancel (wsh->job); + wsh->job = NULL; + } + GNUNET_free_non_null (wsh->db.buf); + GNUNET_free (wsh->url); + GNUNET_free (wsh); +} + + +/* ********************** /reserve/withdraw ********************** */ + +/** + * @brief A Withdraw Sign Handle + */ +struct TALER_MINT_ReserveWithdrawHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_ReserveWithdrawResultCallback cb; + + /** + * Key used to blind the value. + */ + const struct TALER_DenominationBlindingKey *blinding_key; + + /** + * Denomination key we are withdrawing. + */ + const struct TALER_MINT_DenomPublicKey *pk; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + + /** + * Hash of the public key of the coin we are signing. + */ + struct GNUNET_HashCode c_hash; + + /** + * Public key of the reserve we are withdrawing from. + */ + struct TALER_ReservePublicKeyP reserve_pub; + +}; + + +/** + * We got a 200 OK response for the /reserve/withdraw operation. + * Extract the coin's signature and return it to the caller. + * The signature we get from the mint is for the blinded value. + * Thus, we first must unblind it and then should verify its + * validity against our coin's hash. + * + * If everything checks out, we return the unblinded signature + * to the application via the callback. + * + * @param wsh operation handle + * @param json reply from the mint + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static int +reserve_withdraw_ok (struct TALER_MINT_ReserveWithdrawHandle *wsh, + json_t *json) +{ + struct GNUNET_CRYPTO_rsa_Signature *blind_sig; + struct GNUNET_CRYPTO_rsa_Signature *sig; + struct TALER_DenominationSignature dsig; + struct MAJ_Specification spec[] = { + MAJ_spec_rsa_signature ("ev_sig", &blind_sig), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, + wsh->blinding_key->rsa_blinding_key, + wsh->pk->key.rsa_public_key); + GNUNET_CRYPTO_rsa_signature_free (blind_sig); + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (&wsh->c_hash, + sig, + wsh->pk->key.rsa_public_key)) + { + GNUNET_break_op (0); + GNUNET_CRYPTO_rsa_signature_free (sig); + return GNUNET_SYSERR; + } + /* signature is valid, return it to the application */ + dsig.rsa_signature = sig; + wsh->cb (wsh->cb_cls, + MHD_HTTP_OK, + &dsig, + json); + /* make sure callback isn't called again after return */ + wsh->cb = NULL; + GNUNET_CRYPTO_rsa_signature_free (sig); + return GNUNET_OK; +} + + +/** + * We got a 402 PAYMENT REQUIRED response for the /reserve/withdraw operation. + * Check the signatures on the withdraw transactions in the provided + * history and that the balances add up. We don't do anything directly + * with the information, as the JSON will be returned to the application. + * However, our job is ensuring that the mint followed the protocol, and + * this in particular means checking all of the signatures in the history. + * + * @param wsh operation handle + * @param json reply from the mint + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static int +reserve_withdraw_payment_required (struct TALER_MINT_ReserveWithdrawHandle *wsh, + json_t *json) +{ + struct TALER_Amount balance; + struct TALER_Amount balance_from_history; + struct TALER_Amount requested_amount; + json_t *history; + size_t len; + struct MAJ_Specification spec[] = { + MAJ_spec_amount ("balance", &balance), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + history = json_object_get (json, + "history"); + if (NULL == history) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* go over transaction history and compute + total incoming and outgoing amounts */ + len = json_array_size (history); + { + struct TALER_MINT_ReserveHistory rhistory[len]; + + if (GNUNET_OK != + parse_reserve_history (history, + &wsh->reserve_pub, + balance.currency, + &balance_from_history, + len, + rhistory)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + + if (0 != + TALER_amount_cmp (&balance_from_history, + &balance)) + { + /* mint cannot add up balances!? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Compute how much we expected to charge to the reserve */ + if (GNUNET_OK != + TALER_amount_add (&requested_amount, + &wsh->pk->value, + &wsh->pk->fee_withdraw)) + { + /* Overflow here? Very strange, our CPU must be fried... */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* Check that funds were really insufficient */ + if (0 >= TALER_amount_cmp (&requested_amount, + &balance)) + { + /* Requested amount is smaller or equal to reported balance, + so this should not have failed. */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /reserve/withdraw request. + * + * @param cls the `struct TALER_MINT_ReserveWithdrawHandle` + * @param eh curl handle of the request that finished + */ +static void +handle_reserve_withdraw_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_ReserveWithdrawHandle *wsh = cls; + long response_code; + json_t *json; + + wsh->job = NULL; + json = MAC_download_get_result (&wsh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + reserve_withdraw_ok (wsh, + json)) + { + GNUNET_break_op (0); + response_code = 0; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_PAYMENT_REQUIRED: + /* The mint says that the reserve has insufficient funds; + check the signatures in the history... */ + if (GNUNET_OK != + reserve_withdraw_payment_required (wsh, + json)) + { + GNUNET_break_op (0); + response_code = 0; + } + break; + case MHD_HTTP_UNAUTHORIZED: + GNUNET_break (0); + /* Nothing really to verify, mint says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, the mint basically just says + that it doesn't know this reserve. Can happen if we + query before the wire transfer went through. + We should simply pass the JSON reply to the application. */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != wsh->cb) + wsh->cb (wsh->cb_cls, + response_code, + NULL, + json); + json_decref (json); + TALER_MINT_reserve_withdraw_cancel (wsh); +} + + +/** + * Withdraw a coin from the mint using a /reserve/withdraw request. Note + * that to ensure that no money is lost in case of hardware failures, + * the caller must have committed (most of) the arguments to disk + * before calling, and be ready to repeat the request with the same + * arguments in case of failures. + * + * @param mint the mint handle; the mint must be ready to operate + * @param pk kind of coin to create + * @param reserve_priv private key of the reserve to withdraw from + * @param coin_priv where to store the coin's private key, + * caller must have committed this value to disk before the call (with @a pk) + * @param blinding_key where to store the coin's blinding key + * caller must have committed this value to disk before the call (with @a pk) + * @param res_cb the callback to call when the final result for this request is available + * @param res_cb_cls closure for the above callback + * @return #GNUNET_OK on success, #GNUNET_SYSERR + * if the inputs are invalid (i.e. denomination key not with this mint). + * In this case, the callback is not called. + */ +struct TALER_MINT_ReserveWithdrawHandle * +TALER_MINT_reserve_withdraw (struct TALER_MINT_Handle *mint, + const struct TALER_MINT_DenomPublicKey *pk, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + const struct TALER_DenominationBlindingKey *blinding_key, + TALER_MINT_ReserveWithdrawResultCallback res_cb, + void *res_cb_cls) +{ + struct TALER_MINT_ReserveWithdrawHandle *wsh; + struct TALER_WithdrawRequestPS req; + struct TALER_ReserveSignatureP reserve_sig; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_MINT_Context *ctx; + struct TALER_Amount amount_with_fee; + char *coin_ev; + size_t coin_ev_size; + json_t *withdraw_obj; + CURL *eh; + + wsh = GNUNET_new (struct TALER_MINT_ReserveWithdrawHandle); + wsh->mint = mint; + wsh->cb = res_cb; + wsh->cb_cls = res_cb_cls; + wsh->pk = pk; + + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &coin_pub.eddsa_pub); + GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &wsh->c_hash); + coin_ev_size = GNUNET_CRYPTO_rsa_blind (&wsh->c_hash, + blinding_key->rsa_blinding_key, + pk->key.rsa_public_key, + &coin_ev); + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, + &wsh->reserve_pub.eddsa_pub); + req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); + req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); + req.reserve_pub = wsh->reserve_pub; + if (GNUNET_OK != + TALER_amount_add (&amount_with_fee, + &pk->fee_withdraw, + &pk->value)) + { + /* mint gave us denomination keys that overflow like this!? */ + GNUNET_break_op (0); + GNUNET_free (coin_ev); + GNUNET_free (wsh); + return NULL; + } + TALER_amount_hton (&req.amount_with_fee, + &amount_with_fee); + TALER_amount_hton (&req.withdraw_fee, + &pk->fee_withdraw); + GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key, + &req.h_denomination_pub); + GNUNET_CRYPTO_hash (coin_ev, + coin_ev_size, + &req.h_coin_envelope); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, + &req.purpose, + &reserve_sig.eddsa_signature)); + withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub and coin_ev */ + " s:o, s:o}",/* reserve_pub and reserve_sig */ + "denom_pub", TALER_json_from_rsa_public_key (pk->key.rsa_public_key), + "coin_ev", TALER_json_from_data (coin_ev, + coin_ev_size), + "reserve_pub", TALER_json_from_data (&wsh->reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)), + "reserve_sig", TALER_json_from_data (&reserve_sig, + sizeof (reserve_sig))); + GNUNET_free (coin_ev); + + wsh->blinding_key = blinding_key; + wsh->url = MAH_path_to_url (mint, "/reserve/withdraw"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (wsh->json_enc = + json_dumps (withdraw_obj, + JSON_COMPACT))); + json_decref (withdraw_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wsh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + wsh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (wsh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wsh->db)); + ctx = MAH_handle_to_context (mint); + wsh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_reserve_withdraw_finished, + wsh); + return wsh; +} + + +/** + * Cancel a withdraw status request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param sign the withdraw sign request handle + */ +void +TALER_MINT_reserve_withdraw_cancel (struct TALER_MINT_ReserveWithdrawHandle *sign) +{ + if (NULL != sign->job) + { + MAC_job_cancel (sign->job); + sign->job = NULL; + } + GNUNET_free_non_null (sign->db.buf); + GNUNET_free (sign->url); + GNUNET_free (sign->json_enc); + GNUNET_free (sign); +} + + +/* end of mint_api_reserve.c */ diff --git a/src/mint-lib/mint_api_withdraw.c b/src/mint-lib/mint_api_withdraw.c deleted file mode 100644 index ddabb811..00000000 --- a/src/mint-lib/mint_api_withdraw.c +++ /dev/null @@ -1,927 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - 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 - Foundation; either version 3, 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 General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see - -*/ -/** - * @file mint-lib/mint_api_withdraw.c - * @brief Implementation of the /withdraw requests of the mint's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include "taler_mint_service.h" -#include "mint_api_json.h" -#include "mint_api_context.h" -#include "mint_api_handle.h" -#include "taler_signatures.h" - - -/* ********************** /withdraw/status ********************** */ - -/** - * @brief A Withdraw Status Handle - */ -struct TALER_MINT_WithdrawStatusHandle -{ - - /** - * The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct MAC_Job *job; - - /** - * Function to call with the result. - */ - TALER_MINT_WithdrawStatusResultCallback cb; - - /** - * Public key of the reserve we are querying. - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Download buffer - */ - struct MAC_DownloadBuffer db; - -}; - - -/** - * Parse history given in JSON format and return it in binary - * format. - * - * @param[in] history JSON array with the history - * @param reserve_pub public key of the reserve to inspect - * @param currency currency we expect the balance to be in - * @param[out] balance final balance - * @param history_length number of entries in @a history - * @param[out] rhistory array of length @a history_length, set to the - * parsed history entries - * @return #GNUNET_OK if history was valid and @a rhistory and @a balance - * were set, - * #GNUNET_SYSERR if there was a protocol violation in @a history - */ -static int -parse_reserve_history (json_t *history, - const struct TALER_ReservePublicKeyP *reserve_pub, - const char *currency, - struct TALER_Amount *balance, - unsigned int history_length, - struct TALER_MINT_ReserveHistory *rhistory) -{ - struct GNUNET_HashCode uuid[history_length]; - unsigned int uuid_off; - struct TALER_Amount total_in; - struct TALER_Amount total_out; - size_t off; - - TALER_amount_get_zero (currency, - &total_in); - TALER_amount_get_zero (currency, - &total_out); - uuid_off = 0; - for (off=0;offeddsa_pub), - MAJ_spec_end - }; - unsigned int i; - - rhistory[off].type = TALER_MINT_RTT_WITHDRAWAL; - if (GNUNET_OK != - MAJ_parse_json (transaction, - withdraw_spec)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* Check that the signature actually signed a withdraw request */ - if ( (ntohl (purpose->purpose) != TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW) || - (ntohl (purpose->size) != sizeof (struct TALER_WithdrawRequestPS)) ) - { - GNUNET_break_op (0); - MAJ_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - withdraw_purpose = (const struct TALER_WithdrawRequestPS *) purpose; - TALER_amount_ntoh (&amount_from_purpose, - &withdraw_purpose->amount_with_fee); - if (0 != TALER_amount_cmp (&amount, - &amount_from_purpose)) - { - GNUNET_break_op (0); - MAJ_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - rhistory[off].details.out_authorization_sig = json_object_get (transaction, - "signature"); - /* Check check that the same withdraw transaction - isn't listed twice by the mint. We use the - "uuid" array to remember the hashes of all - purposes, and compare the hashes to find - duplicates. */ - GNUNET_CRYPTO_hash (withdraw_purpose, - ntohl (withdraw_purpose->purpose.size), - &uuid[uuid_off]); - for (i=0;ijob = NULL; - json = MAC_download_get_result (&wsh->db, - eh, - &response_code); - switch (response_code) - { - case 0: - break; - case MHD_HTTP_OK: - { - /* TODO: move into separate function... */ - json_t *history; - unsigned int len; - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; - struct MAJ_Specification spec[] = { - MAJ_spec_amount ("balance", &balance), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (json, - spec)) - { - GNUNET_break_op (0); - response_code = 0; - break; - } - history = json_object_get (json, - "history"); - if (NULL == history) - { - GNUNET_break_op (0); - response_code = 0; - break; - } - len = json_array_size (history); - { - struct TALER_MINT_ReserveHistory rhistory[len]; - - if (GNUNET_OK != - parse_reserve_history (history, - &wsh->reserve_pub, - balance.currency, - &balance_from_history, - len, - rhistory)) - { - GNUNET_break_op (0); - response_code = 0; - break; - } - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) - { - /* mint cannot add up balances!? */ - GNUNET_break_op (0); - response_code = 0; - break; - } - wsh->cb (wsh->cb_cls, - response_code, - json, - &balance, - len, - rhistory); - wsh->cb = NULL; - } - } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the mint is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u\n", - response_code); - GNUNET_break (0); - response_code = 0; - break; - } - if (NULL != wsh->cb) - wsh->cb (wsh->cb_cls, - response_code, - json, - NULL, - 0, NULL); - json_decref (json); - TALER_MINT_withdraw_status_cancel (wsh); -} - - -/** - * Submit a request to obtain the transaction history of a reserve - * from the mint. Note that while we return the full response to the - * caller for further processing, we do already verify that the - * response is well-formed (i.e. that signatures included in the - * response are all valid and add up to the balance). If the mint's - * reply is not well-formed, we return an HTTP status code of zero to - * @a cb. - * - * @param mint the mint handle; the mint must be ready to operate - * @param reserve_pub public key of the reserve to inspect - * @param cb the callback to call when a reply for this request is available - * @param cb_cls closure for the above callback - * @return a handle for this request; NULL if the inputs are invalid (i.e. - * signatures fail to verify). In this case, the callback is not called. - */ -struct TALER_MINT_WithdrawStatusHandle * -TALER_MINT_withdraw_status (struct TALER_MINT_Handle *mint, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_MINT_WithdrawStatusResultCallback cb, - void *cb_cls) -{ - struct TALER_MINT_WithdrawStatusHandle *wsh; - struct TALER_MINT_Context *ctx; - CURL *eh; - char *pub_str; - char *arg_str; - - if (GNUNET_YES != - MAH_handle_is_ready (mint)) - { - GNUNET_break (0); - return NULL; - } - pub_str = GNUNET_STRINGS_data_to_string_alloc (reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)); - GNUNET_asprintf (&arg_str, - "/withdraw/status?reserve_pub=%s", - pub_str); - GNUNET_free (pub_str); - wsh = GNUNET_new (struct TALER_MINT_WithdrawStatusHandle); - wsh->mint = mint; - wsh->cb = cb; - wsh->cb_cls = cb_cls; - wsh->reserve_pub = *reserve_pub; - wsh->url = MAH_path_to_url (mint, - arg_str); - GNUNET_free (arg_str); - - eh = curl_easy_init (); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_URL, - wsh->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEFUNCTION, - &MAC_download_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEDATA, - &wsh->db)); - ctx = MAH_handle_to_context (mint); - wsh->job = MAC_job_add (ctx, - eh, - GNUNET_NO, - &handle_withdraw_status_finished, - wsh); - return wsh; -} - - -/** - * Cancel a withdraw status request. This function cannot be used - * on a request handle if a response is already served for it. - * - * @param wsh the withdraw status request handle - */ -void -TALER_MINT_withdraw_status_cancel (struct TALER_MINT_WithdrawStatusHandle *wsh) -{ - if (NULL != wsh->job) - { - MAC_job_cancel (wsh->job); - wsh->job = NULL; - } - GNUNET_free_non_null (wsh->db.buf); - GNUNET_free (wsh->url); - GNUNET_free (wsh); -} - - -/* ********************** /withdraw/sign ********************** */ - -/** - * @brief A Withdraw Sign Handle - */ -struct TALER_MINT_WithdrawSignHandle -{ - - /** - * The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this request. - */ - char *url; - - /** - * JSON encoding of the request to POST. - */ - char *json_enc; - - /** - * Handle for the request. - */ - struct MAC_Job *job; - - /** - * Function to call with the result. - */ - TALER_MINT_WithdrawSignResultCallback cb; - - /** - * Key used to blind the value. - */ - const struct TALER_DenominationBlindingKey *blinding_key; - - /** - * Denomination key we are withdrawing. - */ - const struct TALER_MINT_DenomPublicKey *pk; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Download buffer - */ - struct MAC_DownloadBuffer db; - - /** - * Hash of the public key of the coin we are signing. - */ - struct GNUNET_HashCode c_hash; - - /** - * Public key of the reserve we are withdrawing from. - */ - struct TALER_ReservePublicKeyP reserve_pub; - -}; - - -/** - * We got a 200 OK response for the /withdraw/sign operation. - * Extract the coin's signature and return it to the caller. - * The signature we get from the mint is for the blinded value. - * Thus, we first must unblind it and then should verify its - * validity against our coin's hash. - * - * If everything checks out, we return the unblinded signature - * to the application via the callback. - * - * @param wsh operation handle - * @param json reply from the mint - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static int -withdraw_sign_ok (struct TALER_MINT_WithdrawSignHandle *wsh, - json_t *json) -{ - struct GNUNET_CRYPTO_rsa_Signature *blind_sig; - struct GNUNET_CRYPTO_rsa_Signature *sig; - struct TALER_DenominationSignature dsig; - struct MAJ_Specification spec[] = { - MAJ_spec_rsa_signature ("ev_sig", &blind_sig), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (json, - spec)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, - wsh->blinding_key->rsa_blinding_key, - wsh->pk->key.rsa_public_key); - GNUNET_CRYPTO_rsa_signature_free (blind_sig); - if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&wsh->c_hash, - sig, - wsh->pk->key.rsa_public_key)) - { - GNUNET_break_op (0); - GNUNET_CRYPTO_rsa_signature_free (sig); - return GNUNET_SYSERR; - } - /* signature is valid, return it to the application */ - dsig.rsa_signature = sig; - wsh->cb (wsh->cb_cls, - MHD_HTTP_OK, - &dsig, - json); - /* make sure callback isn't called again after return */ - wsh->cb = NULL; - GNUNET_CRYPTO_rsa_signature_free (sig); - return GNUNET_OK; -} - - -/** - * We got a 402 PAYMENT REQUIRED response for the /withdraw/sign operation. - * Check the signatures on the withdraw transactions in the provided - * history and that the balances add up. We don't do anything directly - * with the information, as the JSON will be returned to the application. - * However, our job is ensuring that the mint followed the protocol, and - * this in particular means checking all of the signatures in the history. - * - * @param wsh operation handle - * @param json reply from the mint - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static int -withdraw_sign_payment_required (struct TALER_MINT_WithdrawSignHandle *wsh, - json_t *json) -{ - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; - struct TALER_Amount requested_amount; - json_t *history; - size_t len; - struct MAJ_Specification spec[] = { - MAJ_spec_amount ("balance", &balance), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (json, - spec)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - history = json_object_get (json, - "history"); - if (NULL == history) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - /* go over transaction history and compute - total incoming and outgoing amounts */ - len = json_array_size (history); - { - struct TALER_MINT_ReserveHistory rhistory[len]; - - if (GNUNET_OK != - parse_reserve_history (history, - &wsh->reserve_pub, - balance.currency, - &balance_from_history, - len, - rhistory)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) - { - /* mint cannot add up balances!? */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* Compute how much we expected to charge to the reserve */ - if (GNUNET_OK != - TALER_amount_add (&requested_amount, - &wsh->pk->value, - &wsh->pk->fee_withdraw)) - { - /* Overflow here? Very strange, our CPU must be fried... */ - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* Check that funds were really insufficient */ - if (0 >= TALER_amount_cmp (&requested_amount, - &balance)) - { - /* Requested amount is smaller or equal to reported balance, - so this should not have failed. */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /withdraw/sign request. - * - * @param cls the `struct TALER_MINT_WithdrawSignHandle` - * @param eh curl handle of the request that finished - */ -static void -handle_withdraw_sign_finished (void *cls, - CURL *eh) -{ - struct TALER_MINT_WithdrawSignHandle *wsh = cls; - long response_code; - json_t *json; - - wsh->job = NULL; - json = MAC_download_get_result (&wsh->db, - eh, - &response_code); - switch (response_code) - { - case 0: - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - withdraw_sign_ok (wsh, - json)) - { - GNUNET_break_op (0); - response_code = 0; - } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the mint is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_PAYMENT_REQUIRED: - /* The mint says that the reserve has insufficient funds; - check the signatures in the history... */ - if (GNUNET_OK != - withdraw_sign_payment_required (wsh, - json)) - { - GNUNET_break_op (0); - response_code = 0; - } - break; - case MHD_HTTP_UNAUTHORIZED: - GNUNET_break (0); - /* Nothing really to verify, mint says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the mint basically just says - that it doesn't know this reserve. Can happen if we - query before the wire transfer went through. - We should simply pass the JSON reply to the application. */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u\n", - response_code); - GNUNET_break (0); - response_code = 0; - break; - } - if (NULL != wsh->cb) - wsh->cb (wsh->cb_cls, - response_code, - NULL, - json); - json_decref (json); - TALER_MINT_withdraw_sign_cancel (wsh); -} - - -/** - * Withdraw a coin from the mint using a /withdraw/sign request. Note - * that to ensure that no money is lost in case of hardware failures, - * the caller must have committed (most of) the arguments to disk - * before calling, and be ready to repeat the request with the same - * arguments in case of failures. - * - * @param mint the mint handle; the mint must be ready to operate - * @param pk kind of coin to create - * @param reserve_priv private key of the reserve to withdraw from - * @param coin_priv where to store the coin's private key, - * caller must have committed this value to disk before the call (with @a pk) - * @param blinding_key where to store the coin's blinding key - * caller must have committed this value to disk before the call (with @a pk) - * @param res_cb the callback to call when the final result for this request is available - * @param res_cb_cls closure for the above callback - * @return #GNUNET_OK on success, #GNUNET_SYSERR - * if the inputs are invalid (i.e. denomination key not with this mint). - * In this case, the callback is not called. - */ -struct TALER_MINT_WithdrawSignHandle * -TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint, - const struct TALER_MINT_DenomPublicKey *pk, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKey *blinding_key, - TALER_MINT_WithdrawSignResultCallback res_cb, - void *res_cb_cls) -{ - struct TALER_MINT_WithdrawSignHandle *wsh; - struct TALER_WithdrawRequestPS req; - struct TALER_ReserveSignatureP reserve_sig; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_MINT_Context *ctx; - struct TALER_Amount amount_with_fee; - char *coin_ev; - size_t coin_ev_size; - json_t *withdraw_obj; - CURL *eh; - - wsh = GNUNET_new (struct TALER_MINT_WithdrawSignHandle); - wsh->mint = mint; - wsh->cb = res_cb; - wsh->cb_cls = res_cb_cls; - wsh->pk = pk; - - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &wsh->c_hash); - coin_ev_size = GNUNET_CRYPTO_rsa_blind (&wsh->c_hash, - blinding_key->rsa_blinding_key, - pk->key.rsa_public_key, - &coin_ev); - GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, - &wsh->reserve_pub.eddsa_pub); - req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); - req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); - req.reserve_pub = wsh->reserve_pub; - if (GNUNET_OK != - TALER_amount_add (&amount_with_fee, - &pk->fee_withdraw, - &pk->value)) - { - /* mint gave us denomination keys that overflow like this!? */ - GNUNET_break_op (0); - GNUNET_free (coin_ev); - GNUNET_free (wsh); - return NULL; - } - TALER_amount_hton (&req.amount_with_fee, - &amount_with_fee); - TALER_amount_hton (&req.withdraw_fee, - &pk->fee_withdraw); - GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key, - &req.h_denomination_pub); - GNUNET_CRYPTO_hash (coin_ev, - coin_ev_size, - &req.h_coin_envelope); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, - &req.purpose, - &reserve_sig.eddsa_signature)); - withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub and coin_ev */ - " s:o, s:o}",/* reserve_pub and reserve_sig */ - "denom_pub", TALER_json_from_rsa_public_key (pk->key.rsa_public_key), - "coin_ev", TALER_json_from_data (coin_ev, - coin_ev_size), - "reserve_pub", TALER_json_from_data (&wsh->reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)), - "reserve_sig", TALER_json_from_data (&reserve_sig, - sizeof (reserve_sig))); - GNUNET_free (coin_ev); - - wsh->blinding_key = blinding_key; - wsh->url = MAH_path_to_url (mint, "/withdraw/sign"); - - eh = curl_easy_init (); - GNUNET_assert (NULL != (wsh->json_enc = - json_dumps (withdraw_obj, - JSON_COMPACT))); - json_decref (withdraw_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_URL, - wsh->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDS, - wsh->json_enc)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDSIZE, - strlen (wsh->json_enc))); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEFUNCTION, - &MAC_download_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEDATA, - &wsh->db)); - ctx = MAH_handle_to_context (mint); - wsh->job = MAC_job_add (ctx, - eh, - GNUNET_YES, - &handle_withdraw_sign_finished, - wsh); - return wsh; -} - - -/** - * Cancel a withdraw status request. This function cannot be used - * on a request handle if a response is already served for it. - * - * @param sign the withdraw sign request handle - */ -void -TALER_MINT_withdraw_sign_cancel (struct TALER_MINT_WithdrawSignHandle *sign) -{ - if (NULL != sign->job) - { - MAC_job_cancel (sign->job); - sign->job = NULL; - } - GNUNET_free_non_null (sign->db.buf); - GNUNET_free (sign->url); - GNUNET_free (sign->json_enc); - GNUNET_free (sign); -} - - -/* end of mint_api_withdraw.c */ diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 96a15207..b2f833d0 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -131,7 +131,7 @@ struct MeltDetails const char *amount; /** - * Reference to withdraw_sign operations for coin to + * Reference to reserve_withdraw operations for coin to * be used for the /refresh/melt operation. */ const char *coin_ref; @@ -243,14 +243,14 @@ struct Command /** * Set to the API's handle during the operation. */ - struct TALER_MINT_WithdrawStatusHandle *wsh; + struct TALER_MINT_ReserveStatusHandle *wsh; /** * Expected reserve balance. */ const char *expected_balance; - } withdraw_status; + } reserve_status; /** * Information for a #OC_WITHDRAW_SIGN command. @@ -296,9 +296,9 @@ struct Command /** * Withdraw handle (while operation is running). */ - struct TALER_MINT_WithdrawSignHandle *wsh; + struct TALER_MINT_ReserveWithdrawHandle *wsh; - } withdraw_sign; + } reserve_withdraw; /** * Information for a #OC_DEPOSIT command. @@ -312,7 +312,7 @@ struct Command const char *amount; /** - * Reference to a withdraw_sign operation for a coin to + * Reference to a reserve_withdraw operation for a coin to * be used for the /deposit operation. */ const char *coin_ref; @@ -649,8 +649,8 @@ compare_admin_add_incoming_history (const struct TALER_MINT_ReserveHistory *h, * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not */ static int -compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, - const struct Command *cmd) +compare_reserve_withdraw_history (const struct TALER_MINT_ReserveHistory *h, + const struct Command *cmd) { struct TALER_Amount amount; struct TALER_Amount amount_with_fee; @@ -661,12 +661,12 @@ compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, return GNUNET_SYSERR; } GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.withdraw_sign.amount, + TALER_string_to_amount (cmd->details.reserve_withdraw.amount, &amount)); GNUNET_assert (GNUNET_OK == TALER_amount_add (&amount_with_fee, &amount, - &cmd->details.withdraw_sign.pk->fee_withdraw)); + &cmd->details.reserve_withdraw.pk->fee_withdraw)); if (0 != TALER_amount_cmp (&amount_with_fee, &h->amount)) { @@ -678,7 +678,7 @@ compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, /** - * Function called with the result of a /withdraw/status request. + * Function called with the result of a /reserve/status request. * * @param cls closure with the interpreter state * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request @@ -689,12 +689,12 @@ compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, * @param history detailed transaction history, NULL on error */ static void -withdraw_status_cb (void *cls, - unsigned int http_status, - json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_MINT_ReserveHistory *history) +reserve_status_cb (void *cls, + unsigned int http_status, + json_t *json, + const struct TALER_Amount *balance, + unsigned int history_length, + const struct TALER_MINT_ReserveHistory *history) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; @@ -703,7 +703,7 @@ withdraw_status_cb (void *cls, unsigned int j; struct TALER_Amount amount; - cmd->details.withdraw_status.wsh = NULL; + cmd->details.reserve_status.wsh = NULL; if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -727,10 +727,10 @@ withdraw_status_cb (void *cls, { case OC_ADMIN_ADD_INCOMING: if ( ( (NULL != rel->label) && - (0 == strcmp (cmd->details.withdraw_status.reserve_reference, + (0 == strcmp (cmd->details.reserve_status.reserve_reference, rel->label) ) ) || ( (NULL != rel->details.admin_add_incoming.reserve_reference) && - (0 == strcmp (cmd->details.withdraw_status.reserve_reference, + (0 == strcmp (cmd->details.reserve_status.reserve_reference, rel->details.admin_add_incoming.reserve_reference) ) ) ) { if (GNUNET_OK != @@ -745,11 +745,11 @@ withdraw_status_cb (void *cls, } break; case OC_WITHDRAW_SIGN: - if (0 == strcmp (cmd->details.withdraw_status.reserve_reference, - rel->details.withdraw_sign.reserve_reference)) + if (0 == strcmp (cmd->details.reserve_status.reserve_reference, + rel->details.reserve_withdraw.reserve_reference)) { if (GNUNET_OK != - compare_withdraw_sign_history (&history[j], + compare_reserve_withdraw_history (&history[j], rel)) { GNUNET_break (0); @@ -770,10 +770,10 @@ withdraw_status_cb (void *cls, fail (is); return; } - if (NULL != cmd->details.withdraw_status.expected_balance) + if (NULL != cmd->details.reserve_status.expected_balance) { GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.withdraw_status.expected_balance, + TALER_string_to_amount (cmd->details.reserve_status.expected_balance, &amount)); if (0 != TALER_amount_cmp (&amount, balance)) @@ -796,7 +796,7 @@ withdraw_status_cb (void *cls, /** - * Function called upon completion of our /withdraw/sign request. + * Function called upon completion of our /reserve/withdraw request. * * @param cls closure with the interpreter state * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request @@ -805,15 +805,15 @@ withdraw_status_cb (void *cls, * @param full_response full response from the mint (for logging, in case of errors) */ static void -withdraw_sign_cb (void *cls, - unsigned int http_status, - const struct TALER_DenominationSignature *sig, - json_t *full_response) +reserve_withdraw_cb (void *cls, + unsigned int http_status, + const struct TALER_DenominationSignature *sig, + json_t *full_response) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; - cmd->details.withdraw_sign.wsh = NULL; + cmd->details.reserve_withdraw.wsh = NULL; if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -834,7 +834,7 @@ withdraw_sign_cb (void *cls, fail (is); return; } - cmd->details.withdraw_sign.sig.rsa_signature + cmd->details.reserve_withdraw.sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); break; case MHD_HTTP_PAYMENT_REQUIRED: @@ -1309,44 +1309,44 @@ interpreter_run (void *cls, return; case OC_WITHDRAW_STATUS: GNUNET_assert (NULL != - cmd->details.withdraw_status.reserve_reference); + cmd->details.reserve_status.reserve_reference); ref = find_command (is, - cmd->details.withdraw_status.reserve_reference); + cmd->details.reserve_status.reserve_reference); GNUNET_assert (NULL != ref); GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub); - cmd->details.withdraw_status.wsh - = TALER_MINT_withdraw_status (mint, - &reserve_pub, - &withdraw_status_cb, - is); + cmd->details.reserve_status.wsh + = TALER_MINT_reserve_status (mint, + &reserve_pub, + &reserve_status_cb, + is); trigger_context_task (); return; case OC_WITHDRAW_SIGN: GNUNET_assert (NULL != - cmd->details.withdraw_sign.reserve_reference); + cmd->details.reserve_withdraw.reserve_reference); ref = find_command (is, - cmd->details.withdraw_sign.reserve_reference); + cmd->details.reserve_withdraw.reserve_reference); GNUNET_assert (NULL != ref); GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - if (NULL != cmd->details.withdraw_sign.amount) + if (NULL != cmd->details.reserve_withdraw.amount) { if (GNUNET_OK != - TALER_string_to_amount (cmd->details.withdraw_sign.amount, + TALER_string_to_amount (cmd->details.reserve_withdraw.amount, &amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse amount `%s' at %u\n", - cmd->details.withdraw_sign.amount, + cmd->details.reserve_withdraw.amount, is->ip); fail (is); return; } - cmd->details.withdraw_sign.pk = find_pk (is->keys, - &amount); + cmd->details.reserve_withdraw.pk = find_pk (is->keys, + &amount); } - if (NULL == cmd->details.withdraw_sign.pk) + if (NULL == cmd->details.reserve_withdraw.pk) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to determine denomination key at %u\n", @@ -1360,22 +1360,22 @@ interpreter_run (void *cls, struct GNUNET_CRYPTO_EddsaPrivateKey *priv; priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.withdraw_sign.coin_priv.eddsa_priv = *priv; + cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv; GNUNET_free (priv); } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.withdraw_sign.coin_priv.eddsa_priv, + GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv, &coin_pub.eddsa_pub); - cmd->details.withdraw_sign.blinding_key.rsa_blinding_key - = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.withdraw_sign.pk->key.rsa_public_key)); - cmd->details.withdraw_sign.wsh - = TALER_MINT_withdraw_sign (mint, - cmd->details.withdraw_sign.pk, - &ref->details.admin_add_incoming.reserve_priv, - &cmd->details.withdraw_sign.coin_priv, - &cmd->details.withdraw_sign.blinding_key, - &withdraw_sign_cb, - is); - if (NULL == cmd->details.withdraw_sign.wsh) + cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key + = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.reserve_withdraw.pk->key.rsa_public_key)); + cmd->details.reserve_withdraw.wsh + = TALER_MINT_reserve_withdraw (mint, + cmd->details.reserve_withdraw.pk, + &ref->details.admin_add_incoming.reserve_priv, + &cmd->details.reserve_withdraw.coin_priv, + &cmd->details.reserve_withdraw.blinding_key, + &reserve_withdraw_cb, + is); + if (NULL == cmd->details.reserve_withdraw.wsh) { GNUNET_break (0); fail (is); @@ -1404,9 +1404,9 @@ interpreter_run (void *cls, switch (ref->oc) { case OC_WITHDRAW_SIGN: - coin_priv = &ref->details.withdraw_sign.coin_priv; - coin_pk = ref->details.withdraw_sign.pk; - coin_pk_sig = &ref->details.withdraw_sign.sig; + coin_priv = &ref->details.reserve_withdraw.coin_priv; + coin_pk = ref->details.reserve_withdraw.pk; + coin_pk_sig = &ref->details.reserve_withdraw.sig; break; case OC_REFRESH_REVEAL: { @@ -1549,7 +1549,7 @@ interpreter_run (void *cls, GNUNET_assert (NULL != ref); GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); - melt_privs[i] = ref->details.withdraw_sign.coin_priv; + melt_privs[i] = ref->details.reserve_withdraw.coin_priv; if (GNUNET_OK != TALER_string_to_amount (md->amount, &melt_amounts[i])) @@ -1561,8 +1561,8 @@ interpreter_run (void *cls, fail (is); return; } - melt_sigs[i] = ref->details.withdraw_sign.sig; - melt_pks[i] = *ref->details.withdraw_sign.pk; + melt_sigs[i] = ref->details.reserve_withdraw.sig; + melt_pks[i] = *ref->details.reserve_withdraw.pk; } for (i=0;idetails.withdraw_sign.amount, + cmd->details.reserve_withdraw.amount, is->ip); fail (is); return; @@ -1639,7 +1639,7 @@ interpreter_run (void *cls, /* find melt command */ ref = find_command (is, ref->details.refresh_reveal.melt_ref); - /* find withdraw_sign command */ + /* find reserve_withdraw command */ { unsigned int idx; const struct MeltDetails *md; @@ -1658,7 +1658,7 @@ interpreter_run (void *cls, /* finally, use private key from withdraw sign command */ cmd->details.refresh_link.rlh = TALER_MINT_refresh_link (mint, - &ref->details.withdraw_sign.coin_priv, + &ref->details.reserve_withdraw.coin_priv, &link_cb, is); if (NULL == cmd->details.refresh_link.rlh) @@ -1724,35 +1724,35 @@ do_shutdown (void *cls, } break; case OC_WITHDRAW_STATUS: - if (NULL != cmd->details.withdraw_status.wsh) + if (NULL != cmd->details.reserve_status.wsh) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Command %u (%s) did not complete\n", i, cmd->label); - TALER_MINT_withdraw_status_cancel (cmd->details.withdraw_status.wsh); - cmd->details.withdraw_status.wsh = NULL; + TALER_MINT_reserve_status_cancel (cmd->details.reserve_status.wsh); + cmd->details.reserve_status.wsh = NULL; } break; case OC_WITHDRAW_SIGN: - if (NULL != cmd->details.withdraw_sign.wsh) + if (NULL != cmd->details.reserve_withdraw.wsh) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Command %u (%s) did not complete\n", i, cmd->label); - TALER_MINT_withdraw_sign_cancel (cmd->details.withdraw_sign.wsh); - cmd->details.withdraw_sign.wsh = NULL; + TALER_MINT_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh); + cmd->details.reserve_withdraw.wsh = NULL; } - if (NULL != cmd->details.withdraw_sign.sig.rsa_signature) + if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature) { - GNUNET_CRYPTO_rsa_signature_free (cmd->details.withdraw_sign.sig.rsa_signature); - cmd->details.withdraw_sign.sig.rsa_signature = NULL; + GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature); + cmd->details.reserve_withdraw.sig.rsa_signature = NULL; } - if (NULL != cmd->details.withdraw_sign.blinding_key.rsa_blinding_key) + if (NULL != cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key) { - GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.withdraw_sign.blinding_key.rsa_blinding_key); - cmd->details.withdraw_sign.blinding_key.rsa_blinding_key = NULL; + GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key); + cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL; } break; case OC_DEPOSIT: @@ -2018,15 +2018,15 @@ run (void *cls, { .oc = OC_WITHDRAW_SIGN, .label = "withdraw-coin-1", .expected_response_code = MHD_HTTP_OK, - .details.withdraw_sign.reserve_reference = "create-reserve-1", - .details.withdraw_sign.amount = "EUR:5" }, + .details.reserve_withdraw.reserve_reference = "create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, /* Check that deposit and withdraw operation are in history, and that the balance is now at zero */ { .oc = OC_WITHDRAW_STATUS, .label = "withdraw-status-1", .expected_response_code = MHD_HTTP_OK, - .details.withdraw_status.reserve_reference = "create-reserve-1", - .details.withdraw_status.expected_balance = "EUR:0" }, + .details.reserve_status.reserve_reference = "create-reserve-1", + .details.reserve_status.expected_balance = "EUR:0" }, /* Try to deposit the 5 EUR coin (in full) */ { .oc = OC_DEPOSIT, .label = "deposit-simple", @@ -2041,8 +2041,8 @@ run (void *cls, { .oc = OC_WITHDRAW_SIGN, .label = "withdraw-coin-2", .expected_response_code = MHD_HTTP_PAYMENT_REQUIRED, - .details.withdraw_sign.reserve_reference = "create-reserve-1", - .details.withdraw_sign.amount = "EUR:5" }, + .details.reserve_withdraw.reserve_reference = "create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, /* Try to double-spend the 5 EUR coin with different wire details */ { .oc = OC_DEPOSIT, @@ -2086,8 +2086,8 @@ run (void *cls, { .oc = OC_WITHDRAW_SIGN, .label = "refresh-withdraw-coin-1", .expected_response_code = MHD_HTTP_OK, - .details.withdraw_sign.reserve_reference = "refresh-create-reserve-1", - .details.withdraw_sign.amount = "EUR:5" }, + .details.reserve_withdraw.reserve_reference = "refresh-create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full) (merchant would receive EUR:0.99 due to 1 ct deposit fee) */ { .oc = OC_DEPOSIT, diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 7d75f9b6..e5d0c656 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -18,7 +18,7 @@ taler_mint_httpd_SOURCES = \ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \ - taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \ + taler-mint-httpd_reserve.c taler-mint-httpd_reserve.h \ taler-mint-httpd_wire.c taler-mint-httpd_wire.h \ taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h taler_mint_httpd_LDADD = \ diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index e68cd242..cd2f6276 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -30,7 +30,7 @@ #include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_admin.h" #include "taler-mint-httpd_deposit.h" -#include "taler-mint-httpd_withdraw.h" +#include "taler-mint-httpd_reserve.h" #include "taler-mint-httpd_wire.h" #include "taler-mint-httpd_refresh.h" #include "taler-mint-httpd_keystate.h" @@ -190,17 +190,17 @@ handle_mhd_request (void *cls, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, /* Withdrawing coins / interaction with reserves */ - { "/withdraw/status", MHD_HTTP_METHOD_GET, "application/json", + { "/reserve/status", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, - &TMH_WITHDRAW_handler_withdraw_status, MHD_HTTP_OK }, - { "/withdraw/status", NULL, "text/plain", + &TMH_RESERVE_handler_reserve_status, MHD_HTTP_OK }, + { "/reserve/status", NULL, "text/plain", "Only GET is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - { "/withdraw/sign", MHD_HTTP_METHOD_POST, "application/json", + { "/reserve/withdraw", MHD_HTTP_METHOD_POST, "application/json", NULL, 0, - &TMH_WITHDRAW_handler_withdraw_sign, MHD_HTTP_OK }, - { "/withdraw/sign", NULL, "text/plain", + &TMH_RESERVE_handler_reserve_withdraw, MHD_HTTP_OK }, + { "/reserve/withdraw", NULL, "text/plain", "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 27031da2..021a3155 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -203,7 +203,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, /** - * Execute a /withdraw/status. Given the public key of a reserve, + * Execute a /reserve/status. Given the public key of a reserve, * return the associated transaction history. * * @param connection the MHD connection to handle @@ -211,8 +211,8 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve_pub) +TMH_DB_execute_reserve_status (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve_pub) { struct TALER_MINTDB_Session *session; struct TALER_MINTDB_ReserveHistory *rh; @@ -233,7 +233,7 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, "{s:s, s:s}", "error", "Reserve not found", "parameter", "withdraw_pub"); - res = TMH_RESPONSE_reply_withdraw_status_success (connection, + res = TMH_RESPONSE_reply_reserve_status_success (connection, rh); TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); @@ -242,7 +242,7 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, /** - * Execute a "/withdraw/sign". Given a reserve and a properly signed + * Execute a "/reserve/withdraw". Given a reserve and a properly signed * request to withdraw a coin, check the balance of the reserve and * if it is sufficient, store the request and return the signed * blinded envelope. @@ -256,12 +256,12 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve, - const struct TALER_DenominationPublicKey *denomination_pub, - const char *blinded_msg, - size_t blinded_msg_len, - const struct TALER_ReserveSignatureP *signature) +TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve, + const struct TALER_DenominationPublicKey *denomination_pub, + const char *blinded_msg, + size_t blinded_msg_len, + const struct TALER_ReserveSignatureP *signature) { struct TALER_MINTDB_Session *session; struct TALER_MINTDB_ReserveHistory *rh; @@ -303,7 +303,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, /* Don't sign again if we have already signed the coin */ if (GNUNET_YES == res) { - res = TMH_RESPONSE_reply_withdraw_sign_success (connection, + res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, &collectable); GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature); GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key); @@ -431,7 +431,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, TMH_KS_release (key_state); TMH_plugin->rollback (TMH_plugin->cls, session); - res = TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (connection, + res = TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection, rh); TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); @@ -475,10 +475,10 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, TMH_plugin->commit (TMH_plugin->cls, session)) { - TALER_LOG_WARNING ("/withdraw/sign transaction commit failed\n"); + TALER_LOG_WARNING ("/reserve/withdraw transaction commit failed\n"); return TMH_RESPONSE_reply_commit_error (connection); } - res = TMH_RESPONSE_reply_withdraw_sign_success (connection, + res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, &collectable); GNUNET_CRYPTO_rsa_signature_free (sig); return res; diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index 59976217..f1d9fbfb 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -41,7 +41,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, /** - * Execute a "/withdraw/status". Given the public key of a reserve, + * Execute a "/reserve/status". Given the public key of a reserve, * return the associated transaction history. * * @param connection the MHD connection to handle @@ -49,12 +49,12 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve_pub); +TMH_DB_execute_reserve_status (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve_pub); /** - * Execute a "/withdraw/sign". Given a reserve and a properly signed + * Execute a "/reserve/withdraw". Given a reserve and a properly signed * request to withdraw a coin, check the balance of the reserve and * if it is sufficient, store the request and return the signed * blinded envelope. @@ -68,12 +68,12 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve, - const struct TALER_DenominationPublicKey *denomination_pub, - const char *blinded_msg, - size_t blinded_msg_len, - const struct TALER_ReserveSignatureP *signature); +TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve, + const struct TALER_DenominationPublicKey *denomination_pub, + const char *blinded_msg, + size_t blinded_msg_len, + const struct TALER_ReserveSignatureP *signature); /** diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h index 62b041e9..9529de8f 100644 --- a/src/mint/taler-mint-httpd_keystate.h +++ b/src/mint/taler-mint-httpd_keystate.h @@ -65,7 +65,7 @@ TMH_KS_release (struct TMH_KS_StateHandle *key_state); enum TMH_KS_DenominationKeyUse { /** - * The key is to be used for a /withdraw/sign or /refresh (mint) + * The key is to be used for a /reserve/withdraw or /refresh (mint) * operation. */ TMH_KS_DKU_WITHDRAW, diff --git a/src/mint/taler-mint-httpd_reserve.c b/src/mint/taler-mint-httpd_reserve.c new file mode 100644 index 00000000..44527809 --- /dev/null +++ b/src/mint/taler-mint-httpd_reserve.c @@ -0,0 +1,185 @@ +/* + This file is part of TALER + Copyright (C) 2014,2015 GNUnet e.V. + + 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 + Foundation; either version 3, 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd_reserve.c + * @brief Handle /reserve/ requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler-mint-httpd_reserve.h" +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_responses.h" +#include "taler-mint-httpd_keystate.h" + + +/** + * Handle a "/reserve/status" request. Parses the + * given "reserve_pub" argument (which should contain the + * EdDSA public key of a reserve) and then respond with the + * status of the reserve. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_status (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + struct TALER_ReservePublicKeyP reserve_pub; + int res; + + res = TMH_PARSE_mhd_request_arg_data (connection, + "reserve_pub", + &reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)); + if (GNUNET_SYSERR == res) + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* parse error */ + return TMH_DB_execute_reserve_status (connection, + &reserve_pub); +} + + +/** + * Handle a "/reserve/withdraw" request. Parses the "reserve_pub" + * EdDSA key of the reserve and the requested "denom_pub" which + * specifies the key/value of the coin to be withdrawn, and checks + * that the signature "reserve_sig" makes this a valid withdrawl + * request from the specified reserve. If so, the envelope + * with the blinded coin "coin_ev" is passed down to execute the + * withdrawl operation. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + json_t *root; + struct TALER_WithdrawRequestPS wsrd; + int res; + struct TALER_DenominationPublicKey denomination_pub; + char *blinded_msg; + size_t blinded_msg_len; + struct TALER_Amount amount; + struct TALER_Amount amount_with_fee; + struct TALER_Amount fee_withdraw; + struct TALER_ReserveSignatureP signature; + struct TALER_MINTDB_DenominationKeyIssueInformation *dki; + struct TMH_KS_StateHandle *ks; + + struct TMH_PARSE_FieldSpecification spec[] = { + TMH_PARSE_member_variable ("coin_ev", + (void **) &blinded_msg, + &blinded_msg_len), + TMH_PARSE_member_fixed ("reserve_pub", + &wsrd.reserve_pub), + TMH_PARSE_member_fixed ("reserve_sig", + &signature), + TMH_PARSE_member_denomination_public_key ("denom_pub", + &denomination_pub), + TMH_PARSE_MEMBER_END + }; + + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + return MHD_NO; + if ( (GNUNET_NO == res) || (NULL == root) ) + return MHD_YES; + res = TMH_PARSE_json_data (connection, + root, + spec); + json_decref (root); + if (GNUNET_OK != res) + return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + ks = TMH_KS_acquire (); + dki = TMH_KS_denomination_key_lookup (ks, + &denomination_pub, + TMH_KS_DKU_WITHDRAW); + if (NULL == dki) + { + TMH_PARSE_release_data (spec); + return TMH_RESPONSE_reply_arg_unknown (connection, + "denom_pub"); + } + TALER_amount_ntoh (&amount, + &dki->issue.properties.value); + TALER_amount_ntoh (&fee_withdraw, + &dki->issue.properties.fee_withdraw); + GNUNET_assert (GNUNET_OK == + TALER_amount_add (&amount_with_fee, + &amount, + &fee_withdraw)); + TALER_amount_hton (&wsrd.amount_with_fee, + &amount_with_fee); + TALER_amount_hton (&wsrd.withdraw_fee, + &fee_withdraw); + TMH_KS_release (ks); + /* verify signature! */ + wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); + wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); + + GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key, + &wsrd.h_denomination_pub); + GNUNET_CRYPTO_hash (blinded_msg, + blinded_msg_len, + &wsrd.h_coin_envelope); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, + &wsrd.purpose, + &signature.eddsa_signature, + &wsrd.reserve_pub.eddsa_pub)) + { + TALER_LOG_WARNING ("Client supplied invalid signature for /reserve/withdraw request\n"); + TMH_PARSE_release_data (spec); + return TMH_RESPONSE_reply_signature_invalid (connection, + "reserve_sig"); + } + res = TMH_DB_execute_reserve_withdraw (connection, + &wsrd.reserve_pub, + &denomination_pub, + blinded_msg, + blinded_msg_len, + &signature); + TMH_PARSE_release_data (spec); + return res; +} + +/* end of taler-mint-httpd_reserve.c */ diff --git a/src/mint/taler-mint-httpd_reserve.h b/src/mint/taler-mint-httpd_reserve.h new file mode 100644 index 00000000..71a779fe --- /dev/null +++ b/src/mint/taler-mint-httpd_reserve.h @@ -0,0 +1,73 @@ +/* + This file is part of TALER + Copyright (C) 2014 GNUnet e.V. + + 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 + Foundation; either version 3, 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd_reserve.h + * @brief Handle /reserve/ requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_RESERVE_H +#define TALER_MINT_HTTPD_RESERVE_H + +#include +#include "taler-mint-httpd.h" + +/** + * Handle a "/reserve/status" request. Parses the + * given "reserve_pub" argument (which should contain the + * EdDSA public key of a reserve) and then respond with the + * status of the reserve. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_status (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +/** + * Handle a "/reserve/withdraw" request. Parses the "reserve_pub" + * EdDSA key of the reserve and the requested "denom_pub" which + * specifies the key/value of the coin to be withdrawn, and checks + * that the signature "reserve_sig" makes this a valid withdrawl + * request from the specified reserve. If so, the envelope + * with the blinded coin "coin_ev" is passed down to execute the + * withdrawl operation. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + +#endif diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index ce04fa41..367b1904 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -630,8 +630,8 @@ compile_reserve_history (const struct TALER_MINTDB_ReserveHistory *rh, * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh) +TMH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh) { json_t *json_balance; json_t *json_history; @@ -654,15 +654,15 @@ TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection, /** * Send reserve status information to client with the * message that we have insufficient funds for the - * requested /withdraw/sign operation. + * requested /reserve/withdraw operation. * * @param connection connection to the client * @param rh reserve history to return * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh) +TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh) { json_t *json_balance; json_t *json_history; @@ -691,7 +691,7 @@ TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *conn * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection, +TMH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection, const struct TALER_MINTDB_CollectableBlindcoin *collectable) { json_t *sig_json; diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index a3535638..deb7dd63 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -245,22 +245,22 @@ TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh); +TMH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh); /** * Send reserve status information to client with the * message that we have insufficient funds for the - * requested /withdraw/sign operation. + * requested /reserve/withdraw operation. * * @param connection connection to the client * @param rh reserve history to return * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh); +TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh); /** @@ -271,8 +271,8 @@ TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *conn * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection, - const struct TALER_MINTDB_CollectableBlindcoin *collectable); +TMH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection, + const struct TALER_MINTDB_CollectableBlindcoin *collectable); /** diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c deleted file mode 100644 index 4f558164..00000000 --- a/src/mint/taler-mint-httpd_withdraw.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014,2015 GNUnet e.V. - - 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 - Foundation; either version 3, 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 Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file taler-mint-httpd_withdraw.c - * @brief Handle /withdraw/ requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include "taler-mint-httpd_withdraw.h" -#include "taler-mint-httpd_parsing.h" -#include "taler-mint-httpd_responses.h" -#include "taler-mint-httpd_keystate.h" - - -/** - * Handle a "/withdraw/status" request. Parses the - * given "reserve_pub" argument (which should contain the - * EdDSA public key of a reserve) and then respond with the - * status of the reserve. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_status (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) -{ - struct TALER_ReservePublicKeyP reserve_pub; - int res; - - res = TMH_PARSE_mhd_request_arg_data (connection, - "reserve_pub", - &reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)); - if (GNUNET_SYSERR == res) - return MHD_NO; /* internal error */ - if (GNUNET_NO == res) - return MHD_YES; /* parse error */ - return TMH_DB_execute_withdraw_status (connection, - &reserve_pub); -} - - -/** - * Handle a "/withdraw/sign" request. Parses the "reserve_pub" - * EdDSA key of the reserve and the requested "denom_pub" which - * specifies the key/value of the coin to be withdrawn, and checks - * that the signature "reserve_sig" makes this a valid withdrawl - * request from the specified reserve. If so, the envelope - * with the blinded coin "coin_ev" is passed down to execute the - * withdrawl operation. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_sign (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) -{ - json_t *root; - struct TALER_WithdrawRequestPS wsrd; - int res; - struct TALER_DenominationPublicKey denomination_pub; - char *blinded_msg; - size_t blinded_msg_len; - struct TALER_Amount amount; - struct TALER_Amount amount_with_fee; - struct TALER_Amount fee_withdraw; - struct TALER_ReserveSignatureP signature; - struct TALER_MINTDB_DenominationKeyIssueInformation *dki; - struct TMH_KS_StateHandle *ks; - - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("coin_ev", (void **) &blinded_msg, &blinded_msg_len), - TMH_PARSE_member_fixed ("reserve_pub", &wsrd.reserve_pub), - TMH_PARSE_member_fixed ("reserve_sig", &signature), - TMH_PARSE_member_denomination_public_key ("denom_pub", &denomination_pub), - TMH_PARSE_MEMBER_END - }; - - res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - return MHD_NO; - if ( (GNUNET_NO == res) || (NULL == root) ) - return MHD_YES; - res = TMH_PARSE_json_data (connection, - root, - spec); - json_decref (root); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - ks = TMH_KS_acquire (); - dki = TMH_KS_denomination_key_lookup (ks, - &denomination_pub, - TMH_KS_DKU_WITHDRAW); - if (NULL == dki) - { - TMH_PARSE_release_data (spec); - return TMH_RESPONSE_reply_arg_unknown (connection, - "denom_pub"); - } - TALER_amount_ntoh (&amount, - &dki->issue.properties.value); - TALER_amount_ntoh (&fee_withdraw, - &dki->issue.properties.fee_withdraw); - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&amount_with_fee, - &amount, - &fee_withdraw)); - TALER_amount_hton (&wsrd.amount_with_fee, - &amount_with_fee); - TALER_amount_hton (&wsrd.withdraw_fee, - &fee_withdraw); - TMH_KS_release (ks); - /* verify signature! */ - wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); - wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); - - GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key, - &wsrd.h_denomination_pub); - GNUNET_CRYPTO_hash (blinded_msg, - blinded_msg_len, - &wsrd.h_coin_envelope); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, - &wsrd.purpose, - &signature.eddsa_signature, - &wsrd.reserve_pub.eddsa_pub)) - { - TALER_LOG_WARNING ("Client supplied invalid signature for /withdraw/sign request\n"); - TMH_PARSE_release_data (spec); - return TMH_RESPONSE_reply_signature_invalid (connection, - "reserve_sig"); - } - res = TMH_DB_execute_withdraw_sign (connection, - &wsrd.reserve_pub, - &denomination_pub, - blinded_msg, - blinded_msg_len, - &signature); - TMH_PARSE_release_data (spec); - return res; -} - -/* end of taler-mint-httpd_withdraw.c */ diff --git a/src/mint/taler-mint-httpd_withdraw.h b/src/mint/taler-mint-httpd_withdraw.h deleted file mode 100644 index 668178b1..00000000 --- a/src/mint/taler-mint-httpd_withdraw.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014 GNUnet e.V. - - 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 - Foundation; either version 3, 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 Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file taler-mint-httpd_withdraw.h - * @brief Handle /withdraw/ requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef TALER_MINT_HTTPD_WITHDRAW_H -#define TALER_MINT_HTTPD_WITHDRAW_H - -#include -#include "taler-mint-httpd.h" - -/** - * Handle a "/withdraw/status" request. Parses the - * given "reserve_pub" argument (which should contain the - * EdDSA public key of a reserve) and then respond with the - * status of the reserve. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_status (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size); - - -/** - * Handle a "/withdraw/sign" request. Parses the "reserve_pub" - * EdDSA key of the reserve and the requested "denom_pub" which - * specifies the key/value of the coin to be withdrawn, and checks - * that the signature "reserve_sig" makes this a valid withdrawl - * request from the specified reserve. If so, the envelope - * with the blinded coin "coin_ev" is passed down to execute the - * withdrawl operation. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_sign (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size); - -#endif diff --git a/src/mintdb/perf_taler_mintdb.c b/src/mintdb/perf_taler_mintdb.c index fd96e797..73708a6c 100644 --- a/src/mintdb/perf_taler_mintdb.c +++ b/src/mintdb/perf_taler_mintdb.c @@ -287,28 +287,28 @@ main (int argc, char ** argv) PERF_TALER_MINTDB_INIT_CMD_DEBUG ("End of transaction loading"), PERF_TALER_MINTDB_INIT_CMD_GET_TIME ("27 - start"), - PERF_TALER_MINTDB_INIT_CMD_LOOP ("27 - /withdraw/sign", + PERF_TALER_MINTDB_INIT_CMD_LOOP ("27 - /reserve/withdraw", NB_WITHDRAW_SAVE), PERF_TALER_MINTDB_INIT_CMD_LOAD_ARRAY ("27 - reserve", - "27 - /withdraw/sign", + "27 - /reserve/withdraw", "02 - save reserve"), PERF_TALER_MINTDB_INIT_CMD_LOAD_ARRAY ("27 - dki", - "27 - /withdraw/sign", + "27 - /reserve/withdraw", "01 - save denomination"), PERF_TALER_MINTDB_INIT_CMD_WITHDRAW_SIGN ("", "27 - dki", "27 - reserve"), PERF_TALER_MINTDB_INIT_CMD_END_LOOP ("", - "27 - /withdraw/sign"), + "27 - /reserve/withdraw"), PERF_TALER_MINTDB_INIT_CMD_GET_TIME ("27 - end"), PERF_TALER_MINTDB_INIT_CMD_GAUGER ("", "27 - start", "27 - end", "POSTGRES", - "Number of /withdraw/sign per second", + "Number of /reserve/withdraw per second", "item/sec", NB_WITHDRAW_SAVE), - PERF_TALER_MINTDB_INIT_CMD_DEBUG ("End of /withdraw/sign"), + PERF_TALER_MINTDB_INIT_CMD_DEBUG ("End of /reserve/withdraw"), PERF_TALER_MINTDB_INIT_CMD_GET_TIME ("28 - start"), PERF_TALER_MINTDB_INIT_CMD_LOOP ("28 - /deposit", diff --git a/src/mintdb/perf_taler_mintdb_interpreter.h b/src/mintdb/perf_taler_mintdb_interpreter.h index b9283e84..722d1a07 100644 --- a/src/mintdb/perf_taler_mintdb_interpreter.h +++ b/src/mintdb/perf_taler_mintdb_interpreter.h @@ -363,7 +363,7 @@ /** - * The /withdraw/sign api call + * The /reserve/withdraw api call * * Exposes #PERF_TALER_MINTDB_COIN * diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 86db0a11..0cc76f79 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -593,7 +593,7 @@ postgres_prepare (PGconn *db_conn) 1, NULL); /* Used in #postgres_insert_withdraw_info() to store the signature of a blinded coin with the blinded coin's - details before returning it during /withdraw/sign. We store + details before returning it during /reserve/withdraw. We store the coin's denomination information (public key, signature) and the blinded message as well as the reserve that the coin is being withdrawn from and the signature of the message @@ -616,9 +616,9 @@ postgres_prepare (PGconn *db_conn) "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);", 12, NULL); /* Used in #postgres_get_withdraw_info() to - locate the response for a /withdraw/sign request + locate the response for a /reserve/withdraw request using the hash of the blinded message. Used to - make sure /withdraw/sign requests are idempotent. */ + make sure /reserve/withdraw requests are idempotent. */ PREPARE ("get_withdraw_info", "SELECT" " denom_pub" @@ -636,7 +636,7 @@ postgres_prepare (PGconn *db_conn) " WHERE h_blind_ev=$1", 1, NULL); /* Used during #postgres_get_reserve_history() to - obtain all of the /withdraw/sign operations that + obtain all of the /reserve/withdraw operations that have been performed on a given reserve. (i.e. to demonstrate double-spending) */ PREPARE ("get_reserves_out", @@ -1502,7 +1502,7 @@ postgres_reserves_in_insert (void *cls, /** - * Locate the response for a /withdraw/sign request under the + * Locate the response for a /reserve/withdraw request under the * key of the hash of the blinded message. * * @param cls the `struct PostgresClosure` with the plugin-specific state -- cgit v1.2.3 From a6f8fa98b0253091cae0f2e38fb5fcec47e115fa Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 20 Sep 2015 13:48:15 +0200 Subject: implement #3972: support multiple wire formats concurrently --- .gitignore | 1 + src/include/taler_json_lib.h | 4 ++-- src/mint-lib/test_mint_api.c | 4 ++-- src/mint/taler-mint-httpd.c | 28 ++++++++++++++++++++++++++-- src/mint/taler-mint-httpd.h | 6 ++++-- src/mint/taler-mint-httpd_admin.c | 2 +- src/mint/taler-mint-httpd_deposit.c | 2 +- src/mint/taler-mint-httpd_wire.c | 37 +++++++++++++++++++++++++++---------- src/util/test_wireformats.c | 24 ++++++++++++++++-------- src/util/wireformats.c | 26 +++++++++++++------------- 10 files changed, 93 insertions(+), 41 deletions(-) (limited to 'src/mint-lib') diff --git a/.gitignore b/.gitignore index 851e442c..31f08e1d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ GTAGS src/lib/test_mint_api doc/doxygen/doxygen_sqlite3.db src/mint-lib/test_mint_api +src/mint-tools/taler-auditor-sign src/mint-tools/taler-mint-dbinit src/mint-tools/taler-mint-keycheck src/mint-tools/taler-mint-keyup diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 5a13b9bc..63cb8179 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -167,12 +167,12 @@ TALER_hash_json (json_t *json, /** * Check if the given wire format JSON object is correctly formatted * - * @param type the type of the wire format + * @param allowed NULL-terminated array of allowed wire format types * @param wire the JSON wire format object * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not */ int -TALER_json_validate_wireformat (const char *type, +TALER_json_validate_wireformat (const char **allowed, const json_t *wire); diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index b2f833d0..6fb09909 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -1992,13 +1992,13 @@ run (void *cls, #if WIRE_TEST { .oc = OC_WIRE, - .label = "wire", + .label = "wire-test", /* /wire/test replies with a 302 redirect */ .expected_response_code = MHD_HTTP_FOUND }, #endif #if WIRE_SEPA { .oc = OC_WIRE, - .label = "wire", + .label = "wire-sepa", /* /wire/sepa replies with a 200 redirect */ .expected_response_code = MHD_HTTP_OK }, #endif diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index cd2f6276..86d20fdb 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -62,8 +62,10 @@ struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key; /** * In which format does this MINT expect wiring instructions? + * NULL-terminated array of 0-terminated wire format types, + * suitable for passing to #TALER_json_validate_wireformat(). */ -char *TMH_expected_wire_format; +const char **TMH_expected_wire_formats; /** * Our DB plugin. @@ -363,6 +365,9 @@ mint_serve_process_config (const char *mint_directory) { unsigned long long port; char *TMH_master_public_key_str; + char *wireformats; + const char *token; + unsigned int len; cfg = TALER_config_load (mint_directory); if (NULL == cfg) @@ -390,17 +395,36 @@ mint_serve_process_config (const char *mint_directory) (unsigned int) TALER_CURRENCY_LEN); return GNUNET_SYSERR; } + /* Find out list of supported wire formats */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "mint", "wireformat", - &TMH_expected_wire_format)) + &wireformats)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "mint", "wireformat"); return GNUNET_SYSERR; } + /* build NULL-terminated array of TMH_expected_wire_formats */ + TMH_expected_wire_formats = GNUNET_new_array (1, + const char *); + len = 1; + for (token = strtok (wireformats, + " "); + NULL != token; + token = strtok (NULL, + " ")) + { + /* Grow by 1, appending NULL-terminator */ + GNUNET_array_append (TMH_expected_wire_formats, + len, + NULL); + TMH_expected_wire_formats[len - 2] = GNUNET_strdup (token); + } + GNUNET_free (wireformats); + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "mint", diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h index a54e5aa2..3cbbc0df 100644 --- a/src/mint/taler-mint-httpd.h +++ b/src/mint/taler-mint-httpd.h @@ -48,9 +48,11 @@ extern int TMH_test_mode; extern char *TMH_mint_directory; /** - * In which format does this MINT expect wiring instructions? + * In which formats does this MINT expect wiring instructions? + * NULL-terminated array of 0-terminated wire format types, + * suitable for passing to #TALER_json_validate_wireformat(). */ -extern char *TMH_expected_wire_format; +extern const char **TMH_expected_wire_formats; /** * Master public key (according to the diff --git a/src/mint/taler-mint-httpd_admin.c b/src/mint/taler-mint-httpd_admin.c index 5fdfa58e..3e3f13ad 100644 --- a/src/mint/taler-mint-httpd_admin.c +++ b/src/mint/taler-mint-httpd_admin.c @@ -142,7 +142,7 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh, return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } if (GNUNET_YES != - TALER_json_validate_wireformat (TMH_expected_wire_format, + TALER_json_validate_wireformat (TMH_expected_wire_formats, wire)) { TMH_PARSE_release_data (spec); diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index 5725cd1c..c6ebe1d0 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -160,7 +160,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, return MHD_YES; /* failure */ if (GNUNET_YES != - TALER_json_validate_wireformat (TMH_expected_wire_format, + TALER_json_validate_wireformat (TMH_expected_wire_formats, wire)) { TMH_PARSE_release_data (spec); diff --git a/src/mint/taler-mint-httpd_wire.c b/src/mint/taler-mint-httpd_wire.c index 13f23e8e..68dc1419 100644 --- a/src/mint/taler-mint-httpd_wire.c +++ b/src/mint/taler-mint-httpd_wire.c @@ -45,23 +45,32 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh, struct TALER_MintPublicKeyP pub; struct TALER_MintSignatureP sig; json_t *methods; + struct GNUNET_HashContext *hc; + unsigned int i; + const char *wf; + methods = json_array (); + hc = GNUNET_CRYPTO_hash_context_start (); + for (i=0;NULL != (wf = TMH_expected_wire_formats[i]); i++) + { + json_array_append_new (methods, + json_string (wf)); + GNUNET_CRYPTO_hash_context_read (hc, + wf, + strlen (wf) + 1); + } wsm.purpose.size = htonl (sizeof (wsm)); wsm.purpose.purpose = htonl (TALER_SIGNATURE_MINT_WIRE_TYPES); - GNUNET_CRYPTO_hash (TMH_expected_wire_format, - strlen (TMH_expected_wire_format) + 1, - &wsm.h_wire_types); + GNUNET_CRYPTO_hash_context_finish (hc, + &wsm.h_wire_types); TMH_KS_sign (&wsm.purpose, &pub, &sig); - methods = json_array (); /* NOTE: for now, we only support *ONE* wire format per mint instance; if we supply multiple, we need to add the strings for each type separately here -- and hash the 0-terminated strings above differently as well... See #3972. */ - json_array_append_new (methods, - json_string (TMH_expected_wire_format)); return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, "{s:o, s:o, s:o}", @@ -93,6 +102,7 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh, struct MHD_Response *response; int ret; char *wire_test_redirect; + unsigned int i; response = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); @@ -101,8 +111,11 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh, GNUNET_break (0); return MHD_NO; } - if (0 != strcasecmp ("test", - TMH_expected_wire_format)) + for (i=0;NULL != TMH_expected_wire_formats[i];i++) + if (0 == strcasecmp ("test", + TMH_expected_wire_formats[i])) + break; + if (NULL == TMH_expected_wire_formats[i]) { /* Return 501: not implemented */ ret = MHD_queue_response (connection, @@ -155,9 +168,13 @@ TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh, char *sepa_wire_file; int fd; struct stat sbuf; + unsigned int i; - if (0 != strcasecmp ("sepa", - TMH_expected_wire_format)) + for (i=0;NULL != TMH_expected_wire_formats[i];i++) + if (0 == strcasecmp ("sepa", + TMH_expected_wire_formats[i])) + break; + if (NULL == TMH_expected_wire_formats[i]) { /* Return 501: not implemented */ response = MHD_create_response_from_buffer (0, NULL, diff --git a/src/util/test_wireformats.c b/src/util/test_wireformats.c index ebb96604..e4bd238f 100644 --- a/src/util/test_wireformats.c +++ b/src/util/test_wireformats.c @@ -15,9 +15,9 @@ */ /** - * @file util/test_json_validations.c + * @file util/test_wireformats.c * @brief Tests for JSON validations - * @author Sree Harsha Totakura + * @author Sree Harsha Totakura */ #include "platform.h" @@ -65,10 +65,18 @@ static const char * const unsupported_wire_str = \"address\": \"foobar\"}"; -int -main(int argc, +int +main(int argc, const char *const argv[]) { + const char *unsupported[] = { + "unsupported", + NULL + }; + const char *sepa[] = { + "SEPA", + NULL + }; json_t *wire; json_error_t error; int ret; @@ -76,16 +84,16 @@ main(int argc, GNUNET_log_setup ("test-json-validations", "WARNING", NULL); (void) memset(&error, 0, sizeof(error)); GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat ("unsupported", wire)); + GNUNET_assert (1 != TALER_json_validate_wireformat (unsupported, wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat ("SEPA", wire)); + GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL))); - GNUNET_assert (1 != TALER_json_validate_wireformat ("SEPA", wire)); + GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire)); json_decref (wire); GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); - ret = TALER_json_validate_wireformat ("SEPA", wire); + ret = TALER_json_validate_wireformat (sepa, wire); json_decref (wire); if (1 == ret) return 0; diff --git a/src/util/wireformats.c b/src/util/wireformats.c index 88af4a43..5f967852 100644 --- a/src/util/wireformats.c +++ b/src/util/wireformats.c @@ -395,12 +395,12 @@ struct FormatHandler /** * Check if the given wire format JSON object is correctly formatted * - * @param type the expected type of the wire format + * @param allowed NULL-terminated array of allowed wire format types * @param wire the JSON wire format object * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not */ int -TALER_json_validate_wireformat (const char *type, +TALER_json_validate_wireformat (const char **allowed, const json_t *wire) { static const struct FormatHandler format_handlers[] = { @@ -409,33 +409,33 @@ TALER_json_validate_wireformat (const char *type, { NULL, NULL} }; unsigned int i; - char *stype; + const char *stype; json_error_t error; UNPACK_EXITIF (0 != json_unpack_ex ((json_t *) wire, &error, 0, - "{" - "s:s " /* TYPE: type */ - "}", + "{s:s}", "type", &stype)); - if (0 != strcasecmp (type, - stype)) + for (i=0;NULL != allowed[i];i++) + if (0 == strcasecmp (allowed[i], + stype)) + break; + if (NULL == allowed[i]) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Wireformat `%s' does not match mint's expected `%s' format\n", - stype, - type); + "Wireformat `%s' does not match mint's allowed formats\n", + stype); return GNUNET_NO; } for (i=0;NULL != format_handlers[i].type;i++) if (0 == strcasecmp (format_handlers[i].type, - type)) + stype)) return format_handlers[i].handler (wire); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Wireformat `%s' not supported\n", - type); + stype); return GNUNET_NO; EXITIF_exit: return GNUNET_NO; -- cgit v1.2.3 From 16ed21afe071277d797b575a0d15fa29bd969c0b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 20 Sep 2015 14:02:10 +0200 Subject: expanding test to cover multi-wire format scenarios (#3972) --- contrib/mint-template/config/mint-common.conf | 3 ++- src/mint-lib/mint_api_wire.c | 3 ++- .../test-mint-home/config/mint-common.conf | 8 +++---- src/mint-lib/test_mint_api.c | 27 ++++++++++++++-------- src/mint/taler-mint-httpd_wire.c | 5 ---- 5 files changed, 25 insertions(+), 21 deletions(-) (limited to 'src/mint-lib') diff --git a/contrib/mint-template/config/mint-common.conf b/contrib/mint-template/config/mint-common.conf index 958763b2..78a3310a 100644 --- a/contrib/mint-template/config/mint-common.conf +++ b/contrib/mint-template/config/mint-common.conf @@ -2,7 +2,8 @@ # Currency supported by the mint (can only be one) CURRENCY = EUR -# Wire format supported by the mint (currently only SEPA is implemented) +# Wire format supported by the mint, case-insensitive. +# Examples for formats include 'test' for testing and 'sepa' (for EU IBAN). WIREFORMAT = SEPA # HTTP port the mint listens to diff --git a/src/mint-lib/mint_api_wire.c b/src/mint-lib/mint_api_wire.c index f1bbb099..81506961 100644 --- a/src/mint-lib/mint_api_wire.c +++ b/src/mint-lib/mint_api_wire.c @@ -289,7 +289,8 @@ handle_wire_method_finished (void *cls, /* pass on successful reply */ wh->cb (wh->cb_cls, response_code, - NULL, + json_string_value (json_array_get (wh->methods, + wh->methods_off-1)), json); /* trigger request for the next /wire/method */ request_wire_method (wh); diff --git a/src/mint-lib/test-mint-home/config/mint-common.conf b/src/mint-lib/test-mint-home/config/mint-common.conf index eb2f7e90..f4bc2162 100644 --- a/src/mint-lib/test-mint-home/config/mint-common.conf +++ b/src/mint-lib/test-mint-home/config/mint-common.conf @@ -3,11 +3,9 @@ CURRENCY = EUR # Wire format supported by the mint -# We use 'test' for testing, in principle we should -# run tests for all supported wire formats... -# (we should first implement support for a mint running -# with multiple formats at the same time). -WIREFORMAT = test +# We use 'test' for testing of the actual +# coin operations, and 'sepa' to test SEPA-specific routines. +WIREFORMAT = test sepa # HTTP port the mint listens to PORT = 8081 diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 6fb09909..7b96f657 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -33,9 +33,8 @@ /** * Is the configuration file is set to include wire format 'sepa'? - * Needs #3972 to be solved first. */ -#define WIRE_SEPA 0 +#define WIRE_SEPA 1 /** * Main execution context for the main loop. @@ -464,6 +463,11 @@ struct Command */ struct TALER_MINT_WireHandle *wh; + /** + * Format we expect to see, others will be *ignored*. + */ + const char *format; + } wire; } details; @@ -1038,10 +1042,6 @@ link_cb (void *cls, return; } /* check that the coins match */ - fprintf (stderr, - "Got %u coins\n", - num_coins); - for (i=0;idetails.wire.wh = NULL; } + else if ( (NULL != method) && + (0 != strcasecmp (method, + cmd->details.wire.format)) ) + { + /* not the method we care about, skip */ + return; + } if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -1994,13 +2001,15 @@ run (void *cls, { .oc = OC_WIRE, .label = "wire-test", /* /wire/test replies with a 302 redirect */ - .expected_response_code = MHD_HTTP_FOUND }, + .expected_response_code = MHD_HTTP_FOUND, + .details.wire.format = "test" }, #endif #if WIRE_SEPA - { .oc = OC_WIRE, + { .oc = OC_WIRE, .label = "wire-sepa", /* /wire/sepa replies with a 200 redirect */ - .expected_response_code = MHD_HTTP_OK }, + .expected_response_code = MHD_HTTP_OK, + .details.wire.format = "sepa" }, #endif /* *************** end of /wire testing ************** */ diff --git a/src/mint/taler-mint-httpd_wire.c b/src/mint/taler-mint-httpd_wire.c index 68dc1419..143d7c48 100644 --- a/src/mint/taler-mint-httpd_wire.c +++ b/src/mint/taler-mint-httpd_wire.c @@ -66,11 +66,6 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh, TMH_KS_sign (&wsm.purpose, &pub, &sig); - /* NOTE: for now, we only support *ONE* wire format per - mint instance; if we supply multiple, we need to - add the strings for each type separately here -- and - hash the 0-terminated strings above differently as well... - See #3972. */ return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, "{s:o, s:o, s:o}", -- cgit v1.2.3 From ec0131bc1d27774429d289c1151dc0d8f48c76b5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 21 Sep 2015 15:41:19 +0200 Subject: change tests to use persistent DB to vastly improve performance --- src/mint-lib/test-mint-home/config/mint-common.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mint-lib') diff --git a/src/mint-lib/test-mint-home/config/mint-common.conf b/src/mint-lib/test-mint-home/config/mint-common.conf index f4bc2162..5bf7f730 100644 --- a/src/mint-lib/test-mint-home/config/mint-common.conf +++ b/src/mint-lib/test-mint-home/config/mint-common.conf @@ -17,7 +17,7 @@ MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG DB = postgres # This is a testcase, use transient DB actions -TESTRUN = YES +TESTRUN = NO [mintdb-postgres] -- cgit v1.2.3 From 8ae03a690b9695e6edf12e6be4895248408cfd7e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 21 Sep 2015 15:45:03 +0200 Subject: here we want 'yes' --- src/mint-lib/test-mint-home/config/mint-common.conf | 4 ++-- src/mint/test_taler_mint_httpd_afl.sh | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/mint-lib') diff --git a/src/mint-lib/test-mint-home/config/mint-common.conf b/src/mint-lib/test-mint-home/config/mint-common.conf index 5bf7f730..b2b94826 100644 --- a/src/mint-lib/test-mint-home/config/mint-common.conf +++ b/src/mint-lib/test-mint-home/config/mint-common.conf @@ -16,8 +16,8 @@ MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG # How to access our database DB = postgres -# This is a testcase, use transient DB actions -TESTRUN = NO +# Is this is a testcase, use transient DB actions? +TESTRUN = YES [mintdb-postgres] diff --git a/src/mint/test_taler_mint_httpd_afl.sh b/src/mint/test_taler_mint_httpd_afl.sh index a5ae081e..bbcf8edb 100755 --- a/src/mint/test_taler_mint_httpd_afl.sh +++ b/src/mint/test_taler_mint_httpd_afl.sh @@ -23,6 +23,8 @@ # # Setup keys. taler-mint-keyup -d test-mint-home -m test-mint-home/master.priv +# Setup database (just to be sure) +taler-mint-dbinit -d test-mint-home &> /dev/null || true # Only log hard errors, we expect lots of warnings... export GNUNET_FORCE_LOG="taler-mint-httpd;;;;ERROR/libmicrohttpd;;;;ERROR/" # Run test... -- cgit v1.2.3 From 74dfa2dfe0995ad9c01fbd499db7ff11dc2ede95 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 21 Sep 2015 15:52:07 +0200 Subject: AFL test generation scripts --- src/mint-lib/afl-generate.sh | 34 ++++++++++++++++++++++++++++ src/mint-lib/baseline/admin_add_incoming.req | 7 ++++++ src/mint-lib/baseline/deposit.req | 8 +++++++ src/mint-lib/baseline/keys.req | 7 ++++++ src/mint-lib/baseline/refresh_link.req | 5 ++++ src/mint-lib/baseline/refresh_melt.req | 8 +++++++ src/mint-lib/baseline/refresh_reveal.req | 7 ++++++ src/mint-lib/baseline/reserve_status.req | 4 ++++ src/mint-lib/baseline/reserve_withdraw.req | 7 ++++++ src/mint-lib/baseline/wire.req | 5 ++++ src/mint-lib/baseline/wire_sepa.req | 5 ++++ src/mint-lib/baseline/wire_test.req | 5 ++++ 12 files changed, 102 insertions(+) create mode 100644 src/mint-lib/afl-generate.sh create mode 100644 src/mint-lib/baseline/admin_add_incoming.req create mode 100644 src/mint-lib/baseline/deposit.req create mode 100644 src/mint-lib/baseline/keys.req create mode 100644 src/mint-lib/baseline/refresh_link.req create mode 100644 src/mint-lib/baseline/refresh_melt.req create mode 100644 src/mint-lib/baseline/refresh_reveal.req create mode 100644 src/mint-lib/baseline/reserve_status.req create mode 100644 src/mint-lib/baseline/reserve_withdraw.req create mode 100644 src/mint-lib/baseline/wire.req create mode 100644 src/mint-lib/baseline/wire_sepa.req create mode 100644 src/mint-lib/baseline/wire_test.req (limited to 'src/mint-lib') diff --git a/src/mint-lib/afl-generate.sh b/src/mint-lib/afl-generate.sh new file mode 100644 index 00000000..4b505186 --- /dev/null +++ b/src/mint-lib/afl-generate.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# This file is part of TALER +# Copyright (C) 2015 GNUnet e.V. +# +# 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 +# Foundation; either version 3, 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License along with +# TALER; see the file COPYING. If not, If not, see +# +# +# This will generate testcases in a directory 'afl-tests', which can then +# be moved into src/mint/afl-tests/ to be run during mint-testing. +# +# This script uses American Fuzzy Loop (AFL) to fuzz the mint to +# automatically create tests with good coverage. You must install +# AFL and set AFL_HOME to the directory where AFL is installed +# before running. Also, a directory "baseline/" should exist with +# templates for inputs for AFL to fuzz. These can be generated +# by running wireshark on loopback while running 'make check' in +# this directory. Save each HTTP request to a new file. +# +# Note that you want to switch 'TESTRUN = NO' and pre-init the +# database before running this, otherwise it will be awfully slow. +# +# Must be run from this directory. +# +$AFL_HOME/afl-fuzz -i baseline/ -m 250 -o afl-tests/ -f /tmp/afl-input taler-mint-httpd -f /tmp/afl-input -d test-mint-home/ -C diff --git a/src/mint-lib/baseline/admin_add_incoming.req b/src/mint-lib/baseline/admin_add_incoming.req new file mode 100644 index 00000000..677678b5 --- /dev/null +++ b/src/mint-lib/baseline/admin_add_incoming.req @@ -0,0 +1,7 @@ +POST /admin/add/incoming HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json +Content-Length: 220 + +{"reserve_pub":"TMZCK5CFM1KZQGY1WTF0CEZZPGA0670G94969RF79PA5106ARTK0","amount":{"currency":"EUR","value":5,"fraction":10000},"execution_date":"/Date(1442821651)/","wire":{"type":"TEST","bank":"source bank","account":42}} \ No newline at end of file diff --git a/src/mint-lib/baseline/deposit.req b/src/mint-lib/baseline/deposit.req new file mode 100644 index 00000000..f50d83e4 --- /dev/null +++ b/src/mint-lib/baseline/deposit.req @@ -0,0 +1,8 @@ +POST /deposit HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json +Content-Length: 1658 +Expect: 100-continue + +{"ub_sig":"51SPJSSDESGPR80A40M74WV140520818ECG26E9M8S0M6CSH6X334GSN8RW30D9G8MT46CA660W34GSG6MT4AD9K8GT3ECSH6MVK0E2374V38H1M8MR4CDJ66MWK4E1S6MR3GCT28CV32H1Q8N23GCHG70S36C1K8MS3GCSN8RV36D9S710KGD9K6GWKEGJ28GRM4CJ56X1K6DJ18D2KGHA46D13GDA66GVK4GHJ8N13AE9J8RVK6GT184S48E1K6X336G9Q8N142CJ4692M6EA16GRKJD9N6523ADA36X13GG9G70TK6DHN68R36CT18GR4CDSJ6CW3GCT364W46CSR8RV42GJ474SMADSH851K4H9Q8GS42CHS8RV3GCSJ64V46DSN8RSM6HHN6N246D9S6934AH9P6X23JGSH652K0DJ5612KJGA26N242CH35452081918G2J2G0","timestamp":"/Date(1442821652)/","f":{"currency":"EUR","value":5,"fraction":0},"wire":{"type":"TEST","bank":"dest bank","account":42},"coin_pub":"JXWK4NS0H2W4V4BETQ90CCEDADP6QQ3MV3YZ7RV2KXEM8PWXE8Q0","H_wire":"YQED9FDYPKK2QQYB3FS19Y15ZMKBAXJP2C73CXASAF1KM6ZYY723TEJ3HBR6D864A7X5W58G92QJ0A9PFMZNB81ZP9NJAQQCCABM4RG","H_contract":"1CMEEFQ5S4QJGGAMVYFV07XQRHQA311CR2MTRNC5M9KZV6ETDV1SY00WJFEV2CG9BXQTEQPZAF8A54C2HX32TZCN20VBGPFPS2Z16B0","merchant_pub":"C36TEXQXFW00170C2EJ66ZR0000CX9VPZNZG00109NX020000000","denom_pub":"51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GT58S2K2HJ16H336C9N8CVK4E9N6H1MADHH61330HHM6N1K8E1H8RVKJH256D1M6E1K8RWKJGSH8S2M6DJ170TK2H266GTK8DSS64RKJDJ26D144DJ474SK0GHQ711MAD9G752M2CJ58S1KJDA570SK2E9G8N23GCJ28S146DHH610K2H1Q8CW3GGA16S146H9G68TKACSQ6914CE1H691K2E9N6RWM8H9P8CWM2H9S8GSK0H9P6D1K6H9G6X0M4C2171144HJ46N334H9J692M4H9M8MR4CCJ46GRKEGA46533CDJ38MV4CH9K892MAH1P8S2K6D9K6N246E256H244G9Q6D346GJ56S23JGHJ690KADHJ8H242H2575132CSM6X1M4G9N6RR48E9H8MVM8E9354520818CMG26C1H60R30C935452081918G2J2G0","transaction_id":1,"refund_deadline":"/Date(0)/","coin_sig":"X16E0DP8C2BJNVNX09G24FFC5GA4W7RN2YXZP9WJTAN9BY6B4GMA39QNYR51XNNEZ3H1J7TP0K9G55JZ8V7WS7CZMD7E64HWYBFWM00"} \ No newline at end of file diff --git a/src/mint-lib/baseline/keys.req b/src/mint-lib/baseline/keys.req new file mode 100644 index 00000000..a9503a86 --- /dev/null +++ b/src/mint-lib/baseline/keys.req @@ -0,0 +1,7 @@ +GET /keys HTTP/1.1 +User-Agent: Wget/1.16.3 (linux-gnu) +Accept: */* +Accept-Encoding: identity +Host: 127.0.0.1:8081 +Connection: Keep-Alive + diff --git a/src/mint-lib/baseline/refresh_link.req b/src/mint-lib/baseline/refresh_link.req new file mode 100644 index 00000000..acf3dff5 --- /dev/null +++ b/src/mint-lib/baseline/refresh_link.req @@ -0,0 +1,5 @@ +GET /refresh/link?coin_pub=WQHES0X5XK43VBG1Y8FXR2YEJM04HQVMDTCS07MH691XWADG8QCG HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json + diff --git a/src/mint-lib/baseline/refresh_melt.req b/src/mint-lib/baseline/refresh_melt.req new file mode 100644 index 00000000..98b5b638 --- /dev/null +++ b/src/mint-lib/baseline/refresh_melt.req @@ -0,0 +1,8 @@ +POST /refresh/melt HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json +Content-Length: 34136 +Expect: 100-continue + +{"new_denoms":["51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSN8CV38D9G8MT46CSG650M4GHQ6N0M6HHR6X142EA26H13JG9N6RV3AD1P6GV34HHK8S0M6CT28RTMAH9R8H0K2GHN68W46G9K8RT32CT560RKCC268RRKJCSJ70S30CA564SKAD9J84VM8DSG611MAG9R6H2MAG9M8MT4CDHG8CWM8HHH84T3AD1N6X0KEH9P8RRMCCSQ8MT30CSK6MVK0GA48CW30D9J6WTK0CSN650MAD1R70TM4CHG850K2G9H89142DT488S42DT36RVM6DSS6GTK2CSJ84WMCDHP8CV3GDSP6GTM4G9H6N246GHH6GWKGD1N70R34HA689148GHP8RVK2E9M8MRKJCJ28D0KEGT26CS3EH256RVKEDSJ74WKGGT16RSM6H9M6GVKGDS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSN8CV38D9G8MT46CSG650M4GHQ6N0M6HHR6X142EA26H13JG9N6RV3AD1P6GV34HHK8S0M6CT28RTMAH9R8H0K2GHN68W46G9K8RT32CT560RKCC268RRKJCSJ70S30CA564SKAD9J84VM8DSG611MAG9R6H2MAG9M8MT4CDHG8CWM8HHH84T3AD1N6X0KEH9P8RRMCCSQ8MT30CSK6MVK0GA48CW30D9J6WTK0CSN650MAD1R70TM4CHG850K2G9H89142DT488S42DT36RVM6DSS6GTK2CSJ84WMCDHP8CV3GDSP6GTM4G9H6N246GHH6GWKGD1N70R34HA689148GHP8RVK2E9M8MRKJCJ28D0KEGT26CS3EH256RVKEDSJ74WKGGT16RSM6H9M6GVKGDS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSN8CV38D9G8MT46CSG650M4GHQ6N0M6HHR6X142EA26H13JG9N6RV3AD1P6GV34HHK8S0M6CT28RTMAH9R8H0K2GHN68W46G9K8RT32CT560RKCC268RRKJCSJ70S30CA564SKAD9J84VM8DSG611MAG9R6H2MAG9M8MT4CDHG8CWM8HHH84T3AD1N6X0KEH9P8RRMCCSQ8MT30CSK6MVK0GA48CW30D9J6WTK0CSN650MAD1R70TM4CHG850K2G9H89142DT488S42DT36RVM6DSS6GTK2CSJ84WMCDHP8CV3GDSP6GTM4G9H6N246GHH6GWKGD1N70R34HA689148GHP8RVK2E9M8MRKJCJ28D0KEGT26CS3EH256RVKEDSJ74WKGGT16RSM6H9M6GVKGDS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GSP8MSM2E1J70T36G9Q750K8D9P692KCHA36MSM4GHS6MWK6DJ564S32DHQ8MT3EHHG84W32G9M88RKGG9J74W3GC1R8H1M6DHQ891K8C9M84TM4CSQ6124CC1G60T30H9Q6CVK2DSH74T4CGT1751MAC1P88RK2GSR8GTKJEA46N0M2DT48CV4CDSP6MSKJE9K64SM6CSS6GVMCD9G6CT3EDT6652K2GJ46RT36GHJ85138H1Q6114AC1Q6H23ACHS8RR46H9M60VKECT688VKGC9S8CW44E1G612K6H1K70VKADSN68T34D1K70R32G9R6934AE246GRKCCJ66N142G9K8CVK8GHS6WS3GH9J74S4ADHM8MSKGD248D330CHK6MVM2DA48GSKCGHH88TKCC9354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H1J65244GA27533AC248MW34C1P6N136GSR88V3AC1H8N1K8CJ2891M8DA2712K8E1N6GW48DSJ8GW4CDSJ6RT48DJ28GW46DT46H136E216D14AE9Q60VKECT26S2M8EA38MSK2D9S6MTM4CHQ88T3GD9M8D34ACHJ6MR38CT46X1M6CSH74SKCG9Q712K4GHG712M2D9Q8D1K2E9K6X2K0D1H8RVK0E9Q610M2E1K612K4E9M6MTMCH9P60WMCCHR6CWKGE2168SK4GJ26WV3GGT188S38H1P8MW44D1K6S344HHP74W4CG9M8H1K0C1M8933ECA16D13AH9P6RTK4GSM6CRK0GA36RV4ADSM7524CH9G8N0KAEA288WK0GJ16RWK0CSK6N0KJGT674T3GCS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H1J65244GA27533AC248MW34C1P6N136GSR88V3AC1H8N1K8CJ2891M8DA2712K8E1N6GW48DSJ8GW4CDSJ6RT48DJ28GW46DT46H136E216D14AE9Q60VKECT26S2M8EA38MSK2D9S6MTM4CHQ88T3GD9M8D34ACHJ6MR38CT46X1M6CSH74SKCG9Q712K4GHG712M2D9Q8D1K2E9K6X2K0D1H8RVK0E9Q610M2E1K612K4E9M6MTMCH9P60WMCCHR6CWKGE2168SK4GJ26WV3GGT188S38H1P8MW44D1K6S344HHP74W4CG9M8H1K0C1M8933ECA16D13AH9P6RTK4GSM6CRK0GA36RV4ADSM7524CH9G8N0KAEA288WK0GJ16RWK0CSK6N0KJGT674T3GCS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H1J65244GA27533AC248MW34C1P6N136GSR88V3AC1H8N1K8CJ2891M8DA2712K8E1N6GW48DSJ8GW4CDSJ6RT48DJ28GW46DT46H136E216D14AE9Q60VKECT26S2M8EA38MSK2D9S6MTM4CHQ88T3GD9M8D34ACHJ6MR38CT46X1M6CSH74SKCG9Q712K4GHG712M2D9Q8D1K2E9K6X2K0D1H8RVK0E9Q610M2E1K612K4E9M6MTMCH9P60WMCCHR6CWKGE2168SK4GJ26WV3GGT188S38H1P8MW44D1K6S344HHP74W4CG9M8H1K0C1M8933ECA16D13AH9P6RTK4GSM6CRK0GA36RV4ADSM7524CH9G8N0KAEA288WK0GJ16RWK0CSK6N0KJGT674T3GCS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H1J65244GA27533AC248MW34C1P6N136GSR88V3AC1H8N1K8CJ2891M8DA2712K8E1N6GW48DSJ8GW4CDSJ6RT48DJ28GW46DT46H136E216D14AE9Q60VKECT26S2M8EA38MSK2D9S6MTM4CHQ88T3GD9M8D34ACHJ6MR38CT46X1M6CSH74SKCG9Q712K4GHG712M2D9Q8D1K2E9K6X2K0D1H8RVK0E9Q610M2E1K612K4E9M6MTMCH9P60WMCCHR6CWKGE2168SK4GJ26WV3GGT188S38H1P8MW44D1K6S344HHP74W4CG9M8H1K0C1M8933ECA16D13AH9P6RTK4GSM6CRK0GA36RV4ADSM7524CH9G8N0KAEA288WK0GJ16RWK0CSK6N0KJGT674T3GCS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H1J65244GA27533AC248MW34C1P6N136GSR88V3AC1H8N1K8CJ2891M8DA2712K8E1N6GW48DSJ8GW4CDSJ6RT48DJ28GW46DT46H136E216D14AE9Q60VKECT26S2M8EA38MSK2D9S6MTM4CHQ88T3GD9M8D34ACHJ6MR38CT46X1M6CSH74SKCG9Q712K4GHG712M2D9Q8D1K2E9K6X2K0D1H8RVK0E9Q610M2E1K612K4E9M6MTMCH9P60WMCCHR6CWKGE2168SK4GJ26WV3GGT188S38H1P8MW44D1K6S344HHP74W4CG9M8H1K0C1M8933ECA16D13AH9P6RTK4GSM6CRK0GA36RV4ADSM7524CH9G8N0KAEA288WK0GJ16RWK0CSK6N0KJGT674T3GCS354520818CMG26C1H60R30C935452081918G2J2G0","51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30H1J65244GA27533AC248MW34C1P6N136GSR88V3AC1H8N1K8CJ2891M8DA2712K8E1N6GW48DSJ8GW4CDSJ6RT48DJ28GW46DT46H136E216D14AE9Q60VKECT26S2M8EA38MSK2D9S6MTM4CHQ88T3GD9M8D34ACHJ6MR38CT46X1M6CSH74SKCG9Q712K4GHG712M2D9Q8D1K2E9K6X2K0D1H8RVK0E9Q610M2E1K612K4E9M6MTMCH9P60WMCCHR6CWKGE2168SK4GJ26WV3GGT188S38H1P8MW44D1K6S344HHP74W4CG9M8H1K0C1M8933ECA16D13AH9P6RTK4GSM6CRK0GA36RV4ADSM7524CH9G8N0KAEA288WK0GJ16RWK0CSK6N0KJGT674T3GCS354520818CMG26C1H60R30C935452081918G2J2G0"],"transfer_pubs":[["J65E5480S6ZVPBA5HV9D4APYXFS527DAJGN5HKZ5EWP89EFSKGK0"],["TYZZG5HSXTYJMA9XD3EMTX2S36V7F4VPR1RHQPKJSTWXJD2KPDAG"],["2MN2X2X7P6GJCN09Z6ZDF2R9W1W3BSYJER7FTBSDDSWMRJ7DG0DG"]],"melt_coins":[{"value_with_fee":{"currency":"EUR","value":4,"fraction":0},"coin_pub":"WQHES0X5XK43VBG1Y8FXR2YEJM04HQVMDTCS07MH691XWADG8QCG","denom_pub":"51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GT58S2K2HJ16H336C9N8CVK4E9N6H1MADHH61330HHM6N1K8E1H8RVKJH256D1M6E1K8RWKJGSH8S2M6DJ170TK2H266GTK8DSS64RKJDJ26D144DJ474SK0GHQ711MAD9G752M2CJ58S1KJDA570SK2E9G8N23GCJ28S146DHH610K2H1Q8CW3GGA16S146H9G68TKACSQ6914CE1H691K2E9N6RWM8H9P8CWM2H9S8GSK0H9P6D1K6H9G6X0M4C2171144HJ46N334H9J692M4H9M8MR4CCJ46GRKEGA46533CDJ38MV4CH9K892MAH1P8S2K6D9K6N246E256H244G9Q6D346GJ56S23JGHJ690KADHJ8H242H2575132CSM6X1M4G9N6RR48E9H8MVM8E9354520818CMG26C1H60R30C935452081918G2J2G0","denom_sig":"51SPJSSDESGPR80A40M74WV140520818ECG26DJ384R4CCHP8RTK6DJ46X344C9M6S1KEDSQ8923EE9P84SKAH9R84SMAGT670SM4GT66123EDJ46RVK0E1N65336H226H2K8CA38CVKCH9N711K2H1S6X134GHJ6924CGJ68RRMAH9P6MT30H23651KCD1N6X0M8GA58CS34E9K70V32E1S8MTK8GJ270WK0HA368V3GDHM68RK2CHS610MAH1H8RR42CT58H342DSG8D1MCC1J6N0KEHHS6WVM2GHS60WM8G9G6GTMAC258D338HHP8MWM2GSS8MW3GCA270TK6G9G6WRM2GA56RSMCC9R88T3JEA270SK0HJ284R3CC9Q6WVK0DT570TKJE9P68SK0CSG88T3CCHJ60R48C9N611KAD9N6WVMAC9N70T46GH35452081918G2J2G0","confirm_sig":"ZKYCWKVTVQCWMWTAQRNRSKGQP6AC14VF7W525ZQYFZ6J2T1GM7PE7945W3HNW32S0MZBX8K65VQPXH83NMQR2SGTY7DX2T60RKHFP00"}],"secret_encs":[["DCZPFTHF1GZRXFVSYJ4AK1HH4QE46T8F60X1AQ6T6BF1WHBNTJ3FEGH7Y1TH5A6YT10GFYGV12S8V9MB4HEKHRYBBMR7JJ9KB264TNR"],["ANBQS74BACGAWEC0F0654HYGZB3NBSNXVD4KZY4TRP4JMRADJE2Y5BYV513Y37PPMR6V5P200FEAHTZ5TS3R9G9MBYM6Z144N853PQG"],["1X6P2JR301MPR5JYMKPV1HXAFXKH9M09HWVQ4PS6JNK1K6E8ZSMW1C6NS79CPFP443QBVEW9SRWXXJQT91EHFKJ8007AB4S64SPWN3G"]],"coin_evs":[["1XB4A97X8ATPH1KG5NAMJGJY258AGK0MKVJRHN4NGKPRD17DQM0CMTT351FB7T56NTVA9E5VTH9Q12A10C3YVDEYJB4B66EC1NPXNPMNSVTBJ7HGM4DXFB03K1BXK247QK581CGE54286BTK6B5VW0FTS4AZ3NQP43V9E6VV1HTKG1CM5WQJEY14TAC8P2YSV2XH61RG4E84R","JVNGX2AR684RF4VRYFSS69WNMJBDDS9Y4AXFRX5JRFES5QBTMM51SB84BN61S5W0R7PZGK09NQ8QMX7Z90AE0JJH76J7DCM94DKWMV88K966NEA2CNMS50PG6D0K73619K67S9HJ0PQTQH8YD91P4CQQZTRPV7Y2RHEGMBB2G67ZBVVH1S9Q0HYX69Y2B3J2WYFMH3BJESVGE","6MZ6ZDF26MSAZN3VK3QDY62MD3AXZTK8K46A7ZFNBVCR33AE6H3A309F822EKPHT2YTJB6PMSPSJ3ZAE99Z5Q2J8F87X5N723J713A0QR87KYJ2NZ3PFCS5CC8FXJYK6YXZ4ND6FHNJRJZZZZRXE05VP14XW1BZFYZGQQZ8J05FYKY8AYFNCZ50WT7W8M83XS50GTYJGQTNPT","Q0JWR4WRPYT7TCNCV47RJ1FW3HEFQABN9EE27DBMDB4FYAVK8X9KM38HH9KN8QW28RXFXAKT3MGHXPGRWCY9P313CPR5Z2XSR3ZGKFHQHTP1A02SSD0DHH12RCV5TJ7M86WXWT7F00CQ8HV5H90MFEDX6J2X9JNX26NC0PRH0YXHGW7XVWXF00ZEJCGJDEZ1EVXA5BNW3SAEY","B20PSGCM77EVSXEC6T41N1T55KX76B5K3X38VBQ8NSAN2BKNH0W4DQGVZQTBA2588YCRW1TN9G721GP7ZCP9H32BBC9HM3XFTNJ4QBV2FMM7KSG6T0H418EBNBGKZXMTD9ZDVV6A2TEBMXSEKYVK9HCZT8EEQ7E44DWBDHN274N9D4ZDEFCZXNF0MKBM3TYZGHJ5T71T88CE4","F95WT8Y2WRXGK07MY3FMV1Y6PS44CMP59KK8G82EKW3Z0Y6C6QZ2CVC4A6DF9N3E6HW8SQ3CF4FSD1QC2DN5E9DM5G762DW491BAE5RC2A3YPGJTZ3XBFJ3R3NSRDRZMHR6Y755QDRZZEVFTEPN7MZ90SE6KTQS80CHNMTX15NMZPNFJSCQSEJ9RQJKZBH9ZZFMNXT8BXM7K4","1B8GKDJYJYCZ8Y2HCETV6EWA8FRVR0AAD7TRXFXBHNF7G10CZYDFTKBJ8X0J0M7VR1KQV67SF5VMM7VVG5ZQ4F9QEKRV3GSBB4M75CAXSEXSHDT36RJ6A98EKP3EZXT65HX2K788ZE4SVE5N2NJ8Q26P318PK9X69YWB4W7R1F1F9WHZ8WKJ69G5YZ1T9WR1N8CXTJASXW3A8","FV0Y6B21F01D5EH31R80BHG7GKA08C7HN3FYDMGQE6HJ2Y9ZSVJ17HR8Y22WGPN98T08E6XP4PZNMJ3RHAFK8DAVW3AYM5VNPHV3JSB5B64F0H60EMQNA59A2QAFRV3FCHJQATY87SGT9E67HRVE5QDP9HJC7A351WM55VV5MW3T251VVZ8N5V2GVX6MD14MR9ZX9TBS4N30J","K8QSPQTEV9YZC7MC6AT9R8Y0QGG4PT1V45KSSZR2YY9SBA3QEQG7R6YR4RKEKR5DGDM2K4DN9K128TQMMPFWZR8N95F29Q8E2WBD22F5SRFPJ2KJA6SEZ6RMPCHG6C4BJEV7PZAEVTNJDK8T79PJCB5B97D5WQ52TT2DB6Q4S599D61AA54FCVM9R3AB03PDTYZ0PCKBMJ23G","1655VA0GT3ASG2JVEZ93BWE01YDEA70ZW2FD1W3AZTD4B4WK9GYPMM9Y6KXDGDDB2TMDF5CWXNAX0B7E396E1NH3GCK8KFHMGXNPG0CTD2KNW9X3QB4HKK68JM2WGMF05VJ6K4G76R4A10JWPEZTD554TSFYVEM0355C9E5P9W5GFFFYQMTAXG43V8GP7P4BDBD81Q9G87MXG","J3T1MSDB9ZHCCNAYDXEQAMETNSCX5DJYQBCXGREMYAWAQ8AJCY5JV00HHX31DVT7X5S25CEX88707E81X4KNCN7RBRFMFNP8QZHFZHXNS6MN7BD4MGRVQEC50EWH6N6EFY1GA039KYJKX24XMGZXKB27H5R0RKN7YC00EKN99SGKF3YN7DPR8880MG457438G1YJR7BGRCG7J","DX73NQ14FVC978B1HP03MWBD83YT6NVP607WB126RAJPKPB11GSWB7H9W0QJSH9WD4SN9JJE6FDMQZTMJXCN1AKDBKKVPWW9VBEAE0YVCG64S16TXMSF5FTWNXYG7RDTHK03BXDGVXSPZFE7A2F6FPK9WCBBZSMKB8EJFGJK1PKKBHVA1Q2MX91NK32XPF00WFWZCC3N72JY2","PKSMQXMCT9H53FHGKVVPPTTHJ0M5X4SSR0XARN34VV7DX9ZA9N8379NVQKN4E5V6JXAPEVGPREWD720RZCVB90DDHRFW8GG6G434GR1A77SVPG9GKP2DEKBVP3CZ9WDMQD9RFZZ2Z3ZQ34YVD2J1Y4AZ52ADY5FXPB06W9NQ9NH7AQVHTPMSTP181MZM507M777SAPSCR9J8M","1XV578XYBQYYSV9V8ZRACRG3JTN103BZFPF5JXHF8YDCCCJM9XS343JYM8N6Q3J8D0NF2T8TPZKCB32MC1GXWZZRRWQW77JSSYXGA8136DNFYTCKB8EP277KBXAYXBW0ZX7S13J5NDGR7ZJ7QTAAPTEAG9MX6QBT9Z7ZZ1MECV7Z0D7GE7Z6KWASE5T4SW0MFHKVD1EAFNG0P","86PDN3KMT8AW8TVXEBGG3199YSEDMQX91QW82QVZ9TPYCNS3YKK3NCK5J64VKPKGJGQJP8E3RX97NZ4RDQ0PR21QS93MTWXSHKCTK2KTH7TQPKVQSWPGVC8CZDEGD9X484HJKMQG56RP1MZE7AWKK9C5ZK7MSN21GTG1KNF591Q8DVX0TM06BD8VGF6QNC745CFRMRSYHXK7P","KGXXZAECQ6DETR97HRYKFC4A1WAKC3S0FYNHS16J342WMPPP78NHWZXSEPN04XEQY6QNR9ECWZNXY6BZHXWAW2EV42295GBVA4R2J2KAK9EF7MR2789EHY0MVQJME1ADGEPGCQ02WHWT3C173SPDKW1DA5C8HBF847KV00A3F3ZM2HE18N5MFFQXBB014HK0MC4NW6G19AESR","92G786WTFN4BP3Z2RX1Q79ZPHGMPYC21K646XA8AB97V103DSJY0S4Y1FHHSHYQ8WZPWF0W07VY6CDG66438K8E8DWN7CCSB6R8KSC2VNAQBVC1ZD2H9VG33309B6QEKK5PYAGJZN5NG0CJXW9RFGPCNJJZW6KHM4TCMDJVKV9P953Q0TWVM2YZWK37504QJ41MZ4KXN52M30"],["NG0RYVSNPXP4B2NEYCTVC5A0EW5PFFW8V3PH9X5FFQ2VHCETMWDPZ75HBSC8QDBCWZ6EV9SEN1SMEMXABK3YTXBDDXYTN0JNKX1ME9T71JJTZZQ7XA1890DZR3ZAMD0XW8GZENDNAP4R5B80DQYFDEENSB141DZ0HG3A3FZA7JMCSBY0Y0PB2AXNS25Q6CQ5WA7GWCWA09PD2","R70NVRHFT6HCP69K8QQM9HQX0CZQYG09VV2VA7RKCVFDRTNH44XZHRBEJTBW1ASDS7WYA47PBVPB9T77B32ER6V2745Y8B1P22CZ5T70WTE6Y4HSS60CM5SFB6QT1VXTVW9G0DAC3XY2QDX6TKKQA1HX3RB0GPJH47D08KB1Y4EP1SXW851CFNEE373YJHBNYXMSE3MJBCQFP","JGZK4NS2E8D1CTR8ZAR5CHGN7T09Q41P545ZNNB7RMYV0CZ9CQWJWZZWRQZDRDCHV5PQS9G6WZB37CHQMZ7Y207D23FQAPMN6CP30CXYEQ449R3V7B8BYGJ84VYW60201B4343GG75TTTN947VVEYA3BWKRY4M5XCXSPAGW1TTAH9VAE8CBXBKAKJESX8XWS4W5PYBG8793TR","CEVHPNNSWR438ZWEK27YRYBKZXVBT8AM9QP2KXWC59ZSM8Q7J0GZNXZYBES04RB7BJY4D5YCPT3VRT7EPK0576809CG98X5SPTQPSR4TZ0R6DWMXH9893FCTVJX2A4H2EGSGP3Q6H53FD4CSDBMZFZVACXPMW1CR07A1GG7XFXSK1BP1BTGMBQD0818G6BC6SBFTXS1Z68PD8","72BFMSPJ57Q1B88JS6NX8YETQP0GSSRHXM25ZBGNW06YGYJ7FT1D7KVGF4PRKKQK8KN4MGHK5DXRQDTNEJSRZYGAN6DV5QAW9PACT7FQQXG6G4JS7J129VXFQP3DYTV0455Q37H5YPVGMWXZ4GNBQJ86HWWD670XY8JAR4WTYHX8FWJXZW57Q94KK79SD51HBJH1SJ4Q5C04W","2MDED6PYFV264D0SETN9MD4F0SADGVD75J8Y16DTSABNPW09AKE7A5F4QNB2124WDQ0HW4SGHCX26NAG46Q960Y01VYSX0GQHVKESSMEAZ2426GJ8KR0E6KTAXDZY6HZ6DRSPXXGZ07YZT2XF696CP8215HE92RGT7A50H5XXV2FDGFWJXYNCB7QND27ZWYWTK0KTCRYT104Y","267QZPCB0QGC63V9E75Y41J5285Q3FX6KQHM08MAKZ248X9WJK5H6H903C0T02W4G3EA22E0C1MHCWP5VRAEC8D0J0VYW9JNCH9YGPQJ3P941AADABVXG4M2K724C8VCFZQNXYJAMY4Q65WYHW6VYPN9H1K7ZABPAZX82SSSBPYKJZBREJ15MSS50R9HJ6FWW7T9P8ES015PY","7W8HPWAGBVFK0KTWG02RRTWKAE4W0PJH76GH9JWD7H48BS7CVD1T38QBX7HJVC6PJEAJ5NGP7MSJ9D5H1FD2T9Q5X4YQMQ1F80H4G8GRGXFKS863G48HN15D72EZSM9V8M75FXCH3639PHENNV35GA6TWPRS70HEJSBAX9A52DWTB63ZCSZXFGACZEQFGR0EKEE0AZB3HKE2J","JMGGS2DPDKNQKTM593M69Z1R331654KR6N12Q7EZPX20BMHV1P9JZZ8HNN6VHQ76YKWNMW9HR3173C2J66R8EVVWZAR001Z18F9HJ9YBVSM1Y94PJSM57GNVRNN0GH11A3ZRCA4WBKFC64A4RE6KTYMMJKJNHTR9A75QC7JWG5YY9D0J8TM9KXQW8495V702VS0E40121HNHY","REH6B17HSBJGMMPYKG7EA1J4G1D7H95N20P647AWHJKY3VA5BDZKN84FEG0G0FK6TKNN8D0TDFQ41610SK9CBR27SFD16PKEZ7AP97NB9ENPQRQ9PEKR76KB6VFWP666MJBTQJ5R7WGKTTE3ZN5Z2VWY2CPB437746CYBN52G2P2Q7V6SDGXV5HPYJBKXESS1JMVH3EHDC988","MRE9Q877Z18VMFNH1PWNH9N3Z5YE2SDZCVVFBZAA41SSRC2F81D6F9TCZP2AGYAEXWM3977KWJ6SV215ZJ24RAWA94NCKEFQ1CMW8MYJ063EP68NVVPH51SFVYQVHYC45MGNEXK61R9KFK6RWYV5Y6CHMAYZTW5M17WBSHBZ79F4DSWHVVY7774FX3WEEVM8EZ0WMJWY99A1P","2CM59NJVJ68NYX5ZJM2ZX7JK8EW7JXAVE0H2D1ZTX9X6X72P9768WK5GG3ED7MGEYGFQBK5YSSJ0DM68DRPDPTAHQGWTGJ7E79GAW96BXHYV64BQXXEV9YQ0XR84G8VZWQ8Z7T9VX7VQ0HHX1SAS3NGNZAH1H03KPFNAZMNMGV3H4Q3PC96JHGAP5JNDWA1FAFJ410REB33D0","2KEXQVJHACQX7V1XGF8PA6DV0EVAKFNSA037YJ3BP91NNQB60ZBAEFT9DKVW2M19R51B8HGQ2RCBSWBRHD0RFC95TYGJVKNJSCMQPXV7SM6A1JH18DFPWB1AJBS4SSVJXZ9V72TY2P8Q8ZTBMGCYERXCT4QJMASTQM1T22C9AXMMPZ1BHKNB2FBJ3TY9AQP4ZGHRSRBKR75E4","5Q9ZFEFF6C6XPGM78EXAWJF2CKJVJ0SPA113XT7R5JTRW7MKE70TETKK1MBRSTF566AEZB2CBX6CR10J6DPAQ92GC6XYK27A52KQGQHR5W346RGF28WE2PPC8S3XWTGCGVP318AKKTC8GZQ62FRYZVPTVF07HWYHDESQFKYNSDRNV8Q5KPGRQM27SK3CX14RA2K9KZ0NJJ0FE","7ZK7H90WBS080M9TH8XH9DNENKQ78YHYSXWF16VRM76YDKP4RG97DEM1DBX84VXH98M64CJS1GYTDKG4XA8XCNSZ4KQ12GAXCJC168DBR8Q4M29X2Q70WNR7BZDNH0DQJMR154M0C1NH2EHJBKA76PNZT2369PT9Q4961EBCY5FE85EKCX7584GXBMHQF1GRDVCCDVY7TH210","CFSSPFE27VKZHPS7D48AAHE0C2XKXS774CDPFE530P5C4EFZVZEP1PTS3NYB7KBNHNAGMZX8YZJVTNJAB1DE7GCRG43Q2JVS15RGJC4K9PDA0Q2MG4DGRX1CM28HE8CB1XWVW7MCDJHPNE28NWN0CYHDES8GHAZ6Y50NNZRN31FV8C28V0PRNZK4WFAPJ4EGV5NQPHGSZAWNG","9XJB29QY06J1DYBXQRV9F7M3T11JTGSZHX8D0Y80Q07M646VTHNFKNA19CG84BA2DKRE1D5DS3M72QH29S9EZEADW6PCS1FXGCMGZ0RQXT5ASSX7VBQ40NGPYY6PFE6CYV45YYEGPGJ2DCQCNYP2Q9E78SBD53QTQVYZG7W3QE8EASEPSZXP1VVB1TYYHN0B1BZGY2JKGWWPY"],["CSATAT8AQRB91XKHJD1B7MK1DPVPV991WZM4Y2NF0G8SNQSR391KBDGHR0AWGZ8V6EYXKNGTACT9GRBWWAPYTDPWFZ5QPY9GY8Z4YDXRK1QW30YT6JMAYQM2TH36XP81G2X55785ZJWCRXZ9QQDGGMMM8QZTWN9TPW8JXH4XM8A0DPG5354PF3782GVC8J8DXD7KY88KT0FZY","4MG8W9X1M5361HRRG6W9QF13EQDRFWVAQGMWJKEESZ3FBCWP7QQGQBBDT3ZYE3XARBVGJAVFQRQPAYA1PC4CQYRX0CZTS0E4QZB3ACMXG9XJ91VPP7X0ZG08WXWMT02M9V4HCHJ7MJQZ7EWCRJAG7E14BQ465R123P6QFGY770GN0BBNT040S3XPC88CD8X3D8497DE2YA1MT","DCSBAHV955VGACX5WE2N02QZSPQ9N375DCS0W3298N8KF59YHVTS7FYJ1NE011VWF8ET7F6SQZHW1NWJZW3XYWN2ZMWXCRGH80CD6N8YA6EE8Z2N2PW4T65M0DDX4YFXZPG61E7JJGK03VTDW5J8WS2F2R88GNNJMWBQCYBWWTZ34H7J5XAPBBY3AF4M6A8C0RAHPX0FS044W","5CKW4V17Q2XAV54F07X0C2NZCRZ21B9M79JM37F10DZJ3YMY9WPEWSYEDC6DP1QZ2DRRAXDCFG1WZH3C0Z58Y26XGSC28R9Q3ZFSKZHS0F0DC08JWY01W9GPECCN0CP0Q19N7GWX7HAFVR0PR89P0DTBPJ02NKCG14HZK933C6C62AFS8GFWNWN7FJ042YVSH69BAM9FQX4JT","G3J5ZS034N9TKESCEBQJQVXJMYB7HX48QT7QTEZA688019KSSCFVDG2TD963JA9NQF2G80Q3J1853PRB8QD2WFWDSMPP9X4FXKAEH79Q2X8K2KZJ120848KY421H81WAA1AMPZ6APD67RDRBKM0H5GN974TBQ8S5ENFRJSNTJRJEDY57GRHAN8V2R2PCNCY1VWH92EN1MJA4P","BFTC88T9TMD6EQPMBN6M7ZES14AGZMK8CYF0BQ7JAPPAPKXRTW83Q2ZF5270PG9WN0SET02R2HHFEM4J66GDWK2GYCF6GBZWF0GAH9ZC0AHVT3JW2XWDZ2X1DH2WB9Y10A7QB83S3E1S5DQEJXD6AS4J6HCBJBZJPC1EJZP3E5R5ANY1EYZ7P1692WSNABA0KWZS5T05DK51M","HFBFB0NVHM82461FBYT7SKWR1R38QD6MPMTSZXQAAH7CKVSKE21M4KZMWE3EF2C0FFJ6QGZPFWYXM58NANQMHP293WNJD5NYM31ZGV3ZS8SDP9DK1ZVSF5WXW3PEKYCAR3C7J6QZKAT1CVQF86P6M3S7XPVN8EV212SG4ENANH650EF7H5RDBN29PRREWB5JA4PSX9TQR1Y92","1JT0M6407RNBHQFZ0SKEPHQW2ZTZTQ5X7BZ1Q1FBQVR9QBH3EP20CH1KJSP71PC8Z4WFKJ97442BWG991RJC0XE7Y71PW9SXMPX4MG1SVN371ZG3GNCBX3Z9AZAAR9PGHS46AVG1RH71X1WQZ7W9HGQPRYDPCMQBQ7N2PD0AH8EJMT2ASXKPNK09S9H9B0ZTYMB5KK2VB7C72","E5PWK8V063470P271354CCZ3JS5H6GB8GWPDCDB125CVT4YHR3MGTYAFTDJP38F2WSNERY5KNE9VK85N70WC6VZ22WF7MABK2FJZM3179NHBV92FQC19BR21002AZHHE4A171QYFX69NXV3XA90HY27ECQ28YCPZNKEPQXXVCNHVSFJJE8553M68KM81EW45GXEV3797W2KX4","JQGN8RXXYMHG8MD2GSNQ6NQ8QCH8A6WDT0S0WY4ZP5424RGY975300CQC1C575K619FY2JN71RQ62RM6W2TBVZX0Z5B8A2PQ2MTN6W486AHBJKX6ST9HA05EQ9VMJN3PGDSF1GACENFK8CA1EW4KNG185PG6CP7YY1TNX3FZ43H020K8SYTQWMPM6FAZ5N4MA6CQND1ZJ6AC0","NJ5PM2Z4VNYKG6X93KWH0GQZNMBKHYM2FWX0BSNPGJ18Q7R32YC9FPC2Z45MPJ5EZY4E3FD0CD67TB9K4879SQ33SCG4Z9BARNQVWXMK975GRGAB4306D1W7PF1QD1MTVW79DHY3M2VVC01R4T41M3TXYTD1X3JF1PBGC4T63MF9MJS4KZTS03Z84AY6C9WK9RTNHXN7R0VJC","1ERPPXVG2NJ4RHCD8QED1E25R6QCAKKZWRV3EAVS4Y17ZDTXK46TSBJ1MF4HN26K3FHRE3PVBV4DA1RJ36R70BSAHSW7KTEGJNR8M5FBJ7ZDT907X2H0RMPCDANGGT9Q8VWYKBAHQ1T72S0ZAEC079SKV16XWTRX698P3P2TCJQDT7EA0G67N82YFPRE7YVGFKTMYXYC7EH1E","91CHR983Z222JN1695J11YY836RHP6J02SSZ8T5ACZ15X0D1YAW8VTCMECW3CPMCHG2RWTS5C30HSRWTYHAE3J3F61Q5N34ZSBVFPWJMN8WSFTAFEG7WZDWCM1VJZX5KZGXF7RTCS09MHJ0WRMCGC18WSR2SDV64P9HE1WTNCD8KT65JGQ3ZPWAWVJNJS63YNTGDPZ4RHTZ78","8Y2GMEETX3N9XTAB124JYKMHQH29009WJ5KF6V31WWZ99GXWZJHC5C03P3CKMB4R8M7ARQ3XD7AHPJTY2EKKPPF9QXWN7685HDTPT2SBCQ67XHHRA2RJ5E1S4VFP2YB1H0W1D9RE4C6WDT7FNVWXFHXAEQAJRJ577QNJD8G4ZSARSJ2SP6625AW9VQVVRFS0FWMBWJZNHXGWE","33MH8RQ5PXQFB0Q2T0H30VM9G4J0ZQHB3JHH8MWF5K3F3KDBAR20ZG4APHCTF1XRG530W7K2476P7BNFKENP2WXJ4HAW6RS91EQKGYD6PEHRYCVF8JJTMWR32WFVPV5Z2M1FWKNNC5N4CC3MK7S2K1V05PQMZ347KYD45FW7Y4KKJJ89TDPT7SFSQP1A0J00SN3MMTSEGXMVC","HR05CWNNQ5VBAF0WNHP8DZGN3S0DGYJ37EYP8S57MK8KDXQHXTPV2SXNJ2M5SZMS43BR168FJTB1SC9EW9P9YT7DYRBRM7G0VSD86P4QBG42NH06XYCCF7TVN9H9EB5K1G07M2Y3TT4WBAZ2Z350PV67ZANG43054GZA4N08BN2WEDFN94BNFKF45C10C3FFG77D9P3PDMGVG","JS43E6R3YEVTRZGRTS3RF0F9PCW7NBTWB6XEV146AXNTSKEKCDQ8CX76GX6HA06N0RTA699M99FXMD4JX04VY961YXJ18G96Z1AEEJ5BR5A8GYEWSNJSR5Y55ETBCKHBHR6CJ8A3659Y45FNG7H4C1K44WZ520PE4NAQVJ9QS1H2RWQ5ZZ8H8J8WGBDQC6DQYRTQHR0WF2156"]],"link_encs":[["7A1HVQDZ7C2M4K913020KMYGK4K86PM30APZ8AH5ERT5Z83QZPJRTGEFXF0TGYH36R1N36N3VKB5V845FK33ZW3R4G01X6CZNE37XHXE707T0TA4DYGYNX295QSVF5VEPD8QMK1J7ZAGDS27QP9H1QVK3NRBTZVRZ23XAV642CGEFS1ZMPWEDSHWECQ4G6CFK1V0K2118BQYS41R9P05NZ18PY3Z1FWNZH472HTKWT74KC752S0W1C2ASM428009","VN0WT37PVBAEYW3GH2ZGZ6X37R0XATMMGHJ86G953NVD6TE608Z3A6P9PG9XAFAGPQ13GSQ435C8763NR5T28RQHH4JB9N578TW7FHA5Z4Z6MDS6CMFK9A6ZGGT35G28NH6YG55EZR6GCM5E2ZKA8A9TR8Z8C6N4BTEVJCCJRWQ5MG8W4TR5N3YNPA7NAM49TYECT56BHJPCD6DHJ3XQF2XTZQTCECDJWZG4TXK3DEZM7Y55XFJ49C7F1P17MWQJ","88RWAYX9765MBR4KBM2BN9NSQ5KHWFC0AQZ6B0RF617VF5CSJJZBB69XEVDFC12K5D0T06KBE71BHK9E5ZTEWD5FK4Z2JVKD6WV6QBFHQ8Y1ZPP7Y2EYB9XFW7S9NVM27FNX8RKWTAJ45FFYKJPNR7MX5HZZ77B25ZB57Q0NJB1E7ECH4QP5BKZG63CF5M75VA633AJC7A4XWR8JJTTQDZ40KJMXE15X9KPABVZM6HSGF19P3N2SE2VSSWFXF3XH","MBPTJC2E7C7YQMAKCJN8EYBX1Z0CG8Z35G5VW3AZH3PAM6DF32AXKFX06Y9686R1CC1TP5BWX94DMN78RXSZGXFTEKPK3RSEVWQZCBR6BWB7YEEJZTV0C5MGREJE73RP40730074SPPW6CW665XV5TJA5329ZM0XC0KJ3MR9V70TDCE3M9DMB1JXPN3M97A74XG6KNJ57FKDQH4C4NV3Z3MJ276Y9MDAPDF7GM7MW7AQ7T1Y98RJ4H6163WCSN6V","Q2YNH71E69EDZ1NF8PFRS66PJYZ360SPQVQK73422944P34FGDZVJPMF59Y5KVJSB8VTJRPVMBJQX5Q8M8BHEJX1YAHDXAAWBTTBFM12CACZT72CYH6S80TJ8GYGS9ED1HBBWWT8RTZGMD8R4NMBJJRED107EASZJTH4SW32FJ04X62EA7YAMAJFTPCC5E6QJ43F0H592Y3J81NXQS66P6GQXKQMCN5W3FX8ZJ3160V542YVE2FNV3X7WD88FVX3","5H1650VJX95VFVA0G8ZNGWBCACCZ53D6SNJ2S4QVD27X2128KWC4MXRH8B3FK89QXV07NTEAJEXGDZC8BSYW11JZGF7H3D2YVXX8ZEEZBHWESCN7TNSE9AQK5NBV709PNHP9KHE0K7KEJTY2GY08CWAN1G9CQ3ZPF71NEHC7C0C47GYNVHX9PTXAYH2ZKQJSRNNSERC62CFX7NQD5TPF6WFEYC9R1AB3TWEQQF3FKY5EAE04EAEQJWSW0ZFGRCZZ","F5DZP7M59MGX6YJ3M3CEXVSJ0FGD3Q3EVHSQDSDDKSFPJ7ZBN60SYEBB293PDCFZVKC7K4HZ54E3XM7G3GECK2ENZYRCQMS6PS7VM7DYKF7S56KP0N4SE6X69Z6EDA309XPHHPDGRG1593SZ63YN63M815C2JP9M5E7H4FWYWQQNA9C81FQFNH48NVF43G3A54EEM3JV6VKB7X9H3SESC7YRA862NZF42C2FP5MCSACPQBSX0HV2Y6QRZW53BTW2","HJ9AV53HDRMNPEJJPSEPJV98XXVK1D93WT9CQNNY7B0GKY2TTAW55C83EHJQ6SHMRYEWQPBS096ZKRDFHNM5YJF09V0NFEYTTJGSMFCDYKW5GYCZRH3FGZHKEMNN3S98WV5KWDG5PT0S9TCYTDW074WPNMTH9V9KE08E6N8ATBX62PZ2X326TT3N15RG3ZKHQ09360981ANE6MVMTM2J4RXPFMA936KTXAS0EF4FTTGZA1GHS4FNG0JRFBXTNF0D","BXGHTGZ5EWJY0XKATMGECZB8QM74WH1RY4FB4KZP0VQ4STWVBY0NMQ8832YWB04GQ2B50Q5RTD61091HH1R3S2DK6QD7VJYE9BZ4EEQJTDTSSQT86DQEK2QPTXQ8W39WBTAT8PKBT037JM9DCYAC4CRPGTQWBZK3M1Q8BZHRVXYVGGVC5GWG3YQX075EQCSD9Y18N08JKNFHJKVZH6X27ABDHC323HQS30VS15BD65GZ35MH4HBFT7CAHPDZ5C4D","D3EF8ZKVNK6PCTC7KWGDGTSA3804J3N76R186NKQC3YJPW9P9B6CXDN9910S5RNEGST7MSY566PFZY2NP95WZNJTMG8988H3HFVXTXK2TST59AFBNDNTGAK3Y149WVWNDAVZ3G4NM9R75HSWC75BGNDRJ1F6DQ8QACZRAN9E4K6J3NZ3BNG5A2JY48TVXEC3E6TEDMF3E4Q9QSWQXPEHE48TJB5J7Y98WTDDWWV3S186BGM5V06PTGS3V2QRMRW1","YXS4AQF8X7VR35BBA8RBRMCJCSRJ1K2820ERJV75TZ5X3XBB4S5A1EKAGQHRF6KENMX4GEM3NT30K20FD9W03B79AKARV0KK6SMW0W1DY68KWGF69JH95RCHXCSVVV8XGVC48CBW1ZBVPFXS1BEJRDBHJV5229R5CZ9AXC3DNSS7S2QYENQZT7ZSKM9VKYGN604GT55SXD0NXJ0QCVT6TG8MTGTTKSM9D1DFKGDRMP251FCEZVG4XGZSVHKGVHGY","2QYSG97X93Z5TR1JT9FJJG05T1ZKZCPKZ4FNTBNF3DT2WPJR12QF1EPB5NSYQK068XFRKK46YGTT5GX6QEJJZ2B61A35RM30HNTP9S794V2TSME26S4RG32M6AYCNZFR6YEXBKF828ER3HMS1XAF8H475HQSM4M0004GN2S2EHMBXR62TYBEYE20GB123X5V9B31W9650S1E833Y6AH8SKQRE5JY1AK0NATPP6DBKWRC3FTEV6QBDVDGB8B3FKVB","XHTD1E2BZMY311GRBP3PJ6TNJ5S9MMWYZEPFKBF98X6Z3B9GK8EKYZ9DMN0CR9JYQ95KVWETVEKGWMF3V9VT3KK29WGDBPCX1V44J09E2AA1V72A6Y6B6KXPPESB3SM2TH0DZZC1AYQG7TNAMWTQ028ECEZAW30DE3S4ZWKE78ZZJVKP4ZXFXWN3JTRY2TX112B3TRYH94GNYJ1JKGHVG55ESYQTFN7VPPW8GMRSJRVNJZ09S4P37G21HBH46NCK","2N7GXE2T60XCGFPMTS8YJZ64G6STTNPQ4R31CCRJ9Q3F7KJK9RNW4JAQ0VP0A0SCQGR12HJ09NW0TMWKFP46XHQWRJP9WMQA8TXPP6F1SR9XQX7A8KB73EGH3BPN8X5N7YY95M7ZBD1F4SJK24HR0P0GV6BMEEQFTE86QGTP67Z7KC1JPANYM1B3MX9Z9CVESD83WX4VG459X6YR2HR8T576M0EV5K73P9397H25ZSP0NXSAMGY87R0DCQM1GFR3","7WKT07H36FAWM8W3X54AN2VAGJC5DWKP2MJMHES1NA2VFVJD3WAWJJCNWARWP17SQSEWS38HF36A63BNR5Q0Z2R7VETVASYVEYRFQYFGW3KXCVT5AS2ZHJCDMWRJ0N5EQXP0J7MNMCCCQZ8F304SKWWMAQJ8EB5Z7KKM1T0Z8WQHZACR8YTATF4XSXSSJVPGGCXNC47B11QM4RN3HDGXF9MBZZMKASBZRF8AA2EH4M1VFG8NR243ZE3ZKK3818C7","FKZQB344EWN58WPNVZ0BR5A7D30ZR58HZN0PQPVJBKWK3F4A4DPNEV3Q8QXR70E8V5WT4JX8AG9H3X5RZ74DQPKH7RT4M4747NY98P4J09R58JP7BR58KA8Q0AHS6069GHNWC0SX9GF33RV9SA5ZYA75CV37V1N5GRAPX6A0SWR76B6HYSPA94G5PY02R9B05W8Y5MDWDEY70NW4ZXWG2ZH3S68SSNP6SASAJV85KMH6CH7FZXTDYQTAR0HDB4K7","3073VTHEC9679DTAPZZ2J52N4PXFJ2B5JDQQ4FEPWCDFWMTXJK4KTSVMSF6DNMY8BH3EJDS192CK16JZG95JGDGYXMYBAFCY4X77AGA33DZSNXT32VWNVKKP6PN5E7JWRDJT8SSCRJ6C90P9YZ31SFF4PPKFPSVZFRC3PD87CKT8M9D6XDWN61D0PMXKMWGJVSNQDR8NP86MJYE8KGHSX0SWDV2J9N3X9212CXV1JDQNS3DFJ7XEX38EEKWQDSJP"],["8HRTEYDE0W9T678MM8X20QADH4XHBW7JTHJVFDJDANF90K84P7FK7TN0KJGE5BW52VQQFB10YNEQ8S9J3WVRV229RSMC9FKNRJ60968J536EYP24C5WNACGATG6AG8N4YNDQNQQM8V2KQ6DX2XVEH3Z86W9ZYKW7SS4V4B2WM1ZT9WA80SM1H73H9JN62GMMQE37XHVQZ212836WQJMFFKR29ZSYZAE45FJ14AQMP6YKF56M4F1PSATNFZ62VC87","R2CW58HWX89YHVRJ8WYRJ1A2GSC24H1BQW38VGBXP0MXYZS939BWPG8KQM4ED9M19QVXKZ4RC21RN62EC7E1SSAF5VW0BFBGER1FWW3ZHJ78FFF1TNJGRVBE2PF18W1ZXMATHZ12RRKDPSYT81YTGRGAYZ71GHYD1SKYD711P2J80ZTKC6YCWJ6172Z14VWVFA6H8JWWA8VYZ4XTARPXZRWR1Y1YE4232XYZ8X6A8HHWQXS5ZNSM0WPK8BW0KSTA","DE3DY2F0TCKEXVV65EHTWFKKAB2ANTDS435K869BXNVA8NQH4JA6X3WM0H5VAVFT3RCAVQ1WYCRWZ2PKDABAFEXF8RP1R4ABX04ZZS4XPB9K4R1T5KK2HX7GMWQX8RZ16Q40C44DJ70YTHVBD0BM6AF2R5G86SRZ8KERPS21RMPYWYBBWM8DR5YS1D3HM6CEJE6ZX6K398XJGK59E3FCHYJX0B2SCRKJB5E5G1S6PAB3QXKZ2SDGGQR7F8XSESZR","ZNXFM7CDW0M2XRNBTD1GM4FDG74G2XDMTF53N7R71J93YPMPXTT4G6CVZM9YZHCK6GQ35P9CHEKNWK9Q3VESZCYR7TDMMQYYFBNPPM8E4CVHNHFZSBSAHP6MMSWQJ07GZA9FRW7ZGDH6ZNEWNVMQK0V8GV0035C8PP2SYMH66FWMA24E3D15RMJSF4C817TD7SD4F7WG6RCBQ6Y0DDHAY4VW1TB5V4W55J3N0GEMX7AJ98T0TSZQRS6R9PSP1JA2","2J8G5KH2TYWDYHA2KX2CEPW58CRDHK05YX5TER84CGAEBAQASD9Q0ED8R67VBVMDQE2C9NG7V4T3QZ2VRPJPYGQXFPCXBQF99FXK1GBMEGCVP8Y4S4TTN8NJCRBTY6CXA03KQ00JKVJMVSWA85969C2F8YAG8GS5TBPF3D8ZT2C56D2ZD65ZB7F0E11S7B2RWWES7TXQ7HKXDWEBFMYTC2WNGZT1NET0JG21MP03KPS6BEMAX5Q5B139QJNCW8F2","K1605K54DR7EXXPBSWWFNJMXQE6QZS3BN3WWVBF2Y83WD42NMB9FZJG0HXPXVGVJTV1EFEXBAV7PY28FAXTVJHAZ06CDD13ZF7JE12D1BN6T6AT4RHN6N00A2ZJQTE1VCWGBF49Y5B75ZXNS7F9K352AR5A5ZXG21KV7VNT2ZPJ2B1YQG11QY05W920TEGSC8TW0J41HN035ZHCG8KBF5QN5RMGKSVZQV708K7DPVYM7NC2V7X2ZHZ44CBCPEXHD","MGQFD9DMYZD61RHS6KFGC5YGRG2Q56CYNX4GYBGASQ1P69N76M6P4CA3WZV163VFR71JPAXPK11BFCVVCS3A9E59WAR6R0ZWXXN5P9XJXD7DSA1PWCS0JVJ851BCMPWE54M23JGHNRJ03VE76HW8ZH45B6WGG809T8T2HQRBHBPFN3WC9NSE7NY07YWH6V3MFMVPQ2GZJ6QJP4C9D2AGM88H71PD0H65YZY07HD72F3VV1QBJX6T1GPY3NYPEHH3","KWJQX6FGX9XTEXEXYVWJ657TGZHYWHX24KEE1XA5TW5NVVQMQGQVC6HE6GA4RBC1MM0S1GWN7Q838QQEP5DQNDZS7VDBSEP7SPHC832WRN492FN8CEVSJ01AAE36HRXNM8DN6JPK160DJ4M842W9STXQRWPPF94S6HER2WZX330EZ9CECD2NKWTPTYSDGA5FVZS2GC9ERQGYP4YKZWZ8DXRJWCNVE1G7BYYPWNTHPVX0EW5F9GHT4MR29YG9YMRS","50FNFP9CQNHME6EDC0JFNXDVHW58CMC0TJRRZ0FGAYK0MAYQ9EV5WZEYBDV5Y967H72AQBJ28GXD394BDW949KGEMNSAWYE3YX5GSZZNZ9CCXEDKJJSC7W1P1Z04B9VAAM07SPWN4EMREMR9R4XXCD014YHPMMKP64C58X93FCSFJNW8XDF5FW0S2KZJHSP8FGRNQ6D62CQJCVPS615D4N52XNPWTFS1R379GVKSZ4FRR6SRTT4R3PA271HTX3ZQ","NA6QQDXM6SB52138M8CN3E4ZY7P0P9QS9AJ6EGNKYYK58XTRR4D4BANF0ARSTVZGMSJ7FP803B87J21SR2KT35V5K4FEHWVQ9WB9F9ZFMREFAA3SP3NWXNYJQQS0YA2K3RRAWJYJ2XSPQERXW71702QAEQRZQPMGXCMDKFNY9V7Q77147RQ7M87G8ZHZHX83D9C6QHKWGKTSQ0R66MZRCX29C6P6A5EGHACWVXD32Z8SA1ZPNJBDZY0PNJJM68D0","4M00E1KQVQ5KTJPXTCT3EYFN1SH975ZASNAWN7Y0NB12ANR6VZ1163GKCK9WBQJ34Q9P0G763PQ0B68WCJT2KXYXX5X3AV91BPDGGWBYB220HQ52V5VES6H7D75KYS176MA27G09X9PAGTQJ78VQHDAZCYZ5TS82D7HM5GT618605NVEYTYQYT3BA38R6V0RHYGE0CHNEXZTM0TFGDXQZ3P6CTNMYPPPM4E4PMCCQC4XR2S6CGVWYE1TXN0DC59S","N98TSJ6HGBS2N3K3MRPW6WEHBP409B4DC7PW8GMHM2QM2VGC0XQE1AQQ9AT1J4MZAK0B3RAMWF74XZT0D1CBDFABDY8M2M3X6QM7M4C8Z2DV7B2KHF3GBV4FMEG2PWAPW97QJ47383BT5XEYC4KQNXNK1K22NXYQJK0W5GS64KRFZWWKENZABG8VYZ7AYDAXMPVRSCQCCDE1RS6V8MDAVP65SAB59NSM0QQMBAPVBDTWD0P3HCZMG4F17QGTYZYP","ABT20WXHNAHYANMGCVGR9VC1QEQMS5YYRMMPS8GE04RK7XDF4CFXV9HEC1TAN77PBZJXAGX8GVZ8Y0068T84M1DQ90F2VEZGHR1JBBJ91S6RER8FKC301STT9MSF0CYBQTG5GAZK948305FBT6QRFX5BYS4NB5ZX86YMTNZ01V5C26W6Q6T78E5MHC7PH21SQ2VERSMDBWC640068HDX4PCJWBV7809DBSHR2ASJBMM32C49SG4EYVW74H4385QA","M0HBDKZV4TAYEBJJFBMMV55735T3F114RQMHBW2FRTDNNEM6NVBYR65Q25GJ1XNZJV0J7CY55CWMNEARHS2F9TYZPM0BVFZ429DVB2BTX8ZJ6Y9M1RSMVZZZM3GVE865PFEYCVG40AVC5T01Q8XGW1FDZKZTXBVCR5J7H24870S3BJB820X7MQMXDHNNT7VA4XZQJRN96JNHFC3RWB8R2X6Z4BZA9AYETNHP180MY0CJB8CJ8P9DTFHFRAY4M8WE","HV2FVXRE3NGW0WR4XMX0QZX2Z0B7MQB2ANCC95GY0FY9WD74D6CSM11YC0HNMBYCX8CTX7QJS28FGK9FC2RP79M8MKBGHHP0E3SK33AHRQ6457TSMARYKRY2SNSTV7JC8Z1KPV55EH5EH9C736G42J9W84E61JRWG8PMD24V1TS0MJZWKNP1QKSET7N40VAJXZE6B2QDPH2D7F26S4SND52B8KS1HB0NH762F41QE22ANVSXV6S2TN73Y5MXTB9S","70M04PP2WVXGK3NJCMPXGQQCCCDTRW0AF5F8N8A605DHY4CKBFFXX9F5Y512JS3CY6MD9T7K1X0ANT3N5VJRWPF9Y1S97EA2NAZ2W783DEP32Q1M3Y93VBHKCBQNM9M456VSMZA16E8SP3EGPJ9K7QVE0KYV0VWFJRXCA2CWDNNFDEKEJ6GK164SKGZ5MV1QQEDM258SKTQQ0EER895QRAA50PHNNQF1Z8MGHMZCV9WP0ZSJASVK63E4Z073TTVD","799TV24HVN9JDQBG0WFGA5FH0RNJYFJG2XF1B93W5W571CRPMEAHCAKA294YT63VV0K7C46AQYF1AVS58ZJ58CESDYWMY770P95EXPM5H38NV3F3RDTDB7EX5Q9XN0X8QRW37BTN8VBQ4PZ6Z1Y4539C7QGE2F012RBKGK47BST1TQP3XA427H1CDGT8HJBHBY8BD4TH1N7EE0XZF2QV6AJ0JCPWD0H6TKPVMGH1QYKVEKEPQBX20S8K502AYGKQ"],["23NQ8CW42D7FAY7FD7636SFA7THRQA2EGJYMFYF75FA1ZATVSMPPYSHTEC58J84PKDHRD0KPFMK811CTSGW0W274D8A3E5FKEX6P5AZ2ST0K9PK65ZP6R6CR8RMWSGQQX5VAJ2YVJ3RNV20C7YJA0CDCJNQX3JKGRX5NRY8GY3RNA38HG3SM0NW85AKZ0SXP1XRZRY0A0T3SM6E4Q6PTJMJZJ677BRZHXAGB7BEMSK6KDERXHVN9AGN4J9SZ5AY0","ZNZFHJ4X5T79G4DXBX8TPG0RV8SV4B7ZKV56CAN893MZ6Z00CNFPH6KPPXTKM8MB6MTPSCHEA0PRGRHSAKPBSAVR019GB9E75TFSXV452ZQN8Q54ZH9BJPKMAMB26X8BDQKGYK01FAWRWZXJ1TW9GVPJ9TS03WKQS64TVRNH17TTFVED3K8MFMDC9SFSJ4FZC2J19XTSWASXSY4BDE4T8Z2VGWEGDTYF0PNPMHDN2FQN2T25T6TA2DW702HM9MBC","SCC6Y84J99YQ598PR67G5VDT1N9E6S2VEZ22QWD7PQDX108ZZJ2AK16YF9PS8WSAP1PCPM0AYD1R9522XZNGWEHX92RE3R0WVM43A38450XH0EHY6XYKCK0NWJJ9XKN0FG4GACRF6X1WG4Y7AYW6FBFMBKHWDHP8BCTT0J9Q9XXZEBC88C57DMG18ABP6S248JAX9JCN0GZ4Q25AX6XYA2W2KD7CW97H23H11NS47XMC0GFKF7FDZK11JQXASJMT","PJBV9WQ064WKS9WVV0JWN3JHGNCY82T42FC0ANKNV5SEE8BBPJTQ6HXM76D8FAH00A6GNB2HMRYAAS0ZTGESRE2JFDD5HFAWW2EX9BN1QA7T6EB1G4PBFFZXVD3AN3GZKK72A4VT5AFM9QF7GHEXVWC29AHH5HH3ZHYQ1AR5TB5G7CNRCM8GA1Q61P574H0G2DWJJ35XE79D3G6SEWPCPB3VZS89B3XY9A1HV86R6BVY6C48D7FHXEA903AVVSMP","7HS65FGXB1Y1E39DHB5J0SRX8KDS7K6YBNP1XAPBQQNBM8N2T1QAVVS37XG15A2ABWK9J2CP16WCWNMG1ZCS6KEY98DXRNKNEQ099M9P392D00R8HDE1Y8C918PV0DVXKM3141CYJFXTQCYM9A5ZYKDEXNF09NTWHNDT3F990R6EWYS0TT4F4YDW9605SWTPJ2G29CKQNP13N1KKFRJS4BME8VE800YH6HTCSWTPXQ19TFXCHET6PF9EZE00RF9D","39HPW4NZCE7T1TF3ZFW6FGSSSETD2NMY5ED7T49572WAZ5FBJJSGGK8G45CQSSS488KZ2WRVM2N79ZKD78AX08X7TAW7DGWKB5KX9KRFCAF7ZZJQP5W6A1652XYNV2NWR51MS30ME0Y6RPXCMVC7K2B3HK5TFQ893HE8JJJSD2TF3AS9RGMPRBXXPCTC1R3Y5MZJQ79RKV70PVW65MA00RPMC27VGBWW5MA7FWK4X8JWQ8PHB9666MVM64AZJZSC","WFPTT5ZYQZNF2TYKSMFT6D7RP85980PTXNP6PS1HY3J5Y4ZY8DEVNPGFDP85SQ2DST4QQ1EHQQ9CHP9T1Q25XSGNKAHF9QAXX6N029SNH3C0MTG5VAQWQMCSPAYBW08EKFJS560JXX1AT9JCD0CJXZTX1P7TC4XD1JJCBDYKQ592CJZDKBCVMDN2CAG7RNXVSKQ8EWCRM7NCEF3YVWBJ7SQRE9B3BB067SVSRJQ9SB0PTGRP93WRNQ2JEGC584K8","P0VHPX17WERWA5G19P5NV2ZBWXGX3YWFTYHMSWQ2C5H82TPQ08BNDPE813NYWWT231EMTY7VRW2ZS8S447T4K5BCD7S0YQX2D92G5VRP10YTC57P16QZ37T0QZ9JENVPT6665TZ97JERST3M8SG7BD3ZDM5H0S9KCQTXXDJCR9NB1WHNJFTYZ0DJYJFDNZMTKREQG3R5VF5DQXXWKJ2GVKZG6ED2VTJR11MKYTQBMXSKP50RJYEZMVCZQ8H5KZBV","VDS1SGX5A31KBPTBPBR82C4ZD5FG2XRDM5KGVAR7X8XGXP7ZMGREXS9CYFKA9SZ3GMF0H7X4H733C81HPNDDC2NPF4443CB0KY0XSMJ34SA6TY99SE0NTZ96RZ36CZP77HRQQ93WC2EN3FBGS9WQMZEQ7GKNZSH6RBGXMF237ZGRN4FPRGX771VXY5DQXFMPTFYRDHCPWJR9FZKF7TQT0MB1K412PRFHSHM9S8FZJCHYR2PP443WF9SZWGNM2CV8","J8KEHBYBAQVMTR1XTNDY23VSTQVW5CYE2EDNRE9DBP56DV1PSC2XXFH8KKHXT8D3TM5G6MCHE0JWNHN6417G696MHY5NZ6CRAYYBNR4YBPHDF94CHBMZSXH6KD3N0AVW3F0KK88DPA9G2CZ8C82QTV8Z8C8WRRWAHRH0X6SEGK4BJPEB3WEGRMD9AFYTXCPQ0N7BXK83052V4HD6W7AB0AE7EQ789XF6C8AD4TN7ARF7MJDMTCY624B6QR5K15Y5","R5D4QJ0HM2A9E8WEVZYKG9V612D1ZRRB6RCGK4S8MDBM7NRBP7DDJGV1H7SN0JEZJSTW7DGVA7WRK0Z8HSH6VGB2NZ2CRZKZTTX15W58EB2XKJKZJYA0AR5MKTNHPP7D39PFF8BP39PJX9VRFHGXMWB84GHVMND6S2M2WWVHMBVNK2D19TJ1240NZFV6GP64416JT0QW06ZQMXVHVD4GFRESTSVBH1G78AMJ972RAHBF4DM0PJXY0PTXYWQSP2GZ","KZ0R0MPKXYKS13894TAC8SMNGYQ9F1EB9VN9S4P7VJ8KDXJEREYR64HDRAP4HBRY9D50YDKDPE9AHMW9ETR4CXK3E0YQJQ2YGPAWJ823GH7KHJXK1GTFSZ3TERT1VB31FGXC0950TY0MAHESMPHNRXSG9738C0CEJ5Z1TNBVV48WJCH2JPCKAPDMTSFYFAECZ28WJ7J9EW459NNT5H4D0DB5ZZ2CY16CCC8PBC0K4ZYP1Y2ZW5MK0N0MJTXGQ49T","YVH178SVB7QK1HM2HVSDGBZ1QJN9NRTG3RXMC9YVS74VXTW719NRFMAXKJHVZJE77HNRW53S8DSD3QTYDZ9ZNZ1185GZXP0NNP40NQFYJQSBHWVE7WDPR5KZT42BWQE1P20B3F5HCHV3VD1ZCGCZ1NF5K5P4WKT01K7XKXNGFTX9HY5YAG30THBY4S3Q3CPMQBVCBXDHW929DHRBZWHZGGGBCTVNRRNDA7J0EVBAVYYHES2PDK9YW5STCG83S5AB","MA1RKAT9SCJSXJG7T5P1QF1VW9GRBCYK03P8Z55KSCK4SN1K0MFSXR3YERD4VA5QQ1N7KKHPDAPNYHQ6QW2TPBVM932A5G42VEKZ9CH5ZH7346RKDT8BMNSFYQ799W6042CCJ3JSESFYMER8JHZNBVFWFGMKMJJ642BCP9PPA9H7B0XSSGK2CZYWK3RQX5WKZK4V40AMTRJ9JRZNX7QKXRN9W464J3X3BRYHR8Y70C42HH0HVW86CPNVKWE0Q36C","K8CP0BM60F6P218ZMC4HW307DNN3FHHEG3XMWN55KEZR4RQ86ADXGJF2FKD7QW2TVQ3AWN2KVJ6QMP6SFJHF11C6T68FWKWMQVGQWYY28591XTQ3D78FB9N06T2W3R6EERTHRQNK7X9WP2BV3VM43A39YXVJ0W0BHJ0XSZQENN218ZR4GTVDX48788WXFD6064ZXGMWGD2EKWMS4BBVWK59FVZPGDRQD6EN3TRA784HBBWE831JWBEVX3YGPX9T0","QC2WWZPKJDCFZA65QKSGKW7YTAS9RP0W4H0FP96Z5TGCCD0K9FXFX4F75MQEDNKCM0B34D8ZR46CHR97HMMEB3W2T8W6P8X5289Z729365HVDQWCGHMJSGM7AHA1HS6ZND695X67D42H7XB7NQ5X57XG2S32R767YPQJERZ0ESG3S01NGNQ0P62KM3AT28JX41GMHPEA92V1Z80RYS8GWE9CGZ6K66NVGW72SN8DJGDFVC7771HP93X6DYN0TF63","H3P6CNRKH4BSCAWG3T9RRA89EBF1F91JEYEY4RZJQ2NNCZZASY362XHME0KNM36R4SNQD956CBW15XAXVK70TZM6TBVMHJ8KKQQQKBG4Q91ZHKAS10NEVD5B36QGYM7CMJM3DVJ00KTK0ZAM8C4YRNZDZKV5CNFGDR0DDP1P38704Y3G9KFRJ455SD39J7ZYDB5GQV17ZF2PE7GY927S1KSXV4KKXQ7FRX1VZRJGAD1F3DY4KJFRZJGAT5951JNS"]]} diff --git a/src/mint-lib/baseline/refresh_reveal.req b/src/mint-lib/baseline/refresh_reveal.req new file mode 100644 index 00000000..3fb14396 --- /dev/null +++ b/src/mint-lib/baseline/refresh_reveal.req @@ -0,0 +1,7 @@ +POST /refresh/reveal HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json +Content-Length: 255 + +{"session_hash":"V97SB8T670M9V71D1Q0KVQ4GSJCVQ5AAKTTH9QKT0ZJZZBFNZAV4NA8NMWRRGVPFEBEGB6ANCN9BPQASJ40TM4Y1C49648TJJ07PGSG","transfer_privs":[["EQKJA401A9NJ2YJDFZJ1EV8AYXBHWZB6NT5T0TWSJHVKVDM6W8A0"],["TKDJ4DF3GZVG0DGAB9E3RGBGSTANYB6JVVWXJGPMB2AY4VQNTBA0"]]} \ No newline at end of file diff --git a/src/mint-lib/baseline/reserve_status.req b/src/mint-lib/baseline/reserve_status.req new file mode 100644 index 00000000..4f988f66 --- /dev/null +++ b/src/mint-lib/baseline/reserve_status.req @@ -0,0 +1,4 @@ +GET /reserve/status?reserve_pub=TMZCK5CFM1KZQGY1WTF0CEZZPGA0670G94969RF79PA5106ARTK0 HTTP/1.1 +Host: localhost:8081 +Accept: */* + diff --git a/src/mint-lib/baseline/reserve_withdraw.req b/src/mint-lib/baseline/reserve_withdraw.req new file mode 100644 index 00000000..48495025 --- /dev/null +++ b/src/mint-lib/baseline/reserve_withdraw.req @@ -0,0 +1,7 @@ +POST /reserve/withdraw HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json +Content-Length: 919 + +{"coin_ev":"Q5X8A8TCBFH7E5BMY7HSB17SHFTM1JPJGV61P2CA7Z9EXG8P2HYS69B31NZESKXHSZHNJ2DQN3CC2AWFNC6V90J577JD3TXBMAY8Y5M9V60KKT73Z1DW24JFSNAK91G1F2WT55ADP1EG7N5F9AY7A7ZJD03MPYSH0RDP7SVZS2KRPA5JRHFR4GDJ59CFNE7A43M95ZKQHQAS8","denom_pub":"51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GT58S2K2HJ16H336C9N8CVK4E9N6H1MADHH61330HHM6N1K8E1H8RVKJH256D1M6E1K8RWKJGSH8S2M6DJ170TK2H266GTK8DSS64RKJDJ26D144DJ474SK0GHQ711MAD9G752M2CJ58S1KJDA570SK2E9G8N23GCJ28S146DHH610K2H1Q8CW3GGA16S146H9G68TKACSQ6914CE1H691K2E9N6RWM8H9P8CWM2H9S8GSK0H9P6D1K6H9G6X0M4C2171144HJ46N334H9J692M4H9M8MR4CCJ46GRKEGA46533CDJ38MV4CH9K892MAH1P8S2K6D9K6N246E256H244G9Q6D346GJ56S23JGHJ690KADHJ8H242H2575132CSM6X1M4G9N6RR48E9H8MVM8E9354520818CMG26C1H60R30C935452081918G2J2G0","reserve_pub":"TMZCK5CFM1KZQGY1WTF0CEZZPGA0670G94969RF79PA5106ARTK0","reserve_sig":"8427B3RTB217124EB1C37ZVJFC08KN17RHGHE9ENZQMQVJ0S11SAX6H8Z06SWCKT06DRQ9DQ8XD786XKQ94T27PYR9GC9EMT1Y02W10"} \ No newline at end of file diff --git a/src/mint-lib/baseline/wire.req b/src/mint-lib/baseline/wire.req new file mode 100644 index 00000000..a4f1d074 --- /dev/null +++ b/src/mint-lib/baseline/wire.req @@ -0,0 +1,5 @@ +GET /wire HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json + diff --git a/src/mint-lib/baseline/wire_sepa.req b/src/mint-lib/baseline/wire_sepa.req new file mode 100644 index 00000000..80d3d461 --- /dev/null +++ b/src/mint-lib/baseline/wire_sepa.req @@ -0,0 +1,5 @@ +GET /wire/sepa HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json + diff --git a/src/mint-lib/baseline/wire_test.req b/src/mint-lib/baseline/wire_test.req new file mode 100644 index 00000000..684352c9 --- /dev/null +++ b/src/mint-lib/baseline/wire_test.req @@ -0,0 +1,5 @@ +GET /wire/test HTTP/1.1 +Host: localhost:8081 +Accept: */* +Content-Type: application/json + -- cgit v1.2.3 From 3cb9cc788711cc2cc4eda0e06799f6628623e827 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 22 Sep 2015 09:09:42 +0200 Subject: do not try to free NULL --- src/mint-lib/test-mint-home/config/mint-common.conf | 2 +- src/mint/taler-mint-httpd_refresh.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/mint-lib') diff --git a/src/mint-lib/test-mint-home/config/mint-common.conf b/src/mint-lib/test-mint-home/config/mint-common.conf index b2b94826..1a61f4f8 100644 --- a/src/mint-lib/test-mint-home/config/mint-common.conf +++ b/src/mint-lib/test-mint-home/config/mint-common.conf @@ -17,7 +17,7 @@ MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG DB = postgres # Is this is a testcase, use transient DB actions? -TESTRUN = YES +TESTRUN = NO [mintdb-postgres] diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index 72288b0a..29fd4bae 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -536,7 +536,6 @@ handle_refresh_melt_json (struct MHD_Connection *connection, free_commit_coins (commit_coin, TALER_CNC_KAPPA, num_newcoins); - GNUNET_free (link_enc); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } rcc->refresh_link -- cgit v1.2.3