commit 86cab216c4a68dfc5be8dfaea7e1c81d9c110201
parent af85f31619b4be1eed5f0575f13d26d81db37e1f
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Wed, 23 Jul 2025 18:49:56 +0200
update donaukeyupdate to match exchangekeyupdate
Diffstat:
9 files changed, 76 insertions(+), 99 deletions(-)
diff --git a/src/backend/taler-merchant-donaukeyupdate.c b/src/backend/taler-merchant-donaukeyupdate.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2024 Taler Systems SA
+ Copyright (C) 2024, 2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -19,6 +19,7 @@
* @author Bohdan Potuzhnyi
*/
#include "platform.h"
+#include "microhttpd.h"
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <pthread.h>
@@ -107,6 +108,12 @@ struct Donau
* If true, the instance is temporarily paused from making further requests due to reaching a limit.
*/
bool limited;
+
+ /**
+ * Are we force-retrying a /keys download because some keys
+ * were missing?
+ */
+ bool force_retry;
};
@@ -220,14 +227,19 @@ end_inquiry (void)
* Update Donau keys in the database.
*
* @param keys Donau keys to persist
+ * @param first_retry earliest we may retry fetching the keys
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
-insert_donau_keys_data (const struct DONAU_Keys *keys)
+insert_donau_keys_data (const struct DONAU_Keys *keys,
+ struct GNUNET_TIME_Absolute first_retry)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Inserting Donau keys into the database %s\n", keys->donau_url);
- return db_plugin->upsert_donau_keys (db_plugin->cls, keys);
+ "Inserting Donau keys into the database %s\n",
+ keys->donau_url);
+ return db_plugin->upsert_donau_keys (db_plugin->cls,
+ keys,
+ first_retry);
}
@@ -235,10 +247,12 @@ insert_donau_keys_data (const struct DONAU_Keys *keys)
* Store Donau keys in the database and handle retries.
*
* @param keys the keys to store
+ * @param first_retry earliest time we may retry fetching the keys
* @return true on success
*/
static bool
-store_donau_keys (struct DONAU_Keys *keys)
+store_donau_keys (struct DONAU_Keys *keys,
+ struct GNUNET_TIME_Absolute first_retry)
{
enum GNUNET_DB_QueryStatus qs;
@@ -254,8 +268,9 @@ store_donau_keys (struct DONAU_Keys *keys)
return false;
}
- qs = insert_donau_keys_data (keys);
- if (qs < 0)
+ qs = insert_donau_keys_data (keys,
+ first_retry);
+ if (0 > qs)
{
db_plugin->rollback (db_plugin->cls);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -268,7 +283,7 @@ store_donau_keys (struct DONAU_Keys *keys)
}
qs = db_plugin->commit (db_plugin->cls);
- if (qs < 0)
+ if (0 > qs)
{
db_plugin->rollback (db_plugin->cls);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -278,6 +293,7 @@ store_donau_keys (struct DONAU_Keys *keys)
GNUNET_break (0);
return false;
}
+ break;
}
if (qs < 0)
{
@@ -303,6 +319,7 @@ donau_cert_cb (
{
struct Donau *d = cls;
struct GNUNET_TIME_Absolute n;
+ struct GNUNET_TIME_Absolute first_retry;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Starting donau cert with object \n");
@@ -311,23 +328,27 @@ donau_cert_cb (
switch (kr->hr.http_status)
{
case MHD_HTTP_OK:
- if (! store_donau_keys (keys))
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Got new keys for %s, updating database\n",
+ d->donau_url);
+ first_retry = GNUNET_TIME_relative_to_absolute (DONAU_MAXFREQ);
+ if (! store_donau_keys (keys,
+ first_retry))
+ {
+ GNUNET_break (0);
+ DONAU_keys_decref (keys);
break;
+ }
- d->keys = DONAU_keys_incref (keys);
+ d->keys = keys;
/* Reset back-off */
d->retry_delay = DONAU_MAXFREQ;
/* limit retry */
- d->first_retry =
- GNUNET_TIME_relative_to_absolute (
- DONAU_MAXFREQ);
+ d->first_retry = first_retry;
- // Fixme: hardcoded retry delay
+ /* Might be good to reference some key_data_expiration and not first sign_key*/
n = GNUNET_TIME_absolute_max (d->first_retry,
- GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_relative_multiply (
- GNUNET_TIME_UNIT_MONTHS,
- 3)));
+ keys->sign_keys[0].expire_sign.abs_time);
if (NULL != d->retry_task)
GNUNET_SCHEDULER_cancel (d->retry_task);
@@ -338,6 +359,7 @@ donau_cert_cb (
end_inquiry ();
return;
default:
+ GNUNET_break (NULL == keys);
break;
}
@@ -380,11 +402,15 @@ download_keys (void *cls)
}
d->retry_delay
= GNUNET_TIME_STD_BACKOFF (d->retry_delay);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Downloading keys from %s (%s)\n",
+ d->donau_url,
+ d->force_retry ? "forced" : "regular");
d->conn = DONAU_get_keys (ctx,
d->donau_url,
&donau_cert_cb,
d);
-
+ d->force_retry = false;
if (NULL != d->conn)
{
active_inquiries++;
@@ -465,22 +491,15 @@ force_donau_keys (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Donau instance `%s' not found. Creating new instance.\n", url);
- // Create a new Donau instance dynamically
d = GNUNET_new (struct Donau);
d->donau_url = GNUNET_strdup (url);
- d->keys = NULL;
- d->conn = NULL;
- d->retry_task = NULL;
d->retry_delay = DONAU_MAXFREQ;
d->first_retry = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_ZERO);
- d->limited = false;
- // Insert the new instance into the linked list
GNUNET_CONTAINER_DLL_insert (d_head, d_tail, d);
-
- // Schedule a keys download for the newly created instance
download_keys (d);
}
+
if (NULL != d->conn)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -497,7 +516,7 @@ force_donau_keys (void *cls,
true));
if (NULL != d->retry_task)
GNUNET_SCHEDULER_cancel (d->retry_task);
-
+ d->force_retry = true;
d->retry_task
= GNUNET_SCHEDULER_add_at (d->first_retry,
&download_keys,
@@ -518,8 +537,6 @@ accept_donau (void *cls,
{
char *url;
char *currency;
- // char *mks;
-
if (0 !=
strncasecmp (section,
"merchant-donau-",
@@ -576,57 +593,21 @@ accept_donau (void *cls,
GNUNET_SCHEDULER_shutdown ();
return;
}
-// if (GNUNET_OK !=
-// GNUNET_CONFIGURATION_get_value_string (cfg,
-// section,
-// "MASTER_KEY",
-// &mks))
-// {
-// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-// section,
-// "MASTER_KEY");
-// global_ret = EXIT_NOTCONFIGURED;
-// GNUNET_SCHEDULER_shutdown ();
-// GNUNET_free (currency);
-// GNUNET_free (url);
-// return;
-// }
-
-
{
struct Donau *d;
-
d = GNUNET_new (struct Donau);
d->donau_url = url;
GNUNET_CONTAINER_DLL_insert (d_head,
d_tail,
d);
-// if(GNUNET_OK !=
-// GNUNET_CRYPTO_eddsa_public_key_from_string (
-// mks,
-// strlen(mks),
-// &d->master_pub.eddsa_pub))
-// {
-// GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-// section,
-// "MASTER_KEY",
-// "malformed EdDSA key");
-// global_ret = EXIT_NOTCONFIGURED;
-// GNUNET_SCHEDULER_shutdown ();
-// GNUNET_free (mks);
-// return;
-// }
-// GNUNET_free (mks);
-
{
enum GNUNET_DB_QueryStatus qs;
struct DONAU_Keys *keys;
-
qs = db_plugin->lookup_donau_keys (db_plugin->cls,
url,
+ &d->first_retry,
&keys);
-
- if (qs < 0)
+ if (0 > qs)
{
GNUNET_break (0);
global_ret = EXIT_FAILURE;
@@ -645,32 +626,18 @@ accept_donau (void *cls,
DONAU_keys_decref (keys);
keys = NULL;
}
-// if ( (NULL != keys) &&
-// (0 != GNUNET_memcmp (&d->master_pub,
-// &keys->master_pub)) )
-// {
-// /* master pub differs => fetch keys again */
-// GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-// "Master public key of donau `%s' differs from our configuration. Fetching /keys again.\n",
-// d->donau_url);
-// DONAU_keys_decref (keys);
-// keys = NULL;
-// }
+
d->keys = keys;
if (NULL == keys)
{
/* done synchronously so that the active_inquiries
is updated immediately */
-
download_keys (d);
}
else
{
d->retry_task
- = GNUNET_SCHEDULER_add_at (GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_relative_multiply (
- GNUNET_TIME_UNIT_MONTHS,
- 3)),
+ = GNUNET_SCHEDULER_add_at (keys->sign_keys[0].expire_sign.abs_time,
&download_keys,
d);
}
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -734,7 +734,7 @@ struct PayContext
#ifdef HAVE_DONAU_DONAU_SERVICE_H
/**
- * Struct for #phase_generate_donation_receipt
+ * Struct for #phase_generate_donation_receipt()
*/
struct
{
@@ -1918,9 +1918,6 @@ merchant_donau_issue_receipt_cb (void *cls,
/* Donau replies asynchronously, so we expect the PayContext
* to be suspended. */
GNUNET_assert (GNUNET_YES == pc->suspended);
-
- /* REVIEW NULL == resp in donau testing*/
-
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Donau responded with status=%u, ec=%u\n",
resp->hr.http_status,
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -3221,6 +3221,8 @@ phase_merge_inventory (struct OrderContext *oc)
}
+/* ***************** ORDER_PHASE_PARSE_CHOICES **************** */
+
#ifdef HAVE_DONAU_DONAU_SERVICE_H
/**
* Callback function that is called for each donau instance.
@@ -3272,9 +3274,6 @@ add_donau_output (struct OrderContext *oc,
#endif
-
-/* ***************** ORDER_PHASE_PARSE_CHOICES **************** */
-
/**
* Parse contract choices. Upon success, continue
* processing with merge_inventory().
diff --git a/src/backenddb/merchant-0022.sql b/src/backenddb/merchant-0022.sql
@@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS merchant_donau_keys
(donau_keys_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
,donau_url TEXT PRIMARY KEY
,keys_json TEXT NOT NULL
+ ,first_retry INT8 NOT NULL DEFAULT (0)
);
COMMENT ON TABLE merchant_donau_keys
@@ -40,6 +41,8 @@ COMMENT ON COLUMN merchant_donau_keys.donau_url
IS 'Base URL of Donau associated with these keys';
COMMENT ON COLUMN merchant_donau_keys.keys_json
IS 'JSON string of the /keys as generated by Donau';
+COMMENT ON COLUMN merchant_donau_keys.first_retry
+ IS 'Absolute time when this merchant may retry to fetch the keys from this donau at the earliest';
CREATE TABLE IF NOT EXISTS merchant_donau_instances
(donau_instances_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
diff --git a/src/backenddb/pg_lookup_donau_keys.c b/src/backenddb/pg_lookup_donau_keys.c
@@ -30,6 +30,7 @@
enum GNUNET_DB_QueryStatus
TMH_PG_lookup_donau_keys (void *cls,
const char *donau_url,
+ struct GNUNET_TIME_Absolute *first_retry,
struct DONAU_Keys **keys)
{
struct PostgresClosure *pg = cls;
@@ -39,6 +40,8 @@ TMH_PG_lookup_donau_keys (void *cls,
};
json_t *jkeys;
struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_absolute_time ("first_retry",
+ first_retry),
TALER_PQ_result_spec_json ("keys_json",
&jkeys),
GNUNET_PQ_result_spec_end
@@ -49,7 +52,8 @@ TMH_PG_lookup_donau_keys (void *cls,
PREPARE (pg,
"lookup_donau_keys",
"SELECT"
- " keys_json"
+ " first_retry"
+ ",keys_json"
" FROM merchant_donau_keys"
" WHERE donau_url=$1;");
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
diff --git a/src/backenddb/pg_lookup_donau_keys.h b/src/backenddb/pg_lookup_donau_keys.h
@@ -38,6 +38,7 @@
enum GNUNET_DB_QueryStatus
TMH_PG_lookup_donau_keys (void *cls,
const char *donau_url,
+ struct GNUNET_TIME_Absolute *first_retry,
struct DONAU_Keys **keys);
diff --git a/src/backenddb/pg_upsert_donau_keys.c b/src/backenddb/pg_upsert_donau_keys.c
@@ -30,7 +30,8 @@
enum GNUNET_DB_QueryStatus
TMH_PG_upsert_donau_keys (
void *cls,
- const struct DONAU_Keys *keys)
+ const struct DONAU_Keys *keys,
+ struct GNUNET_TIME_Absolute first_retry)
{
enum GNUNET_DB_QueryStatus qs;
struct PostgresClosure *pg = cls;
@@ -42,6 +43,7 @@ TMH_PG_upsert_donau_keys (
{
struct GNUNET_PQ_QueryParam params[] = {
TALER_PQ_query_param_json (jkeys),
+ GNUNET_PQ_query_param_absolute_time (&first_retry),
GNUNET_PQ_query_param_string (keys->donau_url),
GNUNET_PQ_query_param_end
};
@@ -51,15 +53,16 @@ TMH_PG_upsert_donau_keys (
"insert_donau_keys",
"INSERT INTO merchant_donau_keys"
"(keys_json"
+ ",first_retry"
",donau_url"
- ") VALUES ($1, $2);");
+ ") VALUES ($1, $2, $3);");
PREPARE (pg,
"update_donau_keys",
"UPDATE merchant_donau_keys SET"
- " keys_json=$1"
+ " keys_json=$1,"
+ " first_retry=$2"
" WHERE"
- " donau_url=$2;");
-
+ " donau_url=$3;");
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_donau_keys",
params);
diff --git a/src/backenddb/pg_upsert_donau_keys.h b/src/backenddb/pg_upsert_donau_keys.h
@@ -36,7 +36,8 @@
*/
enum GNUNET_DB_QueryStatus
TMH_PG_upsert_donau_keys (void *cls,
- const struct DONAU_Keys *keys);
+ const struct DONAU_Keys *keys,
+ struct GNUNET_TIME_Absolute first_retry);
#endif
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
@@ -4104,7 +4104,8 @@ struct TALER_MERCHANTDB_Plugin
enum GNUNET_DB_QueryStatus
(*upsert_donau_keys)(
void *cls,
- const struct DONAU_Keys *keys
+ const struct DONAU_Keys *keys,
+ struct GNUNET_TIME_Absolute first_retry
);
/**
@@ -4118,6 +4119,7 @@ struct TALER_MERCHANTDB_Plugin
(*lookup_donau_keys)(
void *cls,
const char *donau_url,
+ struct GNUNET_TIME_Absolute *first_retry,
struct DONAU_Keys **keys
);