From c898a1e13b1f06dafec35051dfd232510bd28de3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 15 Mar 2020 20:42:47 +0100 Subject: [PATCH] clean up closer logic, improve error handling, simplify logic, add comments --- src/exchange/taler-exchange-closer.c | 70 ++++++++-------- src/exchange/taler-exchange-transfer.c | 109 +++++++++++++++---------- src/util/amount.c | 7 ++ 3 files changed, 110 insertions(+), 76 deletions(-) diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c index d770197d0..b4faffb48 100644 --- a/src/exchange/taler-exchange-closer.c +++ b/src/exchange/taler-exchange-closer.c @@ -63,10 +63,25 @@ static struct GNUNET_SCHEDULER_Task *task; static struct GNUNET_TIME_Relative aggregator_idle_sleep_interval; /** - * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR + * Value to return from main(). 0 on success, non-zero * on serious errors. */ -static int global_ret; +static enum +{ + GR_SUCCESS = 0, + GR_WIRE_ACCOUNT_NOT_CONFIGURED = 1, + GR_WIRE_TRANSFER_FEES_NOT_CONFIGURED = 2, + GR_FAILURE_TO_ROUND_AMOUNT = 3, + GR_DATABASE_INSERT_HARD_FAIL = 4, + GR_DATABASE_SELECT_HARD_FAIL = 5, + GR_DATABASE_COMMIT_HARD_FAIL = 6, + GR_DATABASE_SESSION_START_FAIL = 7, + GR_DATABASE_TRANSACTION_BEGIN_FAIL = 8, + GR_CONFIGURATION_INVALID = 9, + GR_CMD_LINE_UTF8_ERROR = 10, + GR_CMD_LINE_OPTIONS_WRONG = 11, + GR_INVALID_PAYTO_ENCOUNTERED = 12, +} global_ret; /** * #GNUNET_YES if we are in test mode and should exit when idle. @@ -113,7 +128,7 @@ shutdown_task (void *cls) * @return #GNUNET_OK on success */ static int -parse_wirewatch_config () +parse_wirewatch_config (void) { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, @@ -204,11 +219,6 @@ struct ExpiredReserveContext */ struct TALER_EXCHANGEDB_Session *session; - /** - * Set to #GNUNET_YES if the transaction continues - * asynchronously. - */ - int async_cont; }; @@ -258,7 +268,7 @@ expired_reserve_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No wire account configured to deal with target URI `%s'\n", account_payto_uri); - global_ret = GNUNET_SYSERR; + global_ret = GR_WIRE_ACCOUNT_NOT_CONFIGURED; GNUNET_SCHEDULER_shutdown (); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -275,7 +285,7 @@ expired_reserve_cb (void *cls, session); if (NULL == af) { - global_ret = GNUNET_SYSERR; + global_ret = GR_WIRE_TRANSFER_FEES_NOT_CONFIGURED; GNUNET_SCHEDULER_shutdown (); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -302,7 +312,7 @@ expired_reserve_cb (void *cls, ¤cy_round_unit)) { GNUNET_break (0); - global_ret = GNUNET_SYSERR; + global_ret = GR_FAILURE_TO_ROUND_AMOUNT; GNUNET_SCHEDULER_shutdown (); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -341,7 +351,7 @@ expired_reserve_cb (void *cls, (GNUNET_DB_STATUS_HARD_ERROR == qs) ) { GNUNET_break (0); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_INSERT_HARD_FAIL; GNUNET_SCHEDULER_shutdown (); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -352,20 +362,14 @@ expired_reserve_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Reserve was virtually empty, moving on\n"); (void) commit_or_warn (session); - erc->async_cont = GNUNET_YES; - GNUNET_assert (NULL == task); - task = GNUNET_SCHEDULER_add_now (&run_reserve_closures, - NULL); return qs; } /* success, perform wire transfer */ { - char *method; void *buf; size_t buf_size; - method = TALER_payto_get_method (account_payto_uri); TALER_BANK_prepare_transfer (account_payto_uri, &amount_without_fee, exchange_base_url, @@ -375,15 +379,16 @@ expired_reserve_cb (void *cls, /* Commit our intention to execute the wire transfer! */ qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, session, - method, + wa->method, buf, buf_size); GNUNET_free (buf); - GNUNET_free (method); } if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_break (0); + global_ret = GR_DATABASE_INSERT_HARD_FAIL; + GNUNET_SCHEDULER_shutdown (); return GNUNET_DB_STATUS_HARD_ERROR; } if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -391,10 +396,6 @@ expired_reserve_cb (void *cls, /* start again */ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } - erc->async_cont = GNUNET_YES; - GNUNET_assert (NULL == task); - task = GNUNET_SCHEDULER_add_now (&run_reserve_closures, - NULL); return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } @@ -419,7 +420,7 @@ run_reserve_closures (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to obtain database session!\n"); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_SESSION_START_FAIL; GNUNET_SCHEDULER_shutdown (); return; } @@ -431,12 +432,11 @@ run_reserve_closures (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start database transaction!\n"); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_TRANSACTION_BEGIN_FAIL; GNUNET_SCHEDULER_shutdown (); return; } erc.session = session; - erc.async_cont = GNUNET_NO; now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -454,7 +454,7 @@ run_reserve_closures (void *cls) GNUNET_break (0); db_plugin->rollback (db_plugin->cls, session); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_SELECT_HARD_FAIL; GNUNET_SCHEDULER_shutdown (); return; case GNUNET_DB_STATUS_SOFT_ERROR: @@ -483,8 +483,6 @@ run_reserve_closures (void *cls) return; case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: (void) commit_or_warn (session); - if (GNUNET_YES == erc.async_cont) - break; GNUNET_assert (NULL == task); task = GNUNET_SCHEDULER_add_now (&run_reserve_closures, NULL); @@ -494,7 +492,9 @@ run_reserve_closures (void *cls) /** - * First task. + * First task. Parses the configuration and starts the + * main loop of #run_reserve_closures(). Also schedules + * the #shutdown_task() to clean up. * * @param cls closure, NULL * @param args remaining command-line arguments @@ -515,7 +515,7 @@ run (void *cls, if (GNUNET_OK != parse_wirewatch_config ()) { cfg = NULL; - global_ret = 1; + global_ret = GR_CONFIGURATION_INVALID; return; } GNUNET_assert (NULL == task); @@ -531,7 +531,7 @@ run (void *cls, * * @param argc number of arguments from the command line * @param argv command line arguments - * @return 0 ok, 1 on error + * @return 0 ok, non-zero on error */ int main (int argc, @@ -551,7 +551,7 @@ main (int argc, if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; + return GR_CMD_LINE_UTF8_ERROR; if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "taler-exchange-closer", @@ -561,7 +561,7 @@ main (int argc, &run, NULL)) { GNUNET_free ((void *) argv); - return 1; + return GR_CMD_LINE_OPTIONS_WRONG; } GNUNET_free ((void *) argv); return global_ret; diff --git a/src/exchange/taler-exchange-transfer.c b/src/exchange/taler-exchange-transfer.c index a1d2d0596..1793dc988 100644 --- a/src/exchange/taler-exchange-transfer.c +++ b/src/exchange/taler-exchange-transfer.c @@ -13,7 +13,6 @@ You should have received a copy of the GNU Affero General Public License along with TALER; see the file COPYING. If not, see */ - /** * @file taler-exchange-transfer.c * @brief Process that actually finalizes outgoing transfers with the wire gateway / bank @@ -98,10 +97,23 @@ static struct GNUNET_CURL_RescheduleContext *rc; static struct GNUNET_TIME_Relative aggregator_idle_sleep_interval; /** - * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR - * on serious errors. + * Value to return from main(). 0 on success, non-zero on errors. */ -static int global_ret; +static enum +{ + GR_SUCCESS = 0, + GR_WIRE_TRANSFER_FAILED = 1, + GR_DATABASE_COMMIT_HARD_FAIL = 2, + GR_INVARIANT_FAILURE = 3, + GR_WIRE_ACCOUNT_NOT_CONFIGURED = 4, + GR_WIRE_TRANSFER_BEGIN_FAIL = 5, + GR_DATABASE_TRANSACTION_BEGIN_FAIL = 6, + GR_DATABASE_SESSION_START_FAIL = 7, + GR_CONFIGURATION_INVALID = 8, + GR_CMD_LINE_UTF8_ERROR = 9, + GR_CMD_LINE_OPTIONS_WRONG = 10, + GR_DATABASE_FETCH_FAILURE = 11, +} global_ret; /** * #GNUNET_YES if we are in test mode and should exit when idle. @@ -109,16 +121,6 @@ static int global_ret; static int test_mode; -/** - * Execute the wire transfers that we have committed to - * do. - * - * @param cls NULL - */ -static void -run_transfers (void *cls); - - /** * We're being aborted with CTRL-C (or SIGTERM). Shut down. * @@ -170,7 +172,7 @@ shutdown_task (void *cls) * @return #GNUNET_OK on success */ static int -parse_wirewatch_config () +parse_wirewatch_config (void) { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, @@ -226,8 +228,21 @@ commit_or_warn (struct TALER_EXCHANGEDB_Session *session) } +/** + * Execute the wire transfers that we have committed to + * do. + * + * @param cls NULL + */ +static void +run_transfers (void *cls); + + /** * Function called with the result from the execute step. + * On success, we mark the respective wire transfer as finished, + * and in general we afterwards continue to #run_transfers(), + * except for irrecoverable errors. * * @param cls NULL * @param http_status_code #MHD_HTTP_OK on success @@ -257,7 +272,7 @@ wire_confirm_cb (void *cls, ec); db_plugin->rollback (db_plugin->cls, session); - global_ret = GNUNET_SYSERR; + global_ret = GR_WIRE_TRANSFER_FAILED; GNUNET_SCHEDULER_shutdown (); GNUNET_free (wpd); wpd = NULL; @@ -280,7 +295,7 @@ wire_confirm_cb (void *cls, } else { - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_COMMIT_HARD_FAIL; GNUNET_SCHEDULER_shutdown (); } GNUNET_free (wpd); @@ -299,7 +314,7 @@ wire_confirm_cb (void *cls, return; case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_break (0); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_COMMIT_HARD_FAIL; GNUNET_SCHEDULER_shutdown (); return; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: @@ -313,7 +328,7 @@ wire_confirm_cb (void *cls, return; default: GNUNET_break (0); - global_ret = GNUNET_SYSERR; + global_ret = GR_INVARIANT_FAILURE; GNUNET_SCHEDULER_shutdown (); return; } @@ -321,7 +336,8 @@ wire_confirm_cb (void *cls, /** - * Callback with data about a prepared transaction. + * Callback with data about a prepared transaction. Triggers the respective + * wire transfer using the prepared transaction data. * * @param cls NULL * @param rowid row identifier used to mark prepared transaction as done @@ -339,6 +355,15 @@ wire_prepare_cb (void *cls, struct TALER_EXCHANGEDB_WireAccount *wa; (void) cls; + if ( (NULL == wire_method) || + (NULL == buf) ) + { + GNUNET_break (0); + db_plugin->rollback (db_plugin->cls, + wpd->session); + global_ret = GR_DATABASE_FETCH_FAILURE; + goto cleanup; + } wpd->row_id = rowid; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting wire transfer %llu\n", @@ -351,11 +376,8 @@ wire_prepare_cb (void *cls, GNUNET_break (0); db_plugin->rollback (db_plugin->cls, wpd->session); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (wpd); - wpd = NULL; - return; + global_ret = GR_WIRE_ACCOUNT_NOT_CONFIGURED; + goto cleanup; } wa = wpd->wa; wpd->eh = TALER_BANK_transfer (ctx, @@ -369,12 +391,14 @@ wire_prepare_cb (void *cls, GNUNET_break (0); /* Irrecoverable */ db_plugin->rollback (db_plugin->cls, wpd->session); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (wpd); - wpd = NULL; - return; + global_ret = GR_WIRE_TRANSFER_BEGIN_FAIL; + goto cleanup; } + return; +cleanup: + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (wpd); + wpd = NULL; } @@ -398,7 +422,7 @@ run_transfers (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to obtain database session!\n"); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_SESSION_START_FAIL; GNUNET_SCHEDULER_shutdown (); return; } @@ -409,7 +433,7 @@ run_transfers (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start database transaction!\n"); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_TRANSACTION_BEGIN_FAIL; GNUNET_SCHEDULER_shutdown (); return; } @@ -429,7 +453,7 @@ run_transfers (void *cls) { case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_break (0); - global_ret = GNUNET_SYSERR; + global_ret = GR_DATABASE_COMMIT_HARD_FAIL; GNUNET_SCHEDULER_shutdown (); return; case GNUNET_DB_STATUS_SOFT_ERROR: @@ -440,15 +464,17 @@ run_transfers (void *cls) return; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: /* no more prepared wire transfers, go sleep a bit! */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No more pending wire transfers, going idle\n"); GNUNET_assert (NULL == task); if (GNUNET_YES == test_mode) { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No more pending wire transfers, shutting down (because we are in test mode)\n"); GNUNET_SCHEDULER_shutdown (); } else { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No more pending wire transfers, going idle\n"); task = GNUNET_SCHEDULER_add_delayed (aggregator_idle_sleep_interval, &run_transfers, NULL); @@ -483,7 +509,7 @@ run (void *cls, if (GNUNET_OK != parse_wirewatch_config ()) { cfg = NULL; - global_ret = 1; + global_ret = GR_CONFIGURATION_INVALID; return; } ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, @@ -525,19 +551,20 @@ main (int argc, GNUNET_GETOPT_OPTION_END }; - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, - &argc, &argv)) - return 2; + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return GR_CMD_LINE_UTF8_ERROR; if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, - "taler-exchange-transfers", + "taler-exchange-transfer", gettext_noop ( "background process that executes outgoing wire transfers"), options, &run, NULL)) { GNUNET_free ((void *) argv); - return 1; + return GR_CMD_LINE_OPTIONS_WRONG; } GNUNET_free ((void *) argv); return global_ret; diff --git a/src/util/amount.c b/src/util/amount.c index e443a0d24..fb5a41d74 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -709,6 +709,13 @@ int TALER_amount_round_down (struct TALER_Amount *amount, const struct TALER_Amount *round_unit) { + if (GNUNET_OK != + TALER_amount_cmp_currency (amount, + round_unit)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } if ( (0 != round_unit->fraction) && (0 != round_unit->value) ) {