exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 12b8220f543ca16c3827e2fd63fd6ae1b3b72222
parent 56fa76fc383b1808cd77c35ecc8c1df5bf175e88
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 13 Aug 2026 20:09:38 +0200

handle NULL in reserve_close operations

Diffstat:
Msrc/exchange/taler-exchange-closer.c | 17+++++++++++++++--
Msrc/exchangedb/iterate_unfinished_close_requests.c | 23++++++++++++++++-------
2 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c @@ -209,8 +209,8 @@ commit_or_warn (void) * @param cls NULL * @param reserve_pub public key of the reserve * @param left amount left in the reserve - * @param account_payto_uri information about the bank account that initially - * caused the reserve to be created + * @param account_payto_uri the bank account the balance is to be wired to, + * NULL if no bank account is known for the reserve at all * @param expiration_date when did the reserve expire * @param close_request_row row of request asking for * closure, 0 for expired reserves @@ -242,6 +242,19 @@ expired_reserve_cb (void *cls, GNUNET_TIME_timestamp2s (expiration_date)); now = GNUNET_TIME_timestamp_get (); + if (NULL == account_payto_uri.full_payto) + { + /* Neither the close request nor the reserve names a bank account, so + there is nowhere to wire the balance to. Skip the request -- it + remains marked as processed -- instead of failing the entire run, + which would block the closing of all other reserves indefinitely. */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No bank account known for reserve %s, cannot close it;" + " manual intervention required\n", + TALER_B2S (reserve_pub)); + return GNUNET_OK; + } + /* lookup account we should use */ wa = TALER_EXCHANGEDB_find_account_by_payto_uri (account_payto_uri); if (NULL == wa) diff --git a/src/exchangedb/iterate_unfinished_close_requests.c b/src/exchangedb/iterate_unfinished_close_requests.c @@ -77,8 +77,10 @@ reserve_cb (void *cls, struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_timestamp ("expiration_date", &exp_date), - GNUNET_PQ_result_spec_string ("account_details", - &account_details.full_payto), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("account_details", + &account_details.full_payto), + NULL), GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", &reserve_pub), TALER_PQ_RESULT_SPEC_AMOUNT ("close", @@ -88,6 +90,9 @@ reserve_cb (void *cls, GNUNET_PQ_result_spec_end }; + /* If neither the close request nor the reserve identifies a bank + account, the callback is given a NULL payto URI. */ + account_details.full_payto = NULL; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, @@ -138,11 +143,15 @@ TALER_EXCHANGEDB_iterate_unfinished_close_requests ( " ,close_request_serial_id" " ,close_timestamp AS expiration_date" " ,close" - " ,(SELECT payto_uri" - " FROM reserves_in ri" - " JOIN wire_targets wt" - " ON (ri.wire_source_h_payto = wt.wire_target_h_payto)" - " WHERE ri.reserve_pub=rc.reserve_pub)" + /* The account designated by the client (and covered by the KYC + check done at the time of the request) takes precedence over + the account the reserve was funded from. */ + " ,COALESCE(rc.payto_uri" + " ,(SELECT payto_uri" + " FROM reserves_in ri" + " JOIN wire_targets wt" + " ON (ri.wire_source_h_payto = wt.wire_target_h_payto)" + " WHERE ri.reserve_pub=rc.reserve_pub))" " AS account_details;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "iterate_unfinished_close_requests",