commit 7192d66b632be5d1732fa121baa5f79483ad2721
parent b9f0772bb43ee9c15cc70bd145c8a5167d083a03
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Thu, 28 Aug 2025 07:05:01 +0200
try to fix #10194 (need to re-run test)
Diffstat:
11 files changed, 139 insertions(+), 36 deletions(-)
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -1025,7 +1025,8 @@ full_url_track_callback (void *cls,
* @param authn_s the value of the authorization header
*/
static void
-process_basic_auth (struct TMH_HandlerContext *hc, const char*authn_s)
+process_basic_auth (struct TMH_HandlerContext *hc,
+ const char *authn_s)
{
/* Handle token endpoint slightly differently: Only allow
* instance password (Basic auth) to retrieve access token.
diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c
@@ -680,7 +680,7 @@ TMH_EXCHANGES_lookup_wire_fee (
}
-enum GNUNET_GenericReturnValue
+enum TMH_ExchangeStatus
TMH_exchange_check_debit (
const char *instance_id,
const struct TMH_Exchange *exchange,
@@ -690,9 +690,13 @@ TMH_exchange_check_debit (
const struct TALER_EXCHANGE_Keys *keys = exchange->keys;
bool have_kyc = false;
bool no_access_token = true;
+ enum TMH_ExchangeStatus retry_ok = 0;
+
+ if (GNUNET_TIME_absolute_is_past (exchange->first_retry))
+ retry_ok = TMH_ES_RETRY_OK;
if (NULL == keys)
- return GNUNET_SYSERR;
+ return TMH_ES_NO_KEYS | retry_ok;
if (0 != strcasecmp (keys->currency,
max_amount->currency))
{
@@ -701,7 +705,7 @@ TMH_exchange_check_debit (
exchange->url,
keys->currency,
max_amount->currency);
- return GNUNET_SYSERR;
+ return TMH_ES_NO_CURR | retry_ok;
}
{
struct TALER_NormalizedPayto np;
@@ -713,10 +717,10 @@ TMH_exchange_check_debit (
np);
GNUNET_free (np.normalized_payto);
if (! account_ok)
- return GNUNET_NO;
+ return TMH_ES_NO_ACC | retry_ok;
}
if (! keys->kyc_enabled)
- return GNUNET_YES;
+ return TMH_ES_OK | retry_ok;
{
json_t *jlimits = NULL;
@@ -796,7 +800,7 @@ TMH_exchange_check_debit (
TALER_amount_min (max_amount,
max_amount,
&kyc_limit);
- return GNUNET_YES;
+ return TMH_ES_OK | retry_ok;
} /* END of if qs > 0, NULL != jlimits */
}
@@ -819,7 +823,7 @@ TMH_exchange_check_debit (
TALER_amount_set_zero (
max_amount->currency,
max_amount));
- return GNUNET_YES;
+ return TMH_ES_OK | retry_ok;
}
/* In any case, abide by hard limits (unless we have custom rules). */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -846,7 +850,7 @@ TMH_exchange_check_debit (
TALER_amount_set_zero (max_amount->currency,
max_amount));
}
- return GNUNET_YES;
+ return TMH_ES_OK | retry_ok;
}
diff --git a/src/backend/taler-merchant-httpd_exchanges.h b/src/backend/taler-merchant-httpd_exchanges.h
@@ -210,9 +210,27 @@ TMH_EXCHANGES_lookup_wire_fee (
* exchange; input is an existing maximum, that
* can be lowered by this function due to transaction
* limits and deposit limits of the exchange
- * @return #GNUNET_OK if such a debit can happen
+ * @return #TMH_ES_OK if such a debit can happen
+ * #TMH_ES_NO_ACC if the exchange cannot
+ * be used due to account restrictions
+ * #TMH_ES_NO_CURR if the exchange cannot be used
+ * because it is for a different currency
+ * #TMH_ES_NO_KEYS if the exchange cannot be used
+ * because we do not even know its keys
+ * #TMH_ES_RETRY_OK if the exchange keys request
+ * could be retried (OR-bit!)
*/
-enum GNUNET_GenericReturnValue
+enum TMH_ExchangeStatus
+{
+ TMH_ES_OK = 0,
+ TMH_ES_NO_ACC = 1,
+ TMH_ES_NO_CURR = 2,
+ TMH_ES_NO_KEYS = 3,
+ TMH_ES_RETRY_OK = 16,
+ TMH_ES_NO_ACC_RETRY_OK = TMH_ES_NO_ACC | TMH_ES_RETRY_OK,
+ TMH_ES_NO_CURR_RETRY_OK = TMH_ES_NO_CURR | TMH_ES_RETRY_OK,
+ TMH_ES_NO_KEYS_RETRY_OK = TMH_ES_NO_KEYS | TMH_ES_RETRY_OK,
+}
TMH_exchange_check_debit (
const char *instance_id,
const struct TMH_Exchange *exchange,
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -1310,6 +1310,7 @@ process_pay_with_keys (
struct TMH_HandlerContext *hc = pc->hc;
unsigned int group_size;
struct TALER_Amount max_amount;
+ enum TMH_ExchangeStatus es;
eg->fo = NULL;
pc->batch_deposits.pending_at_eg--;
@@ -1340,14 +1341,16 @@ process_pay_with_keys (
}
max_amount = eg->total;
- if (GNUNET_OK !=
- TMH_exchange_check_debit (
- pc->hc->instance->settings.id,
- exchange,
- pc->check_contract.wm,
- &max_amount))
+ es = TMH_exchange_check_debit (
+ pc->hc->instance->settings.id,
+ exchange,
+ pc->check_contract.wm,
+ &max_amount);
+ if ( (TMH_ES_OK != es) &&
+ (TMH_ES_RETRY_OK != es) )
{
- if (eg->tried_force_keys)
+ if (eg->tried_force_keys ||
+ (0 == (TMH_ES_RETRY_OK & es)) )
{
GNUNET_break_op (0);
resume_pay_with_error (
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-tokens.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-tokens.c
@@ -42,9 +42,10 @@ add_token (void *cls,
{
json_t *pa = cls;
bool refreshable;
- const char*as;
+ const char *as;
- as = TMH_get_name_by_scope (scope, &refreshable);
+ as = TMH_get_name_by_scope (scope,
+ &refreshable);
if (NULL == as)
{
GNUNET_break (0);
@@ -76,13 +77,16 @@ TMH_private_get_instances_ID_tokens (const struct TMH_RequestHandler *rh,
{
json_t *ta;
enum GNUNET_DB_QueryStatus qs;
- int64_t limit;
- uint64_t offset = 0;
+ int64_t limit = -20; /* default */
+ uint64_t offset;
- limit = -20; /* default */
TALER_MHD_parse_request_snumber (connection,
"limit",
&limit);
+ if (limit > 0)
+ offset = 0;
+ else
+ offset = INT64_MAX;
TALER_MHD_parse_request_number (connection,
"offset",
&offset);
@@ -110,4 +114,4 @@ TMH_private_get_instances_ID_tokens (const struct TMH_RequestHandler *rh,
}
-/* end of taler-merchant-httpd_private-get-products.c */
+/* end of taler-merchant-httpd_private-get-instances-ID-tokens.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -550,6 +550,17 @@ struct OrderContext
bool forced_reload;
/**
+ * Did we find a working exchange?
+ */
+ bool exchange_ok;
+
+ /**
+ * Did we find an exchange that justifies
+ * reloading keys?
+ */
+ bool promising_exchange;
+
+ /**
* Set to true once we have attempted to load exchanges
* for the first time.
*/
@@ -2562,8 +2573,10 @@ phase_select_wire_method (struct OrderContext *oc)
}
if ( (want_choices > max_choices) &&
+ (oc->set_exchanges.promising_exchange) &&
(! oc->set_exchanges.forced_reload) )
{
+ oc->set_exchanges.exchange_ok = false;
/* Not all choices in the contract can work with these
exchanges, try again with forcing /keys download */
for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head;
@@ -2792,7 +2805,7 @@ get_acceptable (struct OrderContext *oc,
const struct TALER_Amount *max_needed = NULL;
unsigned int priority = 42; /* make compiler happy */
json_t *j_exchange;
- enum GNUNET_GenericReturnValue res;
+ enum TMH_ExchangeStatus res;
struct TALER_Amount max_amount;
for (unsigned int i = 0; i<oc->add_payment_details.num_max_choice_limits; i++)
@@ -2842,21 +2855,57 @@ get_acceptable (struct OrderContext *oc,
}
switch (res)
{
- case GNUNET_OK:
+ case TMH_ES_OK:
+ case TMH_ES_RETRY_OK:
priority = 1024; /* high */
+ oc->set_exchanges.exchange_ok = true;
break;
- case GNUNET_NO:
+ case TMH_ES_NO_ACC:
if (oc->set_exchanges.forced_reload)
priority = 0; /* fresh negative response */
else
priority = 512; /* stale negative response */
break;
- case GNUNET_SYSERR:
+ case TMH_ES_NO_CURR:
+ if (oc->set_exchanges.forced_reload)
+ priority = 0; /* fresh negative response */
+ else
+ priority = 512; /* stale negative response */
+ break;
+ case TMH_ES_NO_KEYS:
if (oc->set_exchanges.forced_reload)
priority = 256; /* fresh, no accounts yet */
else
priority = 768; /* stale, no accounts yet */
break;
+ case TMH_ES_NO_ACC_RETRY_OK:
+ if (oc->set_exchanges.forced_reload)
+ {
+ priority = 0; /* fresh negative response */
+ }
+ else
+ {
+ oc->set_exchanges.promising_exchange = true;
+ priority = 512; /* stale negative response */
+ }
+ break;
+ case TMH_ES_NO_CURR_RETRY_OK:
+ if (oc->set_exchanges.forced_reload)
+ priority = 0; /* fresh negative response */
+ else
+ priority = 512; /* stale negative response */
+ break;
+ case TMH_ES_NO_KEYS_RETRY_OK:
+ if (oc->set_exchanges.forced_reload)
+ {
+ priority = 256; /* fresh, no accounts yet */
+ }
+ else
+ {
+ oc->set_exchanges.promising_exchange = true;
+ priority = 768; /* stale, no accounts yet */
+ }
+ break;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Exchange %s deposit limit is %s, adding it!\n",
@@ -2915,6 +2964,7 @@ keys_cb (
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to download %skeys\n",
rx->url);
+ oc->set_exchanges.promising_exchange = true;
goto cleanup;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -3051,8 +3101,14 @@ phase_set_exchanges (struct OrderContext *oc)
TMH_exchange_get_trusted (&get_exchange_keys,
oc);
}
- else if (! oc->set_exchanges.forced_reload)
+ else if ( (! oc->set_exchanges.forced_reload) &&
+ (oc->set_exchanges.promising_exchange) &&
+ (! oc->set_exchanges.exchange_ok) )
{
+ for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head;
+ NULL != wmc;
+ wmc = wmc->next)
+ json_array_clear (wmc->exchanges);
/* Try one more time with forcing /keys download */
oc->set_exchanges.forced_reload = true;
TMH_exchange_get_trusted (&get_exchange_keys,
diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf
@@ -3,6 +3,9 @@
[PATHS]
TALER_TEST_HOME = test_merchant_api_home/
+[merchant-exchange-chf]
+DISABLED = YES
+
[merchant-exchange-kudos]
DISABLED = YES
diff --git a/src/testing/test_merchant_api.conf b/src/testing/test_merchant_api.conf
@@ -4,9 +4,6 @@
TALER_TEST_HOME = test_merchant_api_home/
DONAU_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/donau-system-runtime/
-[merchant-exchange-kudos]
-DISABLED = YES
-
[taler-helper-crypto-rsa]
LOOKAHEAD_SIGN = 10 days
@@ -20,6 +17,9 @@ HTTP_PORT = 8082
[merchant-exchange-chf]
DISABLED = YES
+[merchant-exchange-kudos]
+DISABLED = YES
+
[libeufin-bank]
CURRENCY = EUR
WIRE_TYPE = x-taler-bank
diff --git a/src/testing/test_merchant_instance_auth.sh b/src/testing/test_merchant_instance_auth.sh
@@ -408,19 +408,20 @@ RTOKEN=$(jq -e -r .access_token < "$LAST_RESPONSE")
echo " OK" >&2
-echo "Getting 2 login tokens with offset 2." >&2
+echo "Getting last 2 login tokens." >&2
STATUS=$(curl -H "Content-Type: application/json" \
-H "Authorization: Bearer $RWTOKEN" \
- 'http://localhost:9966/instances/second/private/tokens?limit=2&offset=4' \
+ 'http://localhost:9966/instances/second/private/tokens?limit=-2' \
-w "%{http_code}" -s -o $LAST_RESPONSE)
if [ "$STATUS" != "200" ]
then
+ jq < "$LAST_RESPONSE" >&2
exit_fail "Expected 200 OK. Got: $STATUS"
fi
-TOKEN_SERIAL=$(jq -e -r .tokens[1].serial < "$LAST_RESPONSE")
+TOKEN_SERIAL=$(jq -e -r .tokens[0].serial < "$LAST_RESPONSE")
echo -n "Deleting second login token by serial..." >&2
@@ -436,7 +437,7 @@ then
fi
echo " OK" >&2
-echo -n "Using deleted login token..." >&2
+echo -n "Using deleted login token $RTOKEN..." >&2
STATUS=$(curl "http://localhost:9966/instances/second/private/orders" \
-H 'Authorization: Bearer '"$RTOKEN" \
diff --git a/src/testing/test_template.conf b/src/testing/test_template.conf
@@ -4,6 +4,9 @@ TALER_TEST_HOME = test_merchant_api_home/
[merchant-exchange-kudos]
DISABLED = YES
+[merchant-exchange-chf]
+DISABLED = YES
+
[exchange]
CURRENCY = TESTKUDOS
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
diff --git a/src/util/currencies.conf b/src/util/currencies.conf
@@ -2,6 +2,7 @@
ENABLED = YES
name = "Euro"
code = "EUR"
+common_amounts = "EUR:5"
fractional_input_digits = 2
fractional_normal_digits = 2
fractional_trailing_zero_digits = 2
@@ -11,6 +12,7 @@ alt_unit_names = {"0":"€"}
ENABLED = YES
name = "Swiss Francs"
code = "CHF"
+common_amounts = "CHF:5"
fractional_input_digits = 2
fractional_normal_digits = 2
fractional_trailing_zero_digits = 2
@@ -20,6 +22,7 @@ alt_unit_names = {"0":"Fr.","-2":"Rp."}
ENABLED = NO
name = "Hungarian Forint"
code = "HUF"
+common_amounts = "HUF:500"
fractional_input_digits = 0
fractional_normal_digits = 0
fractional_trailing_zero_digits = 0
@@ -29,6 +32,7 @@ alt_unit_names = {"0":"Ft"}
ENABLED = NO
name = "US Dollar"
code = "USD"
+common_amounts = "USD:5"
fractional_input_digits = 2
fractional_normal_digits = 2
fractional_trailing_zero_digits = 2
@@ -38,6 +42,7 @@ alt_unit_names = {"0":"$"}
ENABLED = YES
name = "Kudos (Taler Demonstrator)"
code = "KUDOS"
+common_amounts = "KUDOS:5"
fractional_input_digits = 2
fractional_normal_digits = 2
fractional_trailing_zero_digits = 2
@@ -47,6 +52,7 @@ alt_unit_names = {"0":"ク"}
ENABLED = YES
name = "Test-kudos (Taler Demonstrator)"
code = "TESTKUDOS"
+common_amounts = "TESTKUDOS:5"
fractional_input_digits = 2
fractional_normal_digits = 2
fractional_trailing_zero_digits = 2
@@ -56,6 +62,7 @@ alt_unit_names = {"0":"テ","3":"kテ","-3":"mテ"}
ENABLED = NO
name = "Japanese Yen"
code = "JPY"
+common_amounts = "JPY:1000"
fractional_input_digits = 2
fractional_normal_digits = 0
fractional_trailing_zero_digits = 2
@@ -65,6 +72,7 @@ alt_unit_names = {"0":"¥"}
ENABLED = NO
name = "Bitcoin (Mainnet)"
code = "BITCOINBTC"
+common_amounts = "BITCOINBTC:0.001"
fractional_input_digits = 8
fractional_normal_digits = 3
fractional_trailing_zero_digits = 0
@@ -74,6 +82,7 @@ alt_unit_names = {"0":"BTC","-3":"mBTC"}
ENABLED = NO
name = "WAI-ETHER (Ethereum)"
code = "EthereumWAI"
+common_amounts = "EthereumWAI:1000000"
fractional_input_digits = 0
fractional_normal_digits = 0
fractional_trailing_zero_digits = 0
@@ -83,6 +92,7 @@ alt_unit_names = {"0":"WAI","3":"KWAI","6":"MWAI","9":"GWAI","12":"Szabo","15":"
ENABLED=YES
name=NetzBon
code=NETZBON
+common_amounts = "NETZBON:5"
fractional_input_digits=2
fractional_normal_digits=2
fractional_trailing_zero_digits=2