Merge branch 'master' of ssh://git.taler.net/exchange

This commit is contained in:
Özgür Kesim 2023-04-22 15:39:14 +02:00
commit 89a9224c3b
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
13 changed files with 1713 additions and 1853 deletions

@ -1 +1 @@
Subproject commit 7adfb722ac988170a31cdfd8132073eb7f2a6c43 Subproject commit bf43b20a0362ac19bcf1bab9c33215e55d8d9f36

View File

@ -46,6 +46,12 @@
<anchorfile>microhttpd.h</anchorfile> <anchorfile>microhttpd.h</anchorfile>
<arglist></arglist> <arglist></arglist>
</member> </member>
<member kind="define">
<type>#define</type>
<name>MHD_HTTP_CONTENT_TOO_LARGE</name>
<anchorfile>microhttpd.h</anchorfile>
<arglist></arglist>
</member>
<member kind="define"> <member kind="define">
<type>#define</type> <type>#define</type>
<name>MHD_HTTP_REQUEST_TIMEOUT</name> <name>MHD_HTTP_REQUEST_TIMEOUT</name>

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ DB = postgres
# exchange (or the twister) is actually listening. # exchange (or the twister) is actually listening.
base_url = "http://localhost:8081/" base_url = "http://localhost:8081/"
WIREWATCH_IDLE_SLEEP_INTERVAL = 1500 ms WIREWATCH_IDLE_SLEEP_INTERVAL = 500 ms
[exchange-offline] [exchange-offline]
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
@ -51,11 +51,11 @@ MAX_DEBT = EUR:100000000000.0
MAX_DEBT_BANK = EUR:1000000000000000.0 MAX_DEBT_BANK = EUR:1000000000000000.0
[benchmark] [benchmark]
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42 USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42?receiver-name=user42
[exchange-account-2] [exchange-account-2]
# What is the payto://-URL of the exchange (to generate wire response) # What is the payto://-URL of the exchange (to generate wire response)
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange" PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange?receiver-name=Exchange"
enable_debit = YES enable_debit = YES
enable_credit = YES enable_credit = YES

View File

@ -689,6 +689,8 @@ parallel_benchmark (void)
} }
/* But be extra sure we did finish all shards by doing one more */ /* But be extra sure we did finish all shards by doing one more */
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Shard check phase\n");
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL, NULL, NULL, NULL,
"taler-exchange-wirewatch", "taler-exchange-wirewatch",

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2014-2020 Taler Systems SA (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as under the terms of the GNU Affero General Public License as

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2022 Taler Systems SA Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the 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 terms of the GNU Affero General Public License as published by the Free Software
@ -62,6 +62,16 @@ struct ReservePoller
*/ */
struct GNUNET_TIME_Absolute timeout; struct GNUNET_TIME_Absolute timeout;
/**
* Public key of the reserve the inquiry is about.
*/
struct TALER_ReservePublicKeyP reserve_pub;
/**
* Balance of the reserve, set in the callback.
*/
struct TALER_Amount balance;
/** /**
* True if we are still suspended. * True if we are still suspended.
*/ */
@ -84,13 +94,10 @@ static struct ReservePoller *rp_tail;
void void
TEH_reserves_get_cleanup () TEH_reserves_get_cleanup ()
{ {
struct ReservePoller *rp; for (struct ReservePoller *rp = rp_head;
NULL != rp;
while (NULL != (rp = rp_head)) rp = rp->next)
{ {
GNUNET_CONTAINER_DLL_remove (rp_head,
rp_tail,
rp);
if (rp->suspended) if (rp->suspended)
{ {
rp->suspended = false; rp->suspended = false;
@ -115,11 +122,14 @@ rp_cleanup (struct TEH_RequestContext *rc)
if (NULL != rp->eh) if (NULL != rp->eh)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Cancelling DB event listening\n"); "Cancelling DB event listening on cleanup (odd unless during shutdown)\n");
TEH_plugin->event_listen_cancel (TEH_plugin->cls, TEH_plugin->event_listen_cancel (TEH_plugin->cls,
rp->eh); rp->eh);
rp->eh = NULL; rp->eh = NULL;
} }
GNUNET_CONTAINER_DLL_remove (rp_head,
rp_tail,
rp);
GNUNET_free (rp); GNUNET_free (rp);
} }
@ -137,26 +147,15 @@ db_event_cb (void *cls,
const void *extra, const void *extra,
size_t extra_size) size_t extra_size)
{ {
struct TEH_RequestContext *rc = cls; struct ReservePoller *rp = cls;
struct ReservePoller *rp = rc->rh_ctx;
struct GNUNET_AsyncScopeSave old_scope; struct GNUNET_AsyncScopeSave old_scope;
(void) extra; (void) extra;
(void) extra_size; (void) extra_size;
if (NULL == rp)
return; /* event triggered while main transaction
was still running */
if (! rp->suspended) if (! rp->suspended)
return; /* might get multiple wake-up events */ return; /* might get multiple wake-up events */
rp->suspended = false;
GNUNET_async_scope_enter (&rc->async_scope_id,
&old_scope);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Resuming from long-polling on reserve\n");
TEH_check_invariants (); TEH_check_invariants ();
GNUNET_CONTAINER_DLL_remove (rp_head, rp->suspended = false;
rp_tail,
rp);
MHD_resume_connection (rp->connection); MHD_resume_connection (rp->connection);
TALER_MHD_daemon_trigger (); TALER_MHD_daemon_trigger ();
TEH_check_invariants (); TEH_check_invariants ();
@ -164,191 +163,133 @@ db_event_cb (void *cls,
} }
/**
* Closure for #reserve_history_transaction.
*/
struct ReserveHistoryContext
{
/**
* Public key of the reserve the inquiry is about.
*/
struct TALER_ReservePublicKeyP reserve_pub;
/**
* Balance of the reserve, set in the callback.
*/
struct TALER_Amount balance;
/**
* Set to true if we did not find the reserve.
*/
bool not_found;
};
/**
* Function implementing /reserves/ GET transaction.
* Execute a /reserves/ GET. Given the public key of a reserve,
* return the associated transaction history. Runs the
* transaction logic; IF it returns a non-error code, the transaction
* logic MUST NOT queue a MHD response. IF it returns an hard error,
* the transaction logic MUST queue a MHD response and set @a mhd_ret.
* IF it returns the soft error code, the function MAY be called again
* to retry and MUST not queue a MHD response.
*
* @param cls a `struct ReserveHistoryContext *`
* @param connection MHD request which triggered the transaction
* @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!)
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
reserve_balance_transaction (void *cls,
struct MHD_Connection *connection,
MHD_RESULT *mhd_ret)
{
struct ReserveHistoryContext *rsc = cls;
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
&rsc->reserve_pub,
&rsc->balance);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
*mhd_ret
= TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_reserve_balance");
}
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
rsc->not_found = true;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
rsc->not_found = false;
return qs;
}
MHD_RESULT MHD_RESULT
TEH_handler_reserves_get (struct TEH_RequestContext *rc, TEH_handler_reserves_get (struct TEH_RequestContext *rc,
const char *const args[1]) const char *const args[1])
{ {
struct ReserveHistoryContext rsc; struct ReservePoller *rp = rc->rh_ctx;
struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
struct GNUNET_DB_EventHandler *eh = NULL;
if (GNUNET_OK != if (NULL == rp)
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&rsc.reserve_pub,
sizeof (rsc.reserve_pub)))
{ {
GNUNET_break_op (0); struct GNUNET_TIME_Relative timeout
return TALER_MHD_reply_with_error (rc->connection, = GNUNET_TIME_UNIT_ZERO;
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
{
const char *long_poll_timeout_ms;
long_poll_timeout_ms rp = GNUNET_new (struct ReservePoller);
= MHD_lookup_connection_value (rc->connection, rp->connection = rc->connection;
MHD_GET_ARGUMENT_KIND, rc->rh_ctx = rp;
"timeout_ms"); rc->rh_cleaner = &rp_cleanup;
if (NULL != long_poll_timeout_ms) GNUNET_CONTAINER_DLL_insert (rp_head,
rp_tail,
rp);
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&rp->reserve_pub,
sizeof (rp->reserve_pub)))
{ {
unsigned int timeout_ms; GNUNET_break_op (0);
char dummy; return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
if (1 != sscanf (long_poll_timeout_ms, TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
"%u%c", args[0]);
&timeout_ms,
&dummy))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"timeout_ms (must be non-negative number)");
}
timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
timeout_ms);
} }
{
const char *long_poll_timeout_ms;
long_poll_timeout_ms
= MHD_lookup_connection_value (rc->connection,
MHD_GET_ARGUMENT_KIND,
"timeout_ms");
if (NULL != long_poll_timeout_ms)
{
unsigned int timeout_ms;
char dummy;
if (1 != sscanf (long_poll_timeout_ms,
"%u%c",
&timeout_ms,
&dummy))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"timeout_ms (must be non-negative number)");
}
timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
timeout_ms);
}
}
rp->timeout = GNUNET_TIME_relative_to_absolute (timeout);
} }
if ( (! GNUNET_TIME_relative_is_zero (timeout)) &&
(NULL == rc->rh_ctx) ) if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
(NULL == rp->eh) )
{ {
struct TALER_ReserveEventP rep = { struct TALER_ReserveEventP rep = {
.header.size = htons (sizeof (rep)), .header.size = htons (sizeof (rep)),
.header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING), .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
.reserve_pub = rsc.reserve_pub .reserve_pub = rp->reserve_pub
}; };
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Starting DB event listening\n"); "Starting DB event listening until %s\n",
eh = TEH_plugin->event_listen (TEH_plugin->cls, GNUNET_TIME_absolute2s (rp->timeout));
timeout, rp->eh = TEH_plugin->event_listen (
&rep.header, TEH_plugin->cls,
&db_event_cb, GNUNET_TIME_absolute_get_remaining (rp->timeout),
rc); &rep.header,
&db_event_cb,
rp);
} }
{ {
MHD_RESULT mhd_ret; enum GNUNET_DB_QueryStatus qs;
if (GNUNET_OK != qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
TEH_DB_run_transaction (rc->connection, &rp->reserve_pub,
"get reserve balance", &rp->balance);
TEH_MT_REQUEST_OTHER, switch (qs)
&mhd_ret,
&reserve_balance_transaction,
&rsc))
{
if (NULL != eh)
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
eh);
return mhd_ret;
}
}
/* generate proper response */
if (rsc.not_found)
{
struct ReservePoller *rp = rc->rh_ctx;
if ( (NULL != rp) ||
(GNUNET_TIME_relative_is_zero (timeout)) )
{ {
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (0); /* single-shot query should never have soft-errors */
return TALER_MHD_reply_with_error (rc->connection, return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN, TALER_EC_GENERIC_DB_SOFT_FAILURE,
args[0]); "get_reserve_balance");
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_reserve_balance");
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got reserve balance of %s\n",
TALER_amount2s (&rp->balance));
return TALER_MHD_REPLY_JSON_PACK (rc->connection,
MHD_HTTP_OK,
TALER_JSON_pack_amount ("balance",
&rp->balance));
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
if (! GNUNET_TIME_absolute_is_future (rp->timeout))
{
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN,
args[0]);
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Long-polling on reserve for %s\n",
GNUNET_STRINGS_relative_time_to_string (
GNUNET_TIME_absolute_get_remaining (rp->timeout),
true));
rp->suspended = true;
MHD_suspend_connection (rc->connection);
return MHD_YES;
} }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Long-polling on reserve for %s\n",
GNUNET_STRINGS_relative_time_to_string (timeout,
GNUNET_YES));
rp = GNUNET_new (struct ReservePoller);
rp->connection = rc->connection;
rp->timeout = GNUNET_TIME_relative_to_absolute (timeout);
rp->eh = eh;
rc->rh_ctx = rp;
rc->rh_cleaner = &rp_cleanup;
rp->suspended = true;
GNUNET_CONTAINER_DLL_insert (rp_head,
rp_tail,
rp);
MHD_suspend_connection (rc->connection);
return MHD_YES;
} }
if (NULL != eh) GNUNET_break (0);
TEH_plugin->event_listen_cancel (TEH_plugin->cls, return MHD_NO;
eh);
return TALER_MHD_REPLY_JSON_PACK (
rc->connection,
MHD_HTTP_OK,
TALER_JSON_pack_amount ("balance",
&rsc.balance));
} }

View File

@ -57,6 +57,12 @@ static struct TALER_BANK_CreditHistoryHandle *hh;
*/ */
static bool hh_returned_data; static bool hh_returned_data;
/**
* Set to true if the request for history did not
* succeed because the account was unknown.
*/
static bool hh_account_404;
/** /**
* When did we start the last @e hh request? * When did we start the last @e hh request?
*/ */
@ -472,9 +478,9 @@ transaction_completed (void)
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return; return;
} }
if (! hh_returned_data) if (! (hh_returned_data || hh_account_404) )
{ {
/* Enforce long polling delay even if the server ignored it /* Enforce long-polling delay even if the server ignored it
and returned earlier */ and returned earlier */
struct GNUNET_TIME_Relative latency; struct GNUNET_TIME_Relative latency;
struct GNUNET_TIME_Relative left; struct GNUNET_TIME_Relative left;
@ -482,8 +488,17 @@ transaction_completed (void)
latency = GNUNET_TIME_absolute_get_duration (hh_start_time); latency = GNUNET_TIME_absolute_get_duration (hh_start_time);
left = GNUNET_TIME_relative_subtract (longpoll_timeout, left = GNUNET_TIME_relative_subtract (longpoll_timeout,
latency); latency);
if (! (test_mode ||
GNUNET_TIME_relative_is_zero (left)) )
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, // WARNING,
"Server did not respect long-polling, enforcing client-side by sleeping for %s\n",
GNUNET_TIME_relative2s (left,
true));
delayed_until = GNUNET_TIME_relative_to_absolute (left); delayed_until = GNUNET_TIME_relative_to_absolute (left);
} }
if (hh_account_404)
delayed_until = GNUNET_TIME_relative_to_absolute (
GNUNET_TIME_UNIT_MILLISECONDS);
if (test_mode) if (test_mode)
delayed_until = GNUNET_TIME_UNIT_ZERO_ABS; delayed_until = GNUNET_TIME_UNIT_ZERO_ABS;
GNUNET_assert (NULL == task); GNUNET_assert (NULL == task);
@ -709,7 +724,7 @@ history_cb (void *cls,
} }
GNUNET_assert (NULL == task); GNUNET_assert (NULL == task);
hh = NULL; hh = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"History request returned with HTTP status %u\n", "History request returned with HTTP status %u\n",
reply->http_status); reply->http_status);
switch (reply->http_status) switch (reply->http_status)
@ -723,6 +738,7 @@ history_cb (void *cls,
transaction_completed (); transaction_completed ();
return; return;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
hh_account_404 = true;
if (ignore_account_404) if (ignore_account_404)
{ {
transaction_completed (); transaction_completed ();
@ -761,6 +777,7 @@ continue_with_shard (void *cls)
(unsigned long long) latest_row_off); (unsigned long long) latest_row_off);
hh_start_time = GNUNET_TIME_absolute_get (); hh_start_time = GNUNET_TIME_absolute_get ();
hh_returned_data = false; hh_returned_data = false;
hh_account_404 = false;
hh = TALER_BANK_credit_history (ctx, hh = TALER_BANK_credit_history (ctx,
ai->auth, ai->auth,
latest_row_off, latest_row_off,
@ -857,6 +874,17 @@ lock_shard (void *cls)
job_name, job_name,
GNUNET_STRINGS_relative_time_to_string (rdelay, GNUNET_STRINGS_relative_time_to_string (rdelay,
true)); true));
#if 1
if (GNUNET_TIME_relative_cmp (rdelay,
>,
GNUNET_TIME_UNIT_SECONDS))
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Delay would have been for %s\n",
GNUNET_TIME_relative2s (rdelay,
true));
rdelay = GNUNET_TIME_relative_min (rdelay,
GNUNET_TIME_UNIT_SECONDS);
#endif
delayed_until = GNUNET_TIME_relative_to_absolute (rdelay); delayed_until = GNUNET_TIME_relative_to_absolute (rdelay);
} }
GNUNET_assert (NULL == task); GNUNET_assert (NULL == task);
@ -869,7 +897,7 @@ lock_shard (void *cls)
job_name, job_name,
GNUNET_STRINGS_relative_time_to_string ( GNUNET_STRINGS_relative_time_to_string (
wirewatch_idle_sleep_interval, wirewatch_idle_sleep_interval,
GNUNET_YES)); true));
delayed_until = GNUNET_TIME_relative_to_absolute ( delayed_until = GNUNET_TIME_relative_to_absolute (
wirewatch_idle_sleep_interval); wirewatch_idle_sleep_interval);
shard_open = false; shard_open = false;

File diff suppressed because it is too large Load Diff

View File

@ -1409,7 +1409,7 @@ irbt_cb_table_purse_decision (struct PostgresClosure *pg,
GNUNET_PQ_query_param_timestamp ( GNUNET_PQ_query_param_timestamp (
&td->details.purse_decision.action_timestamp), &td->details.purse_decision.action_timestamp),
GNUNET_PQ_query_param_bool ( GNUNET_PQ_query_param_bool (
&td->details.purse_decision.refunded), td->details.purse_decision.refunded),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,6 @@
*/ */
struct WirewatchState struct WirewatchState
{ {
/** /**
* Process for the wirewatcher. * Process for the wirewatcher.
*/ */

View File

@ -353,7 +353,7 @@ maint_child_death (void *cls)
while (TALER_TESTING_cmd_is_batch (cmd)) while (TALER_TESTING_cmd_is_batch (cmd))
cmd = TALER_TESTING_cmd_batch_get_current (cmd); cmd = TALER_TESTING_cmd_batch_get_current (cmd);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got SIGCHLD for `%s'.\n", "Got SIGCHLD for `%s'.\n",
cmd->label); cmd->label);
is->child_death_task = NULL; is->child_death_task = NULL;