aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog7
-rw-r--r--debian/taler-merchant.taler-merchant-depositcheck.service14
-rw-r--r--doc/Makefile.am5
m---------doc/prebuilt0
-rw-r--r--src/backend/Makefile.am17
-rw-r--r--src/backend/taler-merchant-depositcheck.c572
-rw-r--r--src/backenddb/Makefile.am1
-rw-r--r--src/backenddb/pg_lookup_pending_deposits.c40
-rw-r--r--src/backenddb/pg_lookup_pending_deposits.h50
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c3
-rw-r--r--src/include/taler_merchantdb_plugin.h67
11 files changed, 601 insertions, 175 deletions
diff --git a/debian/changelog b/debian/changelog
index ecd3e21c..52cddd43 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+taler-merchant (0.9.4) unstable; urgency=low
+
+ * Add support for new taler-merchant-depositcheck service.
+ * Packages the v0.9.4 release.
+
+ -- Christian Grothoff <grothoff@gnu.org> Sat, 6 Jan 2024 14:50:12 +0100
+
taler-merchant (0.9.3-5) unstable; urgency=low
* Tolerate missing currencies.conf, but log a warning.
diff --git a/debian/taler-merchant.taler-merchant-depositcheck.service b/debian/taler-merchant.taler-merchant-depositcheck.service
new file mode 100644
index 00000000..85964dbc
--- /dev/null
+++ b/debian/taler-merchant.taler-merchant-depositcheck.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=GNU Taler payment system merchant deposit check service
+After=postgres.service
+
+[Service]
+User=taler-merchant-httpd
+Type=simple
+Restart=always
+RestartSec=1s
+ExecStart=/usr/bin/taler-merchant-depositcheck -c /etc/taler/taler.conf
+PrivateTmp=yes
+PrivateDevices=yes
+ProtectSystem=full
+RuntimeMaxSec=3600s
diff --git a/doc/Makefile.am b/doc/Makefile.am
index dadf6083..3d6852a7 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,12 +1,15 @@
SUBDIRS = . doxygen
man_MANS = \
+ prebuilt/man/taler-merchant-benchmark.1
prebuilt/man/taler-merchant-dbconfig.1\
prebuilt/man/taler-merchant-dbinit.1\
+ prebuilt/man/taler-merchant-depositcheck.1\
prebuilt/man/taler-merchant-httpd.1 \
prebuilt/man/taler-merchant-passwd.1\
prebuilt/man/taler-merchant-setup-reserve.1\
- prebuilt/man/taler-merchant-benchmark.1
+ prebuilt/man/taler-merchant-webhook.1 \
+ prebuilt/man/taler-merchant-wirewatch.1
info_TEXINFOS = \
prebuilt/texinfo/taler-merchant-api-tutorial.texi \
diff --git a/doc/prebuilt b/doc/prebuilt
-Subproject 5e47a72e8a2b5086dfdae4078f695155f5ed7af
+Subproject 5e7026c5cef101c90b6dc58096e0a0946ef11b0
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 7978258d..2fe09eec 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -16,6 +16,7 @@ EXTRA_DIST = \
$(pkgcfg_DATA)
bin_PROGRAMS = \
+ taler-merchant-depositcheck \
taler-merchant-exchange \
taler-merchant-httpd \
taler-merchant-webhook \
@@ -226,6 +227,22 @@ taler_merchant_webhook_LDADD = \
taler_merchant_webhook_CFLAGS = \
$(AM_CFLAGS)
+taler_merchant_depositcheck_SOURCES = \
+ taler-merchant-depositcheck.c
+taler_merchant_depositcheck_LDADD = \
+ $(top_builddir)/src/backenddb/libtalermerchantdb.la \
+ -ltalerexchange \
+ -ltalerjson \
+ -ltalerutil \
+ -ltalerpq \
+ -ljansson \
+ -lgnunetcurl \
+ -lgnunetjson \
+ -lgnunetutil \
+ -lcurl \
+ $(XLIB)
+taler_merchant_depositcheck_CFLAGS = \
+ $(AM_CFLAGS)
taler_merchant_wirewatch_SOURCES = \
taler-merchant-wirewatch.c
diff --git a/src/backend/taler-merchant-depositcheck.c b/src/backend/taler-merchant-depositcheck.c
index 6982d891..4364c9f5 100644
--- a/src/backend/taler-merchant-depositcheck.c
+++ b/src/backend/taler-merchant-depositcheck.c
@@ -26,37 +26,59 @@
#include "taler_merchantdb_plugin.h"
#include <taler/taler_dbevents.h>
+/**
+ * How many requests do we make at most in parallel to the same exchange?
+ */
+#define CONCURRENCY_LIMIT 32
+
/**
* Information we keep per exchange.
*/
-struct ExchangeState
+struct Child
{
/**
* Kept in a DLL.
*/
- struct ExchangeState *next;
+ struct Child *next;
/**
* Kept in a DLL.
*/
- struct ExchangeState *prev;
+ struct Child *prev;
+
+ /**
+ * The child process.
+ */
+ struct GNUNET_OS_Process *process;
+
+ /**
+ * Wait handle.
+ */
+ struct GNUNET_ChildWaitHandle *cwh;
/**
* Which exchange is this state for?
*/
- const char *base_url;
+ char *base_url;
/**
- * Key material of the exchange.
+ * Task to restart the child.
*/
- struct TALER_EXCHANGE_Keys *keys;
+ struct GNUNET_SCHEDULER_Task *rt;
/**
- * Handle for active /keys request.
+ * When should the child be restarted at the earliest?
*/
- struct TALER_EXCHANGE_GetKeysHandle *gkh;
+ struct GNUNET_TIME_Absolute next_start;
+
+ /**
+ * Current minimum delay between restarts, grows
+ * exponentially if child exits befor this time.
+ */
+ struct GNUNET_TIME_Relative rd;
+
};
@@ -76,9 +98,9 @@ struct ExchangeInteraction
struct ExchangeInteraction *prev;
/**
- * Exchange we are interacting with.
+ * Handle for exchange interaction.
*/
- struct ExchangeState *es;
+ struct TALER_EXCHANGE_DepositGetHandle *dgh;
/**
* Wire deadline for the deposit.
@@ -115,18 +137,39 @@ struct ExchangeInteraction
*/
struct TALER_MerchantPrivateKeyP merchant_priv;
+ /**
+ * Serial number of the row in the deposits table
+ * that we are processing.
+ */
+ uint64_t deposit_serial;
+
+ /**
+ * The instance the deposit belongs to.
+ */
+ char *instance_id;
+
};
/**
- * Head of list of exchanges we interact with.
+ * Head of list of children we forked.
+ */
+static struct Child *c_head;
+
+/**
+ * Tail of list of children we forked.
+ */
+static struct Child *c_tail;
+
+/**
+ * Key material of the exchange.
*/
-static struct ExchangeState *e_head;
+static struct TALER_EXCHANGE_Keys *keys;
/**
- * Tail of list of exchanges we interact with.
+ * Handle for active /keys request.
*/
-static struct ExchangeState *e_tail;
+static struct TALER_EXCHANGE_GetKeysHandle *gkh;
/**
* Head of list of active exchange interactions.
@@ -139,6 +182,11 @@ static struct ExchangeInteraction *w_head;
static struct ExchangeInteraction *w_tail;
/**
+ * Number of active entries in the @e w_head list.
+ */
+static uint64_t w_count;
+
+/**
* Notification handler from database on new work.
*/
static struct GNUNET_DB_EventHandler *eh;
@@ -149,6 +197,11 @@ static struct GNUNET_DB_EventHandler *eh;
static const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
+ * Name of the configuration file we use.
+ */
+static const char *cfg_filename;
+
+/**
* Our database plugin.
*/
static struct TALER_MERCHANTDB_Plugin *db_plugin;
@@ -174,6 +227,12 @@ static struct GNUNET_CURL_Context *ctx;
static struct GNUNET_CURL_RescheduleContext *rc;
/**
+ * Which exchange are we monitoring? NULL if we
+ * are the parent of the workers.
+ */
+static char *exchange_url;
+
+/**
* Value to return from main(). 0 on success, non-zero on errors.
*/
static int global_ret;
@@ -192,7 +251,7 @@ static int test_mode;
static void
shutdown_task (void *cls)
{
- struct ExchangeState *es;
+ struct Child *c;
struct ExchangeInteraction *w;
(void) cls;
@@ -208,24 +267,70 @@ shutdown_task (void *cls)
GNUNET_SCHEDULER_cancel (task);
task = NULL;
}
+ if (NULL != gkh)
+ {
+ TALER_EXCHANGE_get_keys_cancel (gkh);
+ gkh = NULL;
+ }
while (NULL != (w = w_head))
{
GNUNET_CONTAINER_DLL_remove (w_head,
w_tail,
w);
+ if (NULL != w->dgh)
+ {
+ TALER_EXCHANGE_deposits_get_cancel (w->dgh);
+ w->dgh = NULL;
+ }
+ w_count--;
+ GNUNET_free (w->instance_id);
GNUNET_free (w);
}
- while (NULL != (es = e_head))
+ while (NULL != (c = c_head))
+ {
+ enum GNUNET_OS_ProcessStatusType type;
+ unsigned long code;
+
+ GNUNET_CONTAINER_DLL_remove (c_head,
+ c_tail,
+ c);
+ if (NULL != c->rt)
+ {
+ GNUNET_SCHEDULER_cancel (c->rt);
+ c->rt = NULL;
+ }
+ if (NULL != c->cwh)
+ {
+ GNUNET_wait_child_cancel (c->cwh);
+ c->cwh = NULL;
+ }
+ if (NULL != c->process)
+ {
+ GNUNET_break (0 ==
+ GNUNET_OS_process_kill (c->process,
+ SIGTERM));
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_OS_process_wait_status (c->process,
+ &type,
+ &code));
+ if ( (GNUNET_OS_PROCESS_EXITED != type) ||
+ (0 != code) )
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Process for exchange %s had trouble (%d/%d)\n",
+ c->base_url,
+ (int) type,
+ (int) code);
+ GNUNET_OS_process_destroy (c->process);
+ }
+ GNUNET_free (c->base_url);
+ GNUNET_free (c);
+ }
+ if (NULL != db_plugin)
{
- GNUNET_CONTAINER_DLL_remove (e_head,
- e_tail,
- es);
- GNUNET_free (es->base_url);
- GNUNET_free (es);
+ db_plugin->rollback (db_plugin->cls); /* just in case */
+ TALER_MERCHANTDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
}
- db_plugin->rollback (db_plugin->cls); /* just in case */
- TALER_MERCHANTDB_plugin_unload (db_plugin);
- db_plugin = NULL;
cfg = NULL;
if (NULL != ctx)
{
@@ -253,22 +358,21 @@ select_work (void *cls);
* Make sure to run the select_work() task at
* the @a next_deadline.
*
- * @param next_deadline deadline when work becomes ready
+ * @param deadline time when work becomes ready
*/
static void
-run_at (struct GNUNET_TIME_Absolute next_deadline)
+run_at (struct GNUNET_TIME_Absolute deadline)
{
if (GNUNET_TIME_absolute_cmp (deadline,
- <,
+ >,
next_deadline))
- {
- if (NULL != task)
- GNUNET_SCHEDULER_cancel (task);
- next_deadline = deadline;
- task = GNUNET_SCHEDULER_add_at (deadline,
- &select_work,
- NULL);
- }
+ return;
+ if (NULL != task)
+ GNUNET_SCHEDULER_cancel (task);
+ next_deadline = deadline;
+ task = GNUNET_SCHEDULER_add_at (deadline,
+ &select_work,
+ NULL);
}
@@ -282,7 +386,7 @@ static void
deposit_get_cb (void *cls,
const struct TALER_EXCHANGE_GetDepositResponse *dr)
{
- struct ExchangeState *es = cls;
+ struct ExchangeInteraction *w = cls;
switch (dr->hr.http_status)
{
@@ -293,10 +397,12 @@ deposit_get_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Exchange returned wire transfer over %s for deposited coin %s\n",
TALER_amount2s (&dr->details.ok.coin_contribution),
- TALER_B2S (&tq->coin_pub));
- qs = TMH_db->insert_deposit_to_transfer (TMH_db->cls,
- tq->deposit_serial,
- &dr->details.ok);
+ TALER_B2S (&w->coin_pub));
+ // FIXME: this must filter this particular entry from the
+ // select below for good!
+ qs = db_plugin->insert_deposit_to_transfer (db_plugin->cls,
+ w->deposit_serial,
+ &dr->details.ok);
if (qs < 0)
{
GNUNET_break (0);
@@ -316,13 +422,16 @@ deposit_get_cb (void *cls,
"Exchange returned KYC requirement (%d/%d) for deposited coin %s\n",
dr->details.accepted.kyc_ok,
dr->details.accepted.aml_decision,
- TALER_B2S (&tq->coin_pub));
+ TALER_B2S (&w->coin_pub));
now = GNUNET_TIME_timestamp_get ();
- qs = TMH_db->account_kyc_set_status (
- TMH_db->cls,
- gorc->hc->instance->settings.id,
- &tq->h_wire,
- tq->exchange_url,
+ // FIXME: this must filter this particular entry from the
+ // select below, at least until the KYC/AML request is
+ // satisfied; how will we learn that?
+ qs = db_plugin->account_kyc_set_status (
+ db_plugin->cls,
+ w->instance_id,
+ &w->h_wire,
+ exchange_url,
dr->details.accepted.requirement_row,
NULL,
NULL,
@@ -340,8 +449,9 @@ deposit_get_cb (void *cls,
default:
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Exchange returned tracking failure for deposited coin %s\n",
- TALER_B2S (&tq->coin_pub));
+ "Exchange %s returned tracking failure for deposited coin %s\n",
+ exchange_url,
+ TALER_B2S (&w->coin_pub));
/* FIXME: how to handle? */
return;
}
@@ -350,107 +460,26 @@ deposit_get_cb (void *cls,
GNUNET_CONTAINER_DLL_remove (w_head,
w_tail,
w);
+ w_count--;
GNUNET_free (w);
- // FIXME: not the best condition to go for more work,
- // one down exchange would halt us entirely!
- if (NULL == w_head)
+ if ( (w_count < CONCURRENCY_LIMIT / 2) ||
+ (0 == w_count) )
task = GNUNET_SCHEDULER_add_now (&select_work,
NULL);
}
/**
- * Initiate request with the exchange about deposits.
- *
- * @param[in,out] exchange interaction handle
- */
-static void
-inquire_at_exchange (struct ExchangeInteraction *w)
-{
- struct ExchangeState *es = w->es;
-
- GNUNET_assert (NULL == w->dgh);
- w->dgh = TALER_EXCHANGE_deposits_get (
- ctx,
- es->exchange_url,
- es->keys,
- &w->merchant_priv,
- &w->h_wire,
- &w->h_contract_terms,
- &w->coin_pub,
- GNUNET_TIME_UNIT_ZERO,
- &deposit_get_cb,
- w);
-}
-
-
-/**
- * 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
- * the callee, thus it is given explicitly and not
- * (only) via @a kr.
- *
- * @param cls closure
- * @param kr response from /keys
- * @param[in] keys keys object passed to callback with
- * reference counter of 1. Must be freed by callee
- * using #TALER_EXCHANGE_keys_decref(). NULL on failure.
- */
-static void
-keys_cb (
- void *cls,
- const struct TALER_EXCHANGE_KeysResponse *kr,
- struct TALER_EXCHANGE_Keys *keys)
-{
- struct ExchangeState *es = cls;
-
- es->gkh = NULL;
- if (NULL == keys)
- return;
- if (NULL != es->keys)
- TALER_EXCHANGE_keys_decref (keys);
- es->keys = TALER_EXCHANGE_keys_incref (keys);
- /* Trigger all deposits blocked on fetching /keys */
- for (struct ExchangeInteraction *w = w_head;
- NULL != w;
- w = w->next)
- {
- if (w->es != es)
- continue;
- if (NULL != w->dgh)
- continue;
- inquire_at_exchange (w);
- }
-}
-
-
-/**
- * Download /keys from an exchange.
- *
- * @param[in,out] es exchange state
- */
-static void
-fetch_keys (struct ExchangeState *es)
-{
- GNUNET_assert (NULL == es->gkh);
- es->gkh = TALER_EXCHANGE_get_keys (ctx,
- es->base_url,
- es->keys,
- &keys_cb,
- es);
-}
-
-
-/**
* Typically called by `select_work`.
*
* @param cls NULL
* @param deposit_serial identifies the deposit operation
- * @param exchange_url URL of the exchange that issued @a coin_pub
- * @param amount_with_fee amount the exchange will deposit for this coin
- * @param deposit_fee fee the exchange will charge for this coin
- * @param h_wire hash of the merchant's wire account into which the deposit was made
+ * @param wire_deadline when is the wire due
+ * @param h_contract_terms hash of the contract terms
+ * @param merchant_priv private key of the merchant
+ * @param instance_id row ID of the instance
+ * @param h_wire hash of the merchant's wire account into * @param amount_with_fee amount the exchange will deposit for this coin
+ * @param deposit_fee fee the exchange will charge for this coin which the deposit was made
* @param coin_pub public key of the deposited coin
*/
static void
@@ -458,16 +487,15 @@ pending_deposits_cb (
void *cls,
uint64_t deposit_serial,
struct GNUNET_TIME_Absolute wire_deadline, /* missing in DB! Funky migration needed! */
- const char *exchange_url,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ const char *instance_id,
const struct TALER_MerchantWireHashP *h_wire,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
struct ExchangeInteraction *w = GNUNET_new (struct ExchangeInteraction);
- struct ExchangeState *es = NULL;
(void) cls;
if (GNUNET_TIME_absolute_is_future (wire_deadline))
@@ -475,6 +503,7 @@ pending_deposits_cb (
run_at (wire_deadline);
return;
}
+ w->deposit_serial = deposit_serial;
w->wire_deadline = wire_deadline;
w->h_contract_terms = *h_contract_terms;
w->merchant_priv = *merchant_priv;
@@ -482,29 +511,30 @@ pending_deposits_cb (
w->amount_with_fee = *amount_with_fee;
w->deposit_fee = *deposit_fee;
w->coin_pub = *coin_pub;
+ w->instance_id = GNUNET_strdup (instance_id);
GNUNET_CONTAINER_DLL_insert (w_head,
w_tail,
w);
- for (es = e_head; NULL != es; es = es->next)
- if (0 == strcmp (exchange_url,
- es->base_url))
- break;
- if (NULL == es)
+ w_count++;
+ GNUNET_assert (NULL != keys);
+ if (GNUNET_TIME_absolute_is_past (keys->key_data_expiration.abs_time))
{
- es = GNUNET_new (struct ExchangeState);
- es->base_url = GNUNET_strdup (exchange_url);
- GNUNET_CONTAINER_DLL_insert (e_head,
- e_tail,
- es);
- }
- w->es = es;
- if ( (NULL == es->keys) ||
- (GNUNET_TIME_absolute_is_past (es->keys->key_data_expiration)) )
- {
- fetch_keys (es);
+ /* Parent should re-start us, then we will re-fetch /keys */
+ GNUNET_SCHEDULER_shutdown ();
return;
}
- inquire_at_exchange (w);
+ GNUNET_assert (NULL == w->dgh);
+ w->dgh = TALER_EXCHANGE_deposits_get (
+ ctx,
+ exchange_url,
+ keys,
+ &w->merchant_priv,
+ &w->h_wire,
+ &w->h_contract_terms,
+ &w->coin_pub,
+ GNUNET_TIME_UNIT_ZERO,
+ &deposit_get_cb,
+ w);
}
@@ -529,6 +559,8 @@ db_notify (void *cls,
GNUNET_break (0);
return;
}
+ if (0 != w_count)
+ return; /* already at work! */
memcpy (&nbo_deadline,
extra,
extra_size);
@@ -541,18 +573,35 @@ static void
select_work (void *cls)
{
bool retry = false;
+ uint32_t limit = CONCURRENCY_LIMIT - w_count;
(void) cls;
task = NULL;
+ GNUNET_assert (NULL != keys);
+ if (GNUNET_TIME_absolute_is_past (keys->key_data_expiration.abs_time))
+ {
+ /* Parent should re-start us, then we will re-fetch /keys */
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
while (1)
{
- struct GNUNET_TIME_Absolute now;
enum GNUNET_DB_QueryStatus qs;
- now = GNUNET_TIME_absolute_get ();
db_plugin->preflight (db_plugin->cls);
+ // NOTE:
+ // SQL must filter all deposits for accounts
+ // that are already KYC/AML-blocked!
+ // (FIXME: How do we then learn about KYC unblocking?)
+ // Usually, select up to limit
+ // deposits with wire deadlines < now,
+ // or *1* deposit with smallest wire deadline if
+ // retry is 'true'.
+ if (retry)
+ limit = 1;
qs = db_plugin->lookup_pending_deposits (db_plugin->cls,
- now,
+ exchange_url,
+ limit,
retry,
&pending_deposits_cb,
NULL);
@@ -582,6 +631,178 @@ select_work (void *cls)
/**
+ * 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
+ * the callee, thus it is given explicitly and not
+ * (only) via @a kr.
+ *
+ * @param cls closure, NULL
+ * @param kr response from /keys
+ * @param[in] keys keys object passed to callback with
+ * reference counter of 1. Must be freed by callee
+ * using #TALER_EXCHANGE_keys_decref(). NULL on failure.
+ */
+static void
+keys_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_KeysResponse *kr,
+ struct TALER_EXCHANGE_Keys *keys)
+{
+ gkh = NULL;
+ if (NULL == keys)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to download %skeys\n",
+ exchange_url);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ keys = TALER_EXCHANGE_keys_incref (keys);
+ GNUNET_assert (NULL == task);
+ task = GNUNET_SCHEDULER_add_now (&select_work,
+ NULL);
+}
+
+
+/**
+ * Start a copy of this process with the exchange URL
+ * set to the given @a base_url
+ *
+ * @param base_url base URL to run with
+ */
+static struct GNUNET_OS_Process *
+start_worker (const char *base_url)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Launching worker for exchange `%s'\n",
+ base_url);
+ return GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
+ NULL,
+ NULL,
+ NULL,
+ "taler-merchant-depositcheck",
+ "-c", cfg_filename,
+ "-e", base_url,
+ NULL);
+}
+
+
+/**
+ * Restart worker process for the given child.
+ *
+ * @param cls a `struct Child *` that needs a worker.
+ */
+static void
+restart_child (void *cls);
+
+
+/**
+ * Function called upon death or completion of a child process.
+ *
+ * @param cls a `struct Child *`
+ * @param type type of the process
+ * @param exit_code status code of the process
+ */
+static void
+child_done_cb (void *cls,
+ enum GNUNET_OS_ProcessStatusType type,
+ long unsigned int exit_code)
+{
+ struct Child *c = cls;
+
+ c->cwh = NULL;
+ if ( (GNUNET_OS_PROCESS_EXITED != type) ||
+ (0 != exit_code) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Process for exchange %s had trouble (%d/%d)\n",
+ c->base_url,
+ (int) type,
+ (int) exit_code);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = 1;
+ return;
+ }
+ GNUNET_OS_process_destroy (c->process);
+ if (GNUNET_TIME_absolute_is_future (c->next_start))
+ c->rd = GNUNET_TIME_STD_BACKOFF (c->rd);
+ else
+ c->rd = GNUNET_TIME_UNIT_SECONDS;
+ c->rt = GNUNET_SCHEDULER_add_at (c->next_start,
+ &restart_child,
+ c);
+}
+
+
+static void
+restart_child (void *cls)
+{
+ struct Child *c = cls;
+
+ c->rt = NULL;
+ c->next_start = GNUNET_TIME_relative_to_absolute (c->rd);
+ c->process = start_worker (c->base_url);
+ if (NULL == c->process)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "exec");
+ global_ret = 1;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ c->cwh = GNUNET_wait_child (c->process,
+ &child_done_cb,
+ c);
+}
+
+
+/**
+ * Function to iterate over section.
+ *
+ * @param cls closure
+ * @param section name of the section
+ */
+static void
+cfg_iter_cb (void *cls,
+ const char *section)
+{
+ char *base_url;
+ struct Child *c;
+
+ if (0 !=
+ strncasecmp (section,
+ "merchant-exchange-",
+ strlen ("merchant-exchange-")))
+ return;
+ if (GNUNET_YES ==
+ GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ section,
+ "DISABLED"))
+ return;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "EXCHANGE_BASE_URL",
+ &base_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ section,
+ "EXCHANGE_BASE_URL");
+ return;
+ }
+ c = GNUNET_new (struct Child);
+ c->rd = GNUNET_TIME_UNIT_SECONDS;
+ c->base_url = base_url;
+ GNUNET_CONTAINER_DLL_insert (c_head,
+ c_tail,
+ c);
+ c->rt = GNUNET_SCHEDULER_add_now (&restart_child,
+ c);
+}
+
+
+/**
* First task.
*
* @param cls closure, NULL
@@ -596,11 +817,25 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *c)
{
(void) args;
- (void) cfgfile;
cfg = c;
+ cfg_filename = cfgfile;
GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
NULL);
+ if (NULL == exchange_url)
+ {
+ GNUNET_CONFIGURATION_iterate_sections (c,
+ &cfg_iter_cb,
+ NULL);
+ if (NULL == c_head)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No exchanges found in configuration\n");
+ return;
+ }
+ return;
+ }
+
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);
@@ -616,6 +851,7 @@ run (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to initialize DB subsystem\n");
GNUNET_SCHEDULER_shutdown ();
+ global_ret = 1;
return;
}
if (GNUNET_OK !=
@@ -624,6 +860,7 @@ run (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to connect to database\n");
GNUNET_SCHEDULER_shutdown ();
+ global_ret = 1;
return;
}
{
@@ -638,9 +875,11 @@ run (void *cls,
&db_notify,
NULL);
}
- GNUNET_assert (NULL == task);
- task = GNUNET_SCHEDULER_add_now (&select_work,
- NULL);
+ gkh = TALER_EXCHANGE_get_keys (ctx,
+ exchange_url,
+ NULL,
+ &keys_cb,
+ NULL);
}
@@ -656,6 +895,11 @@ main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_string ('e',
+ "exchange",
+ "BASE_URL",
+ "limit us to checking deposits of this exchange",
+ &exchange_url),
GNUNET_GETOPT_option_flag ('t',
"test",
"run in test mode and exit when idle",
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index 1818a877..8681d0ec 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -79,6 +79,7 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
pg_select_account_by_uri.h pg_select_account_by_uri.c \
pg_lookup_reserves.h pg_lookup_reserves.c \
pg_lookup_instance_auth.h pg_lookup_instance_auth.c \
+ pg_lookup_pending_deposits.h pg_lookup_pending_deposits.c \
pg_insert_instance.h pg_insert_instance.c \
pg_account_kyc_set_status.h pg_account_kyc_set_status.c \
pg_account_kyc_get_status.h pg_account_kyc_get_status.c \
diff --git a/src/backenddb/pg_lookup_pending_deposits.c b/src/backenddb/pg_lookup_pending_deposits.c
new file mode 100644
index 00000000..411b7eb5
--- /dev/null
+++ b/src/backenddb/pg_lookup_pending_deposits.c
@@ -0,0 +1,40 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_lookup_pending_deposits.c
+ * @brief Implementation of the lookup_pending_deposits function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_lookup_pending_deposits.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_pending_deposits (
+ void *cls,
+ const char *exchange_url,
+ uint32_t limit,
+ bool allow_future,
+ TALER_MERCHANTDB_PendingDepositsCallback cb,
+ void *cb_cls)
+{
+ GNUNET_break (0);
+ return -2; // FIXME!
+}
diff --git a/src/backenddb/pg_lookup_pending_deposits.h b/src/backenddb/pg_lookup_pending_deposits.h
new file mode 100644
index 00000000..47cadfac
--- /dev/null
+++ b/src/backenddb/pg_lookup_pending_deposits.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_lookup_pending_deposits.h
+ * @brief implementation of the lookup_pending_deposits function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_PENDING_DEPOSITS_H
+#define PG_LOOKUP_PENDING_DEPOSITS_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Lookup deposits that are finished and awaiting a wire transfer.
+ *
+ * @param cls closure
+ * @param exchange_url exchange to filter deposits by
+ * @param limit maximum number of deposits to return
+ * @param allow_future true to allow deposits with wire deadline in the future
+ * @param cb function to call with deposit data
+ * @param cb_cls closure for @a cb
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_pending_deposits (
+ void *cls,
+ const char *exchange_url,
+ uint32_t limit,
+ bool allow_future,
+ TALER_MERCHANTDB_PendingDepositsCallback cb,
+ void *cb_cls);
+
+
+#endif
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index 0e538ba9..e57e617f 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -43,6 +43,7 @@
#include "pg_update_account.h"
#include "pg_lookup_instances.h"
#include "pg_lookup_transfers.h"
+#include "pg_lookup_pending_deposits.h"
#include "pg_update_wirewatch_progress.h"
#include "pg_select_wirewatch_accounts.h"
#include "pg_select_open_transfers.h"
@@ -582,6 +583,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
= &TMH_PG_insert_webhook;
plugin->update_webhook
= &TMH_PG_update_webhook;
+ plugin->lookup_pending_deposits
+ = &TMH_PG_lookup_pending_deposits;
plugin->lookup_webhook_by_event
= &TMH_PG_lookup_webhook_by_event;
plugin->lookup_all_webhooks
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
index 7e2b748c..8635c350 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -796,6 +796,33 @@ typedef void
/**
+ * Callback for results from `lookup_pending_deposits`.
+ *
+ * @param cls NULL
+ * @param deposit_serial identifies the deposit operation
+ * @param wire_deadline when is the wire due
+ * @param h_contract_terms hash of the contract terms
+ * @param merchant_priv private key of the merchant
+ * @param instance_id name of the instance
+ * @param h_wire hash of the merchant's wire account into * @param amount_with_fee amount the exchange will deposit for this coin
+ * @param deposit_fee fee the exchange will charge for this coin which the deposit was made
+ * @param coin_pub public key of the deposited coin
+ */
+typedef void
+(*TALER_MERCHANTDB_PendingDepositsCallback) (
+ void *cls,
+ uint64_t deposit_serial,
+ struct GNUNET_TIME_Absolute wire_deadline, /* missing in DB! Funky migration needed! */
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ const char *instance_id,
+ const struct TALER_MerchantWireHashP *h_wire,
+ const struct TALER_Amount *amount_with_fee,
+ const struct TALER_Amount *deposit_fee,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub);
+
+
+/**
* Function called with detailed information about a wire transfer and
* the underlying deposits that are being aggregated.
*
@@ -3629,11 +3656,11 @@ struct TALER_MERCHANTDB_Plugin
* @return database result code
*/
enum GNUNET_DB_QueryStatus
- (*update_token_family)(void *cls,
- const char *instance_id,
- const char *token_family_slug,
- const struct
- TALER_MERCHANTDB_TokenFamilyDetails *details);
+ (*update_token_family)(
+ void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *details);
/**
@@ -3646,11 +3673,31 @@ struct TALER_MERCHANTDB_Plugin
* @return database result code
*/
enum GNUNET_DB_QueryStatus
- (*insert_token_family)(void *cls,
- const char *instance_id,
- const char *token_family_slug,
- const struct
- TALER_MERCHANTDB_TokenFamilyDetails *details);
+ (*insert_token_family)(
+ void *cls,
+ const char *instance_id,
+ const char *token_family_slug,
+ const struct TALER_MERCHANTDB_TokenFamilyDetails *details);
+
+ /**
+ * Lookup deposits that are finished and awaiting a wire transfer.
+ *
+ * @param cls closure
+ * @param exchange_url exchange to filter deposits by
+ * @param limit maximum number of deposits to return
+ * @param allow_future true to allow deposits with wire deadline in the future
+ * @param cb function to call with deposit data
+ * @param cb_cls closure for @a cb
+ * @return transaction status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_pending_deposits)(
+ void *cls,
+ const char *exchange_url,
+ uint32_t limit,
+ bool allow_future,
+ TALER_MERCHANTDB_PendingDepositsCallback cb,
+ void *cb_cls);
};