aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-05-31 18:59:19 +0200
committerChristian Grothoff <christian@grothoff.org>2016-05-31 18:59:19 +0200
commit07449ce578347a79fcff9fda0164b7b7782cec80 (patch)
treecad0cd771065d97d313397d0c769182384343447 /src
parentbc7c9e686ed24acc8d690f32d184c516858c77a1 (diff)
parentc6d1fb3ceb75e7cd5c3ccd320690bc3334240bde (diff)
Merge branch 'master' of git+ssh://taler.net/var/git/exchange
Diffstat (limited to 'src')
-rw-r--r--src/benchmark/Makefile.am6
-rwxr-xr-xsrc/benchmark/taler-exchange-benchmarkbin24848 -> 0 bytes
-rw-r--r--src/exchange-lib/exchange_api_handle.c275
-rw-r--r--src/exchange/taler-exchange-httpd_keystate.c91
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c7
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c6
-rw-r--r--src/exchangedb/test_exchangedb.c67
-rw-r--r--src/include/taler_exchange_service.h23
8 files changed, 408 insertions, 67 deletions
diff --git a/src/benchmark/Makefile.am b/src/benchmark/Makefile.am
index 4deb81723..e92a13944 100644
--- a/src/benchmark/Makefile.am
+++ b/src/benchmark/Makefile.am
@@ -13,9 +13,9 @@ taler_exchange_benchmark_SOURCES = \
taler-exchange-benchmark.c
taler_exchange_benchmark_LDADD = \
$(LIBGCRYPT_LIBS) \
- -ltalerexchange \
- -ltalerjson \
- -ltalerutil \
+ $(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/exchange-lib/libtalerexchange.la \
-lgnunetjson \
-lgnunetcurl \
-lgnunetutil \
diff --git a/src/benchmark/taler-exchange-benchmark b/src/benchmark/taler-exchange-benchmark
deleted file mode 100755
index ae793dc92..000000000
--- a/src/benchmark/taler-exchange-benchmark
+++ /dev/null
Binary files differ
diff --git a/src/exchange-lib/exchange_api_handle.c b/src/exchange-lib/exchange_api_handle.c
index 6de5ee59b..b9104869e 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -109,6 +109,11 @@ struct TALER_EXCHANGE_Handle
struct TALER_EXCHANGE_Keys key_data;
/**
+ * When does @e key_data expire?
+ */
+ struct GNUNET_TIME_Absolute key_data_expiration;
+
+ /**
* Raw key data of the exchange, only valid if
* @e handshake_complete is past stage #MHS_CERT.
*/
@@ -144,6 +149,12 @@ struct KeysRequest
*/
struct GNUNET_CURL_Job *job;
+ /**
+ * Expiration time according to "Expire:" header.
+ * 0 if not provided by the server.
+ */
+ struct GNUNET_TIME_Absolute expire;
+
};
@@ -485,6 +496,7 @@ decode_keys_json (const json_t *resp_obj,
struct GNUNET_HashContext *hash_context;
struct TALER_ExchangePublicKeyP pub;
+ memset (key_data, 0, sizeof (struct TALER_EXCHANGE_Keys));
if (JSON_OBJECT != json_typeof (resp_obj))
return GNUNET_SYSERR;
@@ -605,6 +617,58 @@ decode_keys_json (const json_t *resp_obj,
/**
+ * Free key data object.
+ *
+ * @param key_data data to free (pointer itself excluded)
+ */
+static void
+free_key_data (struct TALER_EXCHANGE_Keys *key_data)
+{
+ unsigned int i;
+
+ GNUNET_array_grow (key_data->sign_keys,
+ key_data->num_sign_keys,
+ 0);
+ for (i=0;i<key_data->num_denom_keys;i++)
+ GNUNET_CRYPTO_rsa_public_key_free (key_data->denom_keys[i].key.rsa_public_key);
+ GNUNET_array_grow (key_data->denom_keys,
+ key_data->num_denom_keys,
+ 0);
+ GNUNET_array_grow (key_data->auditors,
+ key_data->num_auditors,
+ 0);
+}
+
+
+/**
+ * Initiate download of /keys from the exchange.
+ *
+ * @param exchange where to download /keys from
+ */
+static void
+request_keys (struct TALER_EXCHANGE_Handle *exchange);
+
+
+/**
+ * Check if our current response for /keys is valid, and if
+ * not trigger download.
+ *
+ * @param exchange exchange to check keys for
+ * @return until when the response is current, 0 if we are re-downloading
+ */
+struct GNUNET_TIME_Absolute
+TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange)
+{
+ if (NULL != exchange->kr)
+ return GNUNET_TIME_UNIT_ZERO_ABS;
+ if (0 < GNUNET_TIME_absolute_get_remaining (exchange->key_data_expiration).rel_value_us)
+ return exchange->key_data_expiration;
+ request_keys (exchange);
+ return GNUNET_TIME_UNIT_ZERO_ABS;
+}
+
+
+/**
* Callback used when downloading the reply to a /keys request
* is complete.
*
@@ -619,13 +683,16 @@ keys_completed_cb (void *cls,
{
struct KeysRequest *kr = cls;
struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
- TALER_EXCHANGE_CertificationCallback cb;
+ struct TALER_EXCHANGE_Keys kd;
+ struct TALER_EXCHANGE_Keys kd_old;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received keys from URL `%s' with status %ld.\n",
kr->url,
response_code);
- switch (response_code) {
+ kd_old = exchange->key_data;
+ switch (response_code)
+ {
case 0:
break;
case MHD_HTTP_OK:
@@ -635,11 +702,14 @@ keys_completed_cb (void *cls,
break;
}
if (GNUNET_OK !=
- decode_keys_json (resp_obj, &kr->exchange->key_data))
+ decode_keys_json (resp_obj,
+ &kd))
{
response_code = 0;
break;
}
+ exchange->key_data = kd;
+ json_decref (exchange->key_data_raw);
exchange->key_data_raw = json_deep_copy (resp_obj);
break;
default:
@@ -655,24 +725,25 @@ keys_completed_cb (void *cls,
free_keys_request (kr);
exchange->state = MHS_FAILED;
/* notify application that we failed */
- if (NULL != (cb = exchange->cert_cb))
- {
- exchange->cert_cb = NULL;
- cb (exchange->cert_cb_cls,
- NULL);
- }
+ exchange->cert_cb (exchange->cert_cb_cls,
+ NULL);
+ if (NULL != exchange->key_data_raw)
+ {
+ json_decref (exchange->key_data_raw);
+ exchange->key_data_raw = NULL;
+ }
+ free_key_data (&kd_old);
return;
}
+
exchange->kr = NULL;
+ exchange->key_data_expiration = kr->expire;
free_keys_request (kr);
exchange->state = MHS_CERT;
/* notify application about the key information */
- if (NULL != (cb = exchange->cert_cb))
- {
- exchange->cert_cb = NULL;
- cb (exchange->cert_cb_cls,
- &exchange->key_data);
- }
+ exchange->cert_cb (exchange->cert_cb_cls,
+ &exchange->key_data);
+ free_key_data (&kd_old);
}
@@ -730,6 +801,108 @@ MAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
}
+/**
+ * Parse HTTP timestamp.
+ *
+ * @param date header to parse header
+ * @param at where to write the result
+ * @return #GNUNET_OK on success
+ */
+static int
+parse_date_string (const char *date,
+ struct GNUNET_TIME_Absolute *at)
+{
+ static const char *const days[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char *const mons[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ struct tm now;
+ time_t t;
+ char day[3];
+ char mon[3];
+ unsigned int i;
+ unsigned int mday;
+ unsigned int year;
+ unsigned int h;
+ unsigned int m;
+ unsigned int s;
+
+ if (7 != sscanf (date,
+ "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
+ day,
+ &mday,
+ mon,
+ &year,
+ &h,
+ &m,
+ &s))
+ return GNUNET_SYSERR;
+ memset (&now, 0, sizeof (now));
+ now.tm_year = year - 1900;
+ now.tm_mday = mday;
+ now.tm_hour = h;
+ now.tm_min = m;
+ now.tm_sec = s;
+ now.tm_wday = 7;
+ for (i=0;i<7;i++)
+ if (0 == strcasecmp (days[i], day))
+ now.tm_wday = i;
+ now.tm_mon = 12;
+ for (i=0;i<12;i++)
+ if (0 == strcasecmp (mons[i], mon))
+ now.tm_mon = i;
+ if ( (7 == now.tm_mday) ||
+ (12 == now.tm_mon) )
+ return GNUNET_SYSERR;
+ t = mktime (&now);
+ at->abs_value_us = 1000LL * 1000LL * 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 fo 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 KeysRequest`
+ * @return `size * nitems` on success (everything else aborts)
+ */
+static size_t
+header_cb (char *buffer,
+ size_t size,
+ size_t nitems,
+ void *userdata)
+{
+ struct KeysRequest *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 ": "));
+ 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);
+ }
+ GNUNET_free (val);
+ return total;
+}
+
/* ********************* public API ******************* */
/**
@@ -755,40 +928,62 @@ TALER_EXCHANGE_connect (struct GNUNET_CURL_Context *ctx,
...)
{
struct TALER_EXCHANGE_Handle *exchange;
- struct KeysRequest *kr;
- CURL *c;
exchange = GNUNET_new (struct TALER_EXCHANGE_Handle);
exchange->ctx = ctx;
exchange->url = GNUNET_strdup (url);
exchange->cert_cb = cert_cb;
exchange->cert_cb_cls = cert_cb_cls;
+ request_keys (exchange);
+ return exchange;
+}
+
+
+/**
+ * Initiate download of /keys from the exchange.
+ *
+ * @param exchange where to download /keys from
+ */
+static void
+request_keys (struct TALER_EXCHANGE_Handle *exchange)
+{
+ struct KeysRequest *kr;
+ CURL *eh;
+
+ GNUNET_assert (NULL == exchange->kr);
kr = GNUNET_new (struct KeysRequest);
kr->exchange = exchange;
kr->url = MAH_path_to_url (exchange, "/keys");
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Requesting keys with URL `%s'.\n",
kr->url);
- c = curl_easy_init ();
+ eh = curl_easy_init ();
GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (c,
+ curl_easy_setopt (eh,
CURLOPT_VERBOSE,
0));
GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (c,
- CURLOPT_STDERR,
- stdout));
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT,
+ (long) 300));
GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (c,
+ curl_easy_setopt (eh,
+ CURLOPT_HEADERFUNCTION,
+ &header_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_HEADERDATA,
+ kr));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
CURLOPT_URL,
kr->url));
kr->job = GNUNET_CURL_job_add (exchange->ctx,
- c,
+ eh,
GNUNET_NO,
&keys_completed_cb,
kr);
exchange->kr = kr;
- return exchange;
}
@@ -800,26 +995,18 @@ TALER_EXCHANGE_connect (struct GNUNET_CURL_Context *ctx,
void
TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange)
{
- unsigned int i;
-
if (NULL != exchange->kr)
{
GNUNET_CURL_job_cancel (exchange->kr->job);
free_keys_request (exchange->kr);
exchange->kr = NULL;
}
- GNUNET_array_grow (exchange->key_data.sign_keys,
- exchange->key_data.num_sign_keys,
- 0);
- for (i=0;i<exchange->key_data.num_denom_keys;i++)
- GNUNET_CRYPTO_rsa_public_key_free (exchange->key_data.denom_keys[i].key.rsa_public_key);
- GNUNET_array_grow (exchange->key_data.denom_keys,
- exchange->key_data.num_denom_keys,
- 0);
- GNUNET_array_grow (exchange->key_data.auditors,
- exchange->key_data.num_auditors,
- 0);
- json_decref (exchange->key_data_raw);
+ free_key_data (&exchange->key_data);
+ if (NULL != exchange->key_data_raw)
+ {
+ json_decref (exchange->key_data_raw);
+ exchange->key_data_raw = NULL;
+ }
GNUNET_free (exchange->url);
GNUNET_free (exchange);
}
@@ -863,7 +1050,7 @@ TALER_EXCHANGE_test_signing_key (const struct TALER_EXCHANGE_Keys *keys,
*/
const struct TALER_EXCHANGE_DenomPublicKey *
TALER_EXCHANGE_get_denomination_key (const struct TALER_EXCHANGE_Keys *keys,
- const struct TALER_DenominationPublicKey *pk)
+ const struct TALER_DenominationPublicKey *pk)
{
unsigned int i;
@@ -884,7 +1071,7 @@ TALER_EXCHANGE_get_denomination_key (const struct TALER_EXCHANGE_Keys *keys,
*/
const struct TALER_EXCHANGE_DenomPublicKey *
TALER_EXCHANGE_get_denomination_key_by_hash (const struct TALER_EXCHANGE_Keys *keys,
- const struct GNUNET_HashCode *hc)
+ const struct GNUNET_HashCode *hc)
{
unsigned int i;
@@ -904,8 +1091,9 @@ TALER_EXCHANGE_get_denomination_key_by_hash (const struct TALER_EXCHANGE_Keys *k
* @return the exchange's key set
*/
const struct TALER_EXCHANGE_Keys *
-TALER_EXCHANGE_get_keys (const struct TALER_EXCHANGE_Handle *exchange)
+TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange)
{
+ (void) TALER_EXCHANGE_check_keys_current (exchange);
return &exchange->key_data;
}
@@ -918,8 +1106,9 @@ TALER_EXCHANGE_get_keys (const struct TALER_EXCHANGE_Handle *exchange)
* @return the exchange's keys in raw JSON
*/
json_t *
-TALER_EXCHANGE_get_keys_raw (const struct TALER_EXCHANGE_Handle *exchange)
+TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange)
{
+ (void) TALER_EXCHANGE_check_keys_current (exchange);
return json_deep_copy (exchange->key_data_raw);
}
diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c
index 3649bf436..e4fdd2b46 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -89,6 +89,11 @@ struct TMH_KS_StateHandle
struct GNUNET_TIME_Absolute next_reload;
/**
+ * When does the first active denomination key expire (for deposit)?
+ */
+ struct GNUNET_TIME_Absolute min_dk_expire;
+
+ /**
* Exchange signing key that should be used currently.
*/
struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP current_sign_key_issue;
@@ -217,6 +222,7 @@ reload_keys_denom_iter (void *cls,
struct TMH_KS_StateHandle *ctx = cls;
struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Absolute horizon;
+ struct GNUNET_TIME_Absolute expire_deposit;
struct GNUNET_HashCode denom_key_hash;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *d2;
struct TALER_EXCHANGEDB_Session *session;
@@ -235,8 +241,8 @@ reload_keys_denom_iter (void *cls,
return GNUNET_OK;
}
now = GNUNET_TIME_absolute_get ();
- if (GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_deposit).abs_value_us <
- now.abs_value_us)
+ expire_deposit = GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_deposit);
+ if (expire_deposit.abs_value_us < now.abs_value_us)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Skipping expired denomination key `%s'\n",
@@ -339,6 +345,8 @@ reload_keys_denom_iter (void *cls,
GNUNET_free (d2);
return GNUNET_OK;
}
+ ctx->min_dk_expire = GNUNET_TIME_absolute_min (ctx->min_dk_expire,
+ expire_deposit);
json_array_append_new (ctx->denom_keys_array,
denom_key_issue_to_json (&dki->denom_pub,
&dki->issue));
@@ -643,7 +651,7 @@ TMH_KS_acquire_ (const char *location)
{
key_state = GNUNET_new (struct TMH_KS_StateHandle);
key_state->hash_context = GNUNET_CRYPTO_hash_context_start ();
-
+ key_state->min_dk_expire = GNUNET_TIME_UNIT_FOREVER_ABS;
key_state->denom_keys_array = json_array ();
GNUNET_assert (NULL != key_state->denom_keys_array);
@@ -680,7 +688,6 @@ TMH_KS_acquire_ (const char *location)
return NULL;
}
-
ks.purpose.size = htonl (sizeof (ks));
ks.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET);
ks.list_issue_date = GNUNET_TIME_absolute_hton (key_state->reload_time);
@@ -691,7 +698,9 @@ TMH_KS_acquire_ (const char *location)
GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv.eddsa_priv,
&ks.purpose,
&sig.eddsa_signature));
- key_state->next_reload = GNUNET_TIME_absolute_ntoh (key_state->current_sign_key_issue.issue.expire);
+ key_state->next_reload =
+ GNUNET_TIME_absolute_min (GNUNET_TIME_absolute_ntoh (key_state->current_sign_key_issue.issue.expire),
+ key_state->min_dk_expire);
if (0 == key_state->next_reload.abs_value_us)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"No valid signing key found!\n");
@@ -1002,6 +1011,58 @@ TMH_KS_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
/**
+ * Produce HTTP "Date:" header.
+ *
+ * @param at time to write to @a date
+ * @param[out] date where to write the header, with
+ * at least 128 bytes available space.
+ */
+static void
+get_date_string (struct GNUNET_TIME_Absolute at,
+ char *date)
+{
+ static const char *const days[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char *const mons[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec"
+ };
+ struct tm now;
+ time_t t;
+#if !defined(HAVE_C11_GMTIME_S) && !defined(HAVE_W32_GMTIME_S) && !defined(HAVE_GMTIME_R)
+ struct tm* pNow;
+#endif
+
+ date[0] = 0;
+ t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
+#if defined(HAVE_C11_GMTIME_S)
+ if (NULL == gmtime_s (&t, &now))
+ return;
+#elif defined(HAVE_W32_GMTIME_S)
+ if (0 != gmtime_s (&now, &t))
+ return;
+#elif defined(HAVE_GMTIME_R)
+ if (NULL == gmtime_r(&t, &now))
+ return;
+#else
+ pNow = gmtime(&t);
+ if (NULL == pNow)
+ return;
+ now = *pNow;
+#endif
+ sprintf (date,
+ "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
+ days[now.tm_wday % 7],
+ (unsigned int) now.tm_mday,
+ mons[now.tm_mon % 12],
+ (unsigned int) (1900 + now.tm_year),
+ (unsigned int) now.tm_hour,
+ (unsigned int) now.tm_min,
+ (unsigned int) now.tm_sec);
+}
+
+
+/**
* Function to call to handle the request by sending
* back static data from the @a rh.
*
@@ -1022,6 +1083,7 @@ TMH_KS_handler_keys (struct TMH_RequestHandler *rh,
struct TMH_KS_StateHandle *key_state;
struct MHD_Response *response;
int ret;
+ char dat[128];
key_state = TMH_KS_acquire ();
response = MHD_create_response_from_buffer (strlen (key_state->keys_json),
@@ -1034,9 +1096,22 @@ TMH_KS_handler_keys (struct TMH_RequestHandler *rh,
return MHD_NO;
}
TMH_RESPONSE_add_global_headers (response);
- (void) MHD_add_response_header (response,
- "Content-Type",
- rh->mime_type);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type));
+ get_date_string (key_state->reload_time,
+ dat);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_LAST_MODIFIED,
+ dat));
+ get_date_string (key_state->next_reload,
+ dat);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_EXPIRES,
+ dat));
ret = MHD_queue_response (connection,
rh->response_code,
response);
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index aec2ac27b..d6d3d3f9d 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -40,9 +40,10 @@ void
TMH_RESPONSE_add_global_headers (struct MHD_Response *response)
{
if (TMH_exchange_connection_close)
- (void) MHD_add_response_header (response,
- MHD_HTTP_HEADER_CONNECTION,
- "close");
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "close"));
}
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 72f85e7bb..5fbaa26b3 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -4298,6 +4298,12 @@ postgres_gc (void *cls)
conn = connect_to_postgres (pc);
if (NULL == conn)
return GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ postgres_prepare (conn))
+ {
+ PQfinish (conn);
+ return GNUNET_SYSERR;
+ }
result = GNUNET_PQ_exec_prepared (conn,
"gc_prewire",
params_none);
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 2dee9a7ee..b42fb30f3 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -202,11 +202,17 @@ destroy_denom_key_pair (struct DenomKeyPair *dkp)
*
* @param size the size of the denomination key
* @param session the DB session
+ * @param now time to use for key generation, legal expiration will be 3h later.
+ * @param fee_withdraw withdraw fee to use
+ * @param fee_deposit deposit fee to use
+ * @param fee_refresh refresh fee to use
+ * @param fee_refund refund fee to use
* @return the denominaiton key pair; NULL upon error
*/
static struct DenomKeyPair *
create_denom_key_pair (unsigned int size,
struct TALER_EXCHANGEDB_Session *session,
+ struct GNUNET_TIME_Absolute now,
const struct TALER_Amount *value,
const struct TALER_Amount *fee_withdraw,
const struct TALER_Amount *fee_deposit,
@@ -216,7 +222,6 @@ create_denom_key_pair (unsigned int size,
struct DenomKeyPair *dkp;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation dki;
struct TALER_EXCHANGEDB_DenominationKeyInformationP issue2;
- struct GNUNET_TIME_Absolute now;
dkp = GNUNET_new (struct DenomKeyPair);
dkp->priv.rsa_private_key = GNUNET_CRYPTO_rsa_private_key_create (size);
@@ -230,7 +235,6 @@ create_denom_key_pair (unsigned int size,
0,
sizeof (struct TALER_EXCHANGEDB_DenominationKeyIssueInformation));
dki.denom_pub = dkp->pub;
- now = GNUNET_TIME_absolute_get ();
GNUNET_TIME_round_abs (&now);
dki.issue.properties.start = GNUNET_TIME_absolute_hton (now);
dki.issue.properties.expire_withdraw = GNUNET_TIME_absolute_hton
@@ -558,7 +562,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
struct TALER_EXCHANGEDB_LinkDataList *ldl;
struct TALER_EXCHANGEDB_LinkDataList *ldlp;
struct TALER_DenominationSignature ev_sigs[MELT_NEW_COINS];
- unsigned int cnt;
+ unsigned int cnt;
unsigned int i;
int ret;
@@ -575,6 +579,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
/* create a denomination (value: 1; fraction: 100) */
dkp = create_denom_key_pair (512,
session,
+ GNUNET_TIME_absolute_get (),
&value,
&fee_withdraw,
&fee_deposit,
@@ -645,6 +650,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
{
new_dkp[cnt] = create_denom_key_pair (1024,
session,
+ GNUNET_TIME_absolute_get (),
&value,
&fee_withdraw,
&fee_deposit,
@@ -975,6 +981,54 @@ deposit_cb (void *cls,
/**
+ * Test garbage collection.
+ *
+ * @param session DB session to use
+ * @return #GNUNET_OK on success
+ */
+static int
+test_gc (struct TALER_EXCHANGEDB_Session *session)
+{
+ struct DenomKeyPair *dkp;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Absolute past;
+ struct TALER_EXCHANGEDB_DenominationKeyInformationP issue2;
+
+ now = GNUNET_TIME_absolute_get ();
+ past = GNUNET_TIME_absolute_subtract (now,
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
+ 4));
+ dkp = create_denom_key_pair (1024,
+ session,
+ past,
+ &value,
+ &fee_withdraw,
+ &fee_deposit,
+ &fee_refresh,
+ &fee_refund);
+ if (GNUNET_OK !=
+ plugin->gc (plugin->cls))
+ {
+ GNUNET_break(0);
+ destroy_denom_key_pair (dkp);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK ==
+ plugin->get_denomination_info (plugin->cls,
+ session,
+ &dkp->pub,
+ &issue2))
+ {
+ GNUNET_break(0);
+ destroy_denom_key_pair (dkp);
+ return GNUNET_SYSERR;
+ }
+ destroy_denom_key_pair (dkp);
+ return GNUNET_OK;
+}
+
+
+/**
* Main function that will be run by the scheduler.
*
* @param cls closure with config
@@ -1093,7 +1147,9 @@ run (void *cls)
value.fraction * 2,
value.currency));
result = 5;
- dkp = create_denom_key_pair (1024, session,
+ dkp = create_denom_key_pair (1024,
+ session,
+ GNUNET_TIME_absolute_get (),
&value,
&fee_withdraw,
&fee_deposit,
@@ -1427,6 +1483,9 @@ run (void *cls)
transaction_id_wt,
&cb_wtid_check,
&cb_wtid_never));
+ FAILIF (GNUNET_OK !=
+ test_gc (session));
+
result = 0;
drop:
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 1da42b897..e65a9faee 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -220,7 +220,7 @@ struct TALER_EXCHANGE_Keys
*
* @param cls closure
* @param keys information about the various keys used
- * by the exchange
+ * by the exchange, NULL if /keys failed
*/
typedef void
(*TALER_EXCHANGE_CertificationCallback) (void *cls,
@@ -244,7 +244,8 @@ struct TALER_EXCHANGE_Handle;
*
* @param ctx the context
* @param url HTTP base URL for the exchange
- * @param cert_cb function to call with the exchange's certification information
+ * @param cert_cb function to call with the exchange's certification information,
+ * possibly called repeatedly if the information changes
* @param cert_cb_cls closure for @a cert_cb
* @param ... list of additional arguments, terminated by #TALER_EXCHANGE_OPTION_END.
* @return the exchange handle; NULL upon error
@@ -273,18 +274,28 @@ TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange);
* @return the exchange's key set
*/
const struct TALER_EXCHANGE_Keys *
-TALER_EXCHANGE_get_keys (const struct TALER_EXCHANGE_Handle *exchange);
+TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange);
/**
- * Obtain the keys from the exchange in the
- * raw JSON format
+ * Check if our current response for /keys is valid, and if
+ * not, trigger /keys download.
+ *
+ * @param exchange exchange to check keys for
+ * @return until when the response is current, 0 if we are re-downloading
+ */
+struct GNUNET_TIME_Absolute
+TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange);
+
+
+/**
+ * Obtain the keys from the exchange in the raw JSON format.
*
* @param exchange the exchange handle
* @return the exchange's keys in raw JSON
*/
json_t *
-TALER_EXCHANGE_get_keys_raw (const struct TALER_EXCHANGE_Handle *exchange);
+TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange);
/**