exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 17c00ab24e33118d269e3725c02501322b09de06
parent c75013d677ab16961ca0cd5ad4579e7d7787c269
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue,  3 Mar 2026 22:31:06 +0100

modernize GET /keys API

Diffstat:
Msrc/exchange-tools/taler-auditor-offline.c | 31++++++++++++++++++-------------
Msrc/include/taler/taler-exchange/get-keys.h | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/lib/Makefile.am | 1+
Msrc/lib/exchange_api_handle.c | 494++-----------------------------------------------------------------------------
Msrc/lib/exchange_api_handle.h | 21++++++++++++++++++++-
Msrc/lib/exchange_api_post-batch-deposit.c | 6+++---
Msrc/testing/testing_api_cmd_get_exchange.c | 35++++++++++++++++++++++++++---------
7 files changed, 229 insertions(+), 529 deletions(-)

diff --git a/src/exchange-tools/taler-auditor-offline.c b/src/exchange-tools/taler-auditor-offline.c @@ -22,7 +22,15 @@ #include <gnunet/gnunet_json_lib.h> #include <microhttpd.h> #include "taler/taler_json_lib.h" -#include "taler/taler_exchange_service.h" + +#define TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE \ + char *const +#include "taler/taler-exchange/get-keys.h" + +struct DenominationAddRequest; +#define TALER_EXCHANGE_POST_AUDITORS_RESULT_CLOSURE \ + struct DenominationAddRequest +#include "taler/taler-exchange/post-auditors-AUDITOR_PUB-H_DENOM_PUB.h" /** * Name of the input of a denomination key signature for the 'upload' operation. @@ -388,15 +396,14 @@ load_offline_key (int do_create) * Function called with information about the post denomination (signature) * add operation result. * - * @param cls closure with a `struct DenominationAddRequest` + * @param dar the add request * @param adr response data */ static void denomination_add_cb ( - void *cls, + struct DenominationAddRequest *dar, const struct TALER_EXCHANGE_PostAuditorsResponse *adr) { - struct DenominationAddRequest *dar = cls; const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) @@ -647,18 +654,16 @@ do_upload (char *const *args) * Function called with information about who is auditing * a particular exchange and what keys the exchange is using. * - * @param cls closure with the `char **` remaining args + * @param args the remaining args * @param kr response data * @param keys key data from the exchange */ static void keys_cb ( - void *cls, + char *const *args, const struct TALER_EXCHANGE_KeysResponse *kr, struct TALER_EXCHANGE_Keys *keys) { - char *const *args = cls; - exchange = NULL; switch (kr->hr.http_status) { @@ -722,11 +727,11 @@ do_download (char *const *args) global_ret = EXIT_NOTCONFIGURED; return; } - exchange = TALER_EXCHANGE_get_keys (ctx, - exchange_url, - NULL, - &keys_cb, - (void *) args); + exchange = TALER_EXCHANGE_get_keys_create (ctx, + exchange_url); + TALER_EXCHANGE_get_keys_start (exchange, + &keys_cb, + args); GNUNET_free (exchange_url); } diff --git a/src/include/taler/taler-exchange/get-keys.h b/src/include/taler/taler-exchange/get-keys.h @@ -870,6 +870,90 @@ struct TALER_EXCHANGE_KeysResponse /** + * Possible options we can set for the GET /keys request. + */ +enum TALER_EXCHANGE_GetKeysOption +{ + /** + * End of list of options. + */ + TALER_EXCHANGE_GET_KEYS_OPTION_END = 0, + + /** + * Perform incremental fetch using the given previous keys object. + * Defaults to NULL (no incremental fetch). + */ + TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS + +}; + + +/** + * Value for an option for the GET /keys request. + */ +struct TALER_EXCHANGE_GetKeysOptionValue +{ + /** + * Type of the option being set. + */ + enum TALER_EXCHANGE_GetKeysOption option; + + /** + * Specific option value. + */ + union + { + /** + * Value if @e option is TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS. + * Previous keys object for incremental fetch. + */ + struct TALER_EXCHANGE_Keys *last_keys; + + } details; + +}; + + +/** + * @brief Handle for a GET /keys request. + */ +struct TALER_EXCHANGE_GetKeysHandle; + + +/** + * Terminate the list of options. + * + * @return the terminating object of struct TALER_EXCHANGE_GetKeysOptionValue + */ +#define TALER_EXCHANGE_get_keys_option_end_() \ + (const struct TALER_EXCHANGE_GetKeysOptionValue) \ + { \ + .option = TALER_EXCHANGE_GET_KEYS_OPTION_END \ + } + +/** + * Set previous keys for incremental fetch. + * + * @param k previous keys object (may be NULL to request full fetch) + * @return representation of the option as a struct TALER_EXCHANGE_GetKeysOptionValue + */ +#define TALER_EXCHANGE_get_keys_option_last_keys(k) \ + (const struct TALER_EXCHANGE_GetKeysOptionValue) \ + { \ + .option = TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS, \ + .details.last_keys = (k) \ + } + + +#ifndef TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_GetKeysCallback. + */ +#define TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE void +#endif /* TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE */ + +/** * Function called with information about who is auditing * a particular exchange and what keys the exchange is using. * The ownership over the @a keys object is passed to @@ -884,39 +968,85 @@ struct TALER_EXCHANGE_KeysResponse */ typedef void (*TALER_EXCHANGE_GetKeysCallback) ( - void *cls, + TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE *cls, const struct TALER_EXCHANGE_KeysResponse *kr, struct TALER_EXCHANGE_Keys *keys); /** - * @brief Handle for a GET /keys request. + * Set up GET /keys operation. + * Note that you must explicitly start the operation after + * possibly setting options. + * + * @param ctx the context + * @param url HTTP base URL for the exchange + * @return handle to operation, NULL on error */ -struct TALER_EXCHANGE_GetKeysHandle; +struct TALER_EXCHANGE_GetKeysHandle * +TALER_EXCHANGE_get_keys_create ( + struct GNUNET_CURL_Context *ctx, + const char *url); /** - * Fetch the main /keys resources from an exchange. Does an incremental - * fetch if @a last_keys is given. The obtained information will be passed to - * the @a cert_cb (possibly after first merging it with @a last_keys to - * produce a full picture; expired keys (for deposit) will be removed from @a - * last_keys if there are any). + * Set the requested options for the operation. * - * @param ctx the context - * @param url HTTP base URL for the exchange - * @param[in,out] last_keys previous keys object, NULL for none - * @param cert_cb function to call with the exchange's certification information, - * possibly called repeatedly if the information changes + * If any option fails, other options may or may not be applied. + * + * @param gkh the request to set the options for + * @param num_options length of the @a options array + * @param options an array of options + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_keys_set_options_ ( + struct TALER_EXCHANGE_GetKeysHandle *gkh, + unsigned int num_options, + const struct TALER_EXCHANGE_GetKeysOptionValue *options); + + +/** + * Set the requested options for the operation. + * + * If any option fails, other options may or may not be applied. + * + * It should be used with helpers that create required options, for example: + * + * TALER_EXCHANGE_get_keys_set_options ( + * gkh, + * TALER_EXCHANGE_get_keys_option_last_keys (prev_keys)); + * + * @param gkh the request to set the options for + * @param ... the list of options, each created by a + * TALER_EXCHANGE_get_keys_option_NAME(VALUE) helper + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +#define TALER_EXCHANGE_get_keys_set_options(gkh,...) \ + TALER_EXCHANGE_get_keys_set_options_ ( \ + gkh, \ + TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE, \ + ((const struct TALER_EXCHANGE_GetKeysOptionValue[]) \ + {__VA_ARGS__, TALER_EXCHANGE_get_keys_option_end_ () } \ + )) + + +/** + * Start GET /keys operation. + * + * @param[in,out] gkh operation to start + * @param cert_cb function to call with the exchange's certification information * @param cert_cb_cls closure for @a cert_cb - * @return the exchange handle; NULL upon error + * @return status code, #TALER_EC_NONE on success */ -struct TALER_EXCHANGE_GetKeysHandle * -TALER_EXCHANGE_get_keys ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *last_keys, +enum TALER_ErrorCode +TALER_EXCHANGE_get_keys_start ( + struct TALER_EXCHANGE_GetKeysHandle *gkh, TALER_EXCHANGE_GetKeysCallback cert_cb, - void *cert_cb_cls); + TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE *cert_cb_cls); /** diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -33,6 +33,7 @@ libtalerexchange_la_SOURCES = \ exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c \ exchange_api_get-kyc-info-ACCESS_TOKEN.c \ exchange_api_get-kyc-proof-PROVIDER_NAME.c \ + exchange_api_get-keys.c \ exchange_api_get-management-keys.c \ exchange_api_get-purses-PURSE_PUB-merge.c \ exchange_api_get-reserves-attest-RESERVE_PUB.c \ diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c @@ -64,66 +64,6 @@ #define LIFETIME_TOLERANCE GNUNET_TIME_UNIT_HOURS /** - * If the "Expire" cache control header is missing, for - * how long do we assume the reply to be valid at least? - */ -#define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS - -/** - * If the "Expire" cache control header is missing, for - * how long do we assume the reply to be valid at least? - */ -#define MINIMUM_EXPIRATION GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 2) - - -/** - * Handle for a GET /keys request. - */ -struct TALER_EXCHANGE_GetKeysHandle -{ - - /** - * The exchange base URL (i.e. "https://exchange.demo.taler.net/") - */ - char *exchange_url; - - /** - * The url for the /keys request. - */ - char *url; - - /** - * Previous /keys response, NULL for none. - */ - struct TALER_EXCHANGE_Keys *prev_keys; - - /** - * Entry for this request with the `struct GNUNET_CURL_Context`. - */ - struct GNUNET_CURL_Job *job; - - /** - * Expiration time according to "Expire:" header. - * 0 if not provided by the server. - */ - struct GNUNET_TIME_Timestamp expire; - - /** - * Function to call with the exchange's certification data, - * NULL if this has already been done. - */ - TALER_EXCHANGE_GetKeysCallback cert_cb; - - /** - * Closure to pass to @e cert_cb. - */ - void *cert_cb_cls; - -}; - - -/** * Element in the `struct SignatureContext` array. */ struct SignatureElement @@ -355,7 +295,7 @@ parse_fees (const struct TALER_MasterPublicKeyP *master_pub, void -TEAH_get_auditors_for_dc ( +TALER_EXCHANGE_get_auditors_for_dc_ ( struct TALER_EXCHANGE_Keys *keys, TEAH_AuditorCallback ac, void *ac_cls) @@ -967,11 +907,12 @@ parse_wads (const json_t *wads_array, * @return #GNUNET_OK on success, #GNUNET_SYSERR on error * (malformed JSON) */ -static enum GNUNET_GenericReturnValue -decode_keys_json (const json_t *resp_obj, - bool check_sig, - struct TALER_EXCHANGE_Keys *key_data, - enum TALER_EXCHANGE_VersionCompatibility *vc) +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_decode_keys_json_ ( + const json_t *resp_obj, + bool check_sig, + struct TALER_EXCHANGE_Keys *key_data, + enum TALER_EXCHANGE_VersionCompatibility *vc) { struct TALER_ExchangeSignatureP exchange_sig; struct TALER_ExchangePublicKeyP exchange_pub; @@ -1609,419 +1550,6 @@ EXITIF_exit: } -/** - * Callback used when downloading the reply to a /keys request - * is complete. - * - * @param cls the `struct KeysRequest` - * @param response_code HTTP response code, 0 on error - * @param resp_obj parsed JSON result, NULL on error - */ -static void -keys_completed_cb (void *cls, - long response_code, - const void *resp_obj) -{ - struct TALER_EXCHANGE_GetKeysHandle *gkh = cls; - const json_t *j = resp_obj; - struct TALER_EXCHANGE_Keys *kd = NULL; - struct TALER_EXCHANGE_KeysResponse kresp = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code, - .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR, - }; - - gkh->job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received keys from URL `%s' with status %ld and expiration %s.\n", - gkh->url, - response_code, - GNUNET_TIME_timestamp2s (gkh->expire)); - if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time)) - { - if (MHD_HTTP_OK == response_code) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Exchange failed to give expiration time, assuming in %s\n", - GNUNET_TIME_relative2s (DEFAULT_EXPIRATION, - true)); - gkh->expire - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION)); - } - switch (response_code) - { - case 0: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to receive /keys response from exchange %s\n", - gkh->exchange_url); - break; - case MHD_HTTP_OK: - if (NULL == j) - { - GNUNET_break (0); - response_code = 0; - break; - } - kd = GNUNET_new (struct TALER_EXCHANGE_Keys); - kd->exchange_url = GNUNET_strdup (gkh->exchange_url); - if (NULL != gkh->prev_keys) - { - const struct TALER_EXCHANGE_Keys *kd_old = gkh->prev_keys; - - /* We keep the denomination keys and auditor signatures from the - previous iteration (/keys cherry picking) */ - kd->num_denom_keys - = kd_old->num_denom_keys; - kd->last_denom_issue_date - = kd_old->last_denom_issue_date; - GNUNET_array_grow (kd->denom_keys, - kd->denom_keys_size, - kd->num_denom_keys); - /* First make a shallow copy, we then need another pass for the RSA key... */ - GNUNET_memcpy (kd->denom_keys, - kd_old->denom_keys, - kd_old->num_denom_keys - * sizeof (struct TALER_EXCHANGE_DenomPublicKey)); - for (unsigned int i = 0; i<kd_old->num_denom_keys; i++) - TALER_denom_pub_copy (&kd->denom_keys[i].key, - &kd_old->denom_keys[i].key); - kd->num_auditors = kd_old->num_auditors; - kd->auditors - = GNUNET_new_array (kd->num_auditors, - struct TALER_EXCHANGE_AuditorInformation); - /* Now the necessary deep copy... */ - for (unsigned int i = 0; i<kd_old->num_auditors; i++) - { - const struct TALER_EXCHANGE_AuditorInformation *aold = - &kd_old->auditors[i]; - struct TALER_EXCHANGE_AuditorInformation *anew = &kd->auditors[i]; - - anew->auditor_pub = aold->auditor_pub; - anew->auditor_url = GNUNET_strdup (aold->auditor_url); - anew->auditor_name = GNUNET_strdup (aold->auditor_name); - GNUNET_array_grow (anew->denom_keys, - anew->num_denom_keys, - aold->num_denom_keys); - GNUNET_memcpy ( - anew->denom_keys, - aold->denom_keys, - aold->num_denom_keys - * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo)); - } - } - /* Now decode fresh /keys response */ - if (GNUNET_OK != - decode_keys_json (j, - true, - kd, - &kresp.details.ok.compat)) - { - TALER_LOG_ERROR ("Could not decode /keys response\n"); - kd->rc = 1; - TALER_EXCHANGE_keys_decref (kd); - kd = NULL; - kresp.hr.http_status = 0; - kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - kd->rc = 1; - kd->key_data_expiration = gkh->expire; - if (GNUNET_TIME_relative_cmp ( - GNUNET_TIME_absolute_get_remaining (gkh->expire.abs_time), - <, - MINIMUM_EXPIRATION)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Exchange returned keys with expiration time below %s. Compensating.\n", - GNUNET_TIME_relative2s (MINIMUM_EXPIRATION, - true)); - kd->key_data_expiration - = GNUNET_TIME_relative_to_timestamp (MINIMUM_EXPIRATION); - } - - kresp.details.ok.keys = kd; - break; - case MHD_HTTP_BAD_REQUEST: - case MHD_HTTP_UNAUTHORIZED: - case MHD_HTTP_FORBIDDEN: - case MHD_HTTP_NOT_FOUND: - if (NULL == j) - { - kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec); - } - else - { - kresp.hr.ec = TALER_JSON_get_error_code (j); - kresp.hr.hint = TALER_JSON_get_error_hint (j); - } - break; - default: - if (NULL == j) - { - kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec); - } - else - { - kresp.hr.ec = TALER_JSON_get_error_code (j); - kresp.hr.hint = TALER_JSON_get_error_hint (j); - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d\n", - (unsigned int) response_code, - (int) kresp.hr.ec); - break; - } - gkh->cert_cb (gkh->cert_cb_cls, - &kresp, - kd); - TALER_EXCHANGE_get_keys_cancel (gkh); -} - - -/** - * Define a max length for the HTTP "Expire:" header - */ -#define MAX_DATE_LINE_LEN 32 - - -/** - * Parse HTTP timestamp. - * - * @param dateline header to parse header - * @param[out] at where to write the result - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -parse_date_string (const char *dateline, - struct GNUNET_TIME_Timestamp *at) -{ - static const char *MONTHS[] = - { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; - int year; - int mon; - int day; - int hour; - int min; - int sec; - char month[4]; - struct tm tm; - time_t t; - - /* We recognize the three formats in RFC2616, section 3.3.1. Month - names are always in English. The formats are: - Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 - Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 - Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - Note that the first is preferred. - */ - - if (strlen (dateline) > MAX_DATE_LINE_LEN) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - while (*dateline == ' ') - ++dateline; - while (*dateline && *dateline != ' ') - ++dateline; - while (*dateline == ' ') - ++dateline; - /* We just skipped over the day of the week. Now we have:*/ - if ( (sscanf (dateline, - "%d %3s %d %d:%d:%d", - &day, month, &year, &hour, &min, &sec) != 6) && - (sscanf (dateline, - "%d-%3s-%d %d:%d:%d", - &day, month, &year, &hour, &min, &sec) != 6) && - (sscanf (dateline, - "%3s %d %d:%d:%d %d", - month, &day, &hour, &min, &sec, &year) != 6) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* Two digit dates are defined to be relative to 1900; all other dates - * are supposed to be represented as four digits. */ - if (year < 100) - year += 1900; - - for (mon = 0; ; mon++) - { - if (! MONTHS[mon]) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 == strcasecmp (month, - MONTHS[mon])) - break; - } - - memset (&tm, 0, sizeof(tm)); - tm.tm_year = year - 1900; - tm.tm_mon = mon; - tm.tm_mday = day; - tm.tm_hour = hour; - tm.tm_min = min; - tm.tm_sec = sec; - - t = mktime (&tm); - if (((time_t) -1) == t) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "mktime"); - return GNUNET_SYSERR; - } - if (t < 0) - t = 0; /* can happen due to timezone issues if date was 1.1.1970 */ - *at = GNUNET_TIME_timestamp_from_s (t); - return GNUNET_OK; -} - - -/** - * Function called for each header in the HTTP /keys response. - * Finds the "Expire:" header and parses it, storing the result - * in the "expire" field of the keys request. - * - * @param buffer header data received - * @param size size of an item in @a buffer - * @param nitems number of items in @a buffer - * @param userdata the `struct TALER_EXCHANGE_GetKeysHandle` - * @return `size * nitems` on success (everything else aborts) - */ -static size_t -header_cb (char *buffer, - size_t size, - size_t nitems, - void *userdata) -{ - struct TALER_EXCHANGE_GetKeysHandle *kr = userdata; - size_t total = size * nitems; - char *val; - - if (total < strlen (MHD_HTTP_HEADER_EXPIRES ": ")) - return total; - if (0 != strncasecmp (MHD_HTTP_HEADER_EXPIRES ": ", - buffer, - strlen (MHD_HTTP_HEADER_EXPIRES ": "))) - return total; - val = GNUNET_strndup (&buffer[strlen (MHD_HTTP_HEADER_EXPIRES ": ")], - total - strlen (MHD_HTTP_HEADER_EXPIRES ": ")); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Found %s header `%s'\n", - MHD_HTTP_HEADER_EXPIRES, - val); - if (GNUNET_OK != - parse_date_string (val, - &kr->expire)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse %s-header `%s'\n", - MHD_HTTP_HEADER_EXPIRES, - val); - kr->expire = GNUNET_TIME_UNIT_ZERO_TS; - } - GNUNET_free (val); - return total; -} - - -struct TALER_EXCHANGE_GetKeysHandle * -TALER_EXCHANGE_get_keys ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct TALER_EXCHANGE_Keys *last_keys, - TALER_EXCHANGE_GetKeysCallback cert_cb, - void *cert_cb_cls) -{ - struct TALER_EXCHANGE_GetKeysHandle *gkh; - CURL *eh; - char last_date[80] = { 0 }; - - TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n", - url); - gkh = GNUNET_new (struct TALER_EXCHANGE_GetKeysHandle); - gkh->exchange_url = GNUNET_strdup (url); - gkh->cert_cb = cert_cb; - gkh->cert_cb_cls = cert_cb_cls; - if (NULL != last_keys) - { - gkh->prev_keys = TALER_EXCHANGE_keys_incref (last_keys); - TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n", - GNUNET_TIME_timestamp2s ( - last_keys->last_denom_issue_date)); - GNUNET_snprintf (last_date, - sizeof (last_date), - "%llu", - (unsigned long long) - last_keys->last_denom_issue_date.abs_time.abs_value_us - / 1000000LLU); - } - gkh->url = TALER_url_join (url, - "keys", - (NULL != last_keys) - ? "last_issue_date" - : NULL, - (NULL != last_keys) - ? last_date - : NULL, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting keys with URL `%s'.\n", - gkh->url); - eh = TALER_EXCHANGE_curl_easy_get_ (gkh->url); - if (NULL == eh) - { - GNUNET_break (0); - GNUNET_free (gkh->exchange_url); - GNUNET_free (gkh->url); - GNUNET_free (gkh); - return NULL; - } - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_VERBOSE, - 0)); - GNUNET_break (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_TIMEOUT, - 120 /* seconds */)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_HEADERFUNCTION, - &header_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_HEADERDATA, - gkh)); - gkh->job = GNUNET_CURL_job_add_with_ct_json (ctx, - eh, - &keys_completed_cb, - gkh); - return gkh; -} - - -void -TALER_EXCHANGE_get_keys_cancel ( - struct TALER_EXCHANGE_GetKeysHandle *gkh) -{ - if (NULL != gkh->job) - { - GNUNET_CURL_job_cancel (gkh->job); - gkh->job = NULL; - } - TALER_EXCHANGE_keys_decref (gkh->prev_keys); - GNUNET_free (gkh->exchange_url); - GNUNET_free (gkh->url); - GNUNET_free (gkh); -} - - enum GNUNET_GenericReturnValue TALER_EXCHANGE_test_signing_key ( const struct TALER_EXCHANGE_Keys *keys, @@ -2238,10 +1766,10 @@ TALER_EXCHANGE_keys_from_json (const json_t *j) } keys = GNUNET_new (struct TALER_EXCHANGE_Keys); if (GNUNET_OK != - decode_keys_json (jkeys, - false, - keys, - &compat)) + TALER_EXCHANGE_decode_keys_json_ (jkeys, + false, + keys, + &compat)) { GNUNET_break (0); return NULL; diff --git a/src/lib/exchange_api_handle.h b/src/lib/exchange_api_handle.h @@ -54,11 +54,30 @@ typedef void * @param ac_cls closure for @a ac */ void -TEAH_get_auditors_for_dc ( +TALER_EXCHANGE_get_auditors_for_dc_ ( struct TALER_EXCHANGE_Keys *keys, TEAH_AuditorCallback ac, void *ac_cls); +/** + * Decode the JSON in @a resp_obj from the /keys response + * and store the data in the @a key_data. + * + * @param[in] resp_obj JSON object to parse + * @param check_sig true if we should check the signature + * @param[out] key_data where to store the results we decoded + * @param[out] vc where to store version compatibility data + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * (malformed JSON) + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_decode_keys_json_ ( + const json_t *resp_obj, + bool check_sig, + struct TALER_EXCHANGE_Keys *key_data, + enum TALER_EXCHANGE_VersionCompatibility *vc); + + /* end of exchange_api_handle.h */ #endif diff --git a/src/lib/exchange_api_post-batch-deposit.c b/src/lib/exchange_api_post-batch-deposit.c @@ -417,9 +417,9 @@ handle_deposit_finished (void *cls, break; } } - TEAH_get_auditors_for_dc (dh->keys, - &auditor_cb, - dh); + TALER_EXCHANGE_get_auditors_for_dc_ (dh->keys, + &auditor_cb, + dh); } dr->details.ok.exchange_sig = &dh->exchange_sig; dr->details.ok.exchange_pub = &dh->exchange_pub; diff --git a/src/testing/testing_api_cmd_get_exchange.c b/src/testing/testing_api_cmd_get_exchange.c @@ -24,6 +24,8 @@ #include "taler/platform.h" #include "taler/taler_json_lib.h" #include <gnunet/gnunet_curl_lib.h> +struct GetExchangeState; +#define TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE struct GetExchangeState #include "taler/taler_testing_lib.h" @@ -85,16 +87,15 @@ struct GetExchangeState * Function called with information about who is auditing * a particular exchange and what keys the exchange is using. * - * @param cls closure + * @param ges our command state * @param kr response from /keys * @param[in] keys the keys of the exchange */ static void -cert_cb (void *cls, +cert_cb (struct GetExchangeState *ges, const struct TALER_EXCHANGE_KeysResponse *kr, struct TALER_EXCHANGE_Keys *keys) { - struct GetExchangeState *ges = cls; const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr; struct TALER_TESTING_Interpreter *is = ges->is; @@ -232,15 +233,31 @@ get_exchange_run (void *cls, } ges->is = is; ges->exchange - = TALER_EXCHANGE_get_keys (TALER_TESTING_interpreter_get_context (is), - ges->exchange_url, - xkeys, - &cert_cb, - ges); - TALER_EXCHANGE_keys_decref (xkeys); + = TALER_EXCHANGE_get_keys_create ( + TALER_TESTING_interpreter_get_context (is), + ges->exchange_url); if (NULL == ges->exchange) { GNUNET_break (0); + TALER_EXCHANGE_keys_decref (xkeys); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL != xkeys) + { + TALER_EXCHANGE_get_keys_set_options ( + ges->exchange, + TALER_EXCHANGE_get_keys_option_last_keys (xkeys)); + } + TALER_EXCHANGE_keys_decref (xkeys); + if (TALER_EC_NONE != + TALER_EXCHANGE_get_keys_start (ges->exchange, + &cert_cb, + ges)) + { + GNUNET_break (0); + TALER_EXCHANGE_get_keys_cancel (ges->exchange); + ges->exchange = NULL; TALER_TESTING_interpreter_fail (is); return; }