From 1788ca2be11b92f9c92d8b7ad31383f663608ac0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 12 Jan 2020 20:44:33 +0100 Subject: reorganization of file structure --- src/bank-lib/Makefile.am | 88 +- src/bank-lib/bank.conf | 13 - src/bank-lib/bank_twisted.conf | 39 - src/bank-lib/test_bank_api_twisted.c | 222 ---- src/bank-lib/testing_api_cmd_admin_add_incoming.c | 609 ----------- src/bank-lib/testing_api_cmd_history_credit.c | 765 -------------- src/bank-lib/testing_api_cmd_history_debit.c | 766 -------------- src/bank-lib/testing_api_cmd_transfer.c | 394 ------- src/bank-lib/testing_api_helpers.c | 361 ------- src/lib/Makefile.am | 145 ++- src/lib/bank.conf | 13 + src/lib/bank_twisted.conf | 39 + src/lib/test_bank_api_twisted.c | 222 ++++ .../testing_api_cmd_auditor_deposit_confirmation.c | 444 ++++++++ src/lib/testing_api_cmd_auditor_exchanges.c | 358 +++++++ src/lib/testing_api_cmd_auditor_exec_auditor.c | 163 +++ .../testing_api_cmd_auditor_exec_auditor_dbinit.c | 164 +++ .../testing_api_cmd_auditor_exec_wire_auditor.c | 163 +++ src/lib/testing_api_cmd_bank_admin_add_incoming.c | 609 +++++++++++ src/lib/testing_api_cmd_bank_history_credit.c | 765 ++++++++++++++ src/lib/testing_api_cmd_bank_history_debit.c | 766 ++++++++++++++ src/lib/testing_api_cmd_bank_transfer.c | 394 +++++++ src/lib/testing_api_helpers.c | 1080 -------------------- src/lib/testing_api_helpers_auditor.c | 232 +++++ src/lib/testing_api_helpers_bank.c | 361 +++++++ src/lib/testing_api_helpers_exchange.c | 1080 ++++++++++++++++++++ .../testing_auditor_api_cmd_deposit_confirmation.c | 444 -------- src/lib/testing_auditor_api_cmd_exchanges.c | 358 ------- src/lib/testing_auditor_api_cmd_exec_auditor.c | 163 --- .../testing_auditor_api_cmd_exec_auditor_dbinit.c | 164 --- .../testing_auditor_api_cmd_exec_wire_auditor.c | 163 --- src/lib/testing_auditor_api_helpers.c | 232 ----- 32 files changed, 5863 insertions(+), 5916 deletions(-) delete mode 100644 src/bank-lib/bank.conf delete mode 100644 src/bank-lib/bank_twisted.conf delete mode 100644 src/bank-lib/test_bank_api_twisted.c delete mode 100644 src/bank-lib/testing_api_cmd_admin_add_incoming.c delete mode 100644 src/bank-lib/testing_api_cmd_history_credit.c delete mode 100644 src/bank-lib/testing_api_cmd_history_debit.c delete mode 100644 src/bank-lib/testing_api_cmd_transfer.c delete mode 100644 src/bank-lib/testing_api_helpers.c create mode 100644 src/lib/bank.conf create mode 100644 src/lib/bank_twisted.conf create mode 100644 src/lib/test_bank_api_twisted.c create mode 100644 src/lib/testing_api_cmd_auditor_deposit_confirmation.c create mode 100644 src/lib/testing_api_cmd_auditor_exchanges.c create mode 100644 src/lib/testing_api_cmd_auditor_exec_auditor.c create mode 100644 src/lib/testing_api_cmd_auditor_exec_auditor_dbinit.c create mode 100644 src/lib/testing_api_cmd_auditor_exec_wire_auditor.c create mode 100644 src/lib/testing_api_cmd_bank_admin_add_incoming.c create mode 100644 src/lib/testing_api_cmd_bank_history_credit.c create mode 100644 src/lib/testing_api_cmd_bank_history_debit.c create mode 100644 src/lib/testing_api_cmd_bank_transfer.c delete mode 100644 src/lib/testing_api_helpers.c create mode 100644 src/lib/testing_api_helpers_auditor.c create mode 100644 src/lib/testing_api_helpers_bank.c create mode 100644 src/lib/testing_api_helpers_exchange.c delete mode 100644 src/lib/testing_auditor_api_cmd_deposit_confirmation.c delete mode 100644 src/lib/testing_auditor_api_cmd_exchanges.c delete mode 100644 src/lib/testing_auditor_api_cmd_exec_auditor.c delete mode 100644 src/lib/testing_auditor_api_cmd_exec_auditor_dbinit.c delete mode 100644 src/lib/testing_auditor_api_cmd_exec_wire_auditor.c delete mode 100644 src/lib/testing_auditor_api_helpers.c diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index 76c85eac4..35af78262 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -30,8 +30,7 @@ taler_bank_transfer_LDADD = \ lib_LTLIBRARIES = \ libtalerbank.la \ - libtalerfakebank.la \ - libtalerbanktesting.la + libtalerfakebank.la libtalerbank_la_LDFLAGS = \ -version-info 1:0:0 \ @@ -66,24 +65,6 @@ libtalerfakebank_la_LIBADD = \ -lmicrohttpd \ $(XLIB) -libtalerbanktesting_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined -libtalerbanktesting_la_SOURCES = \ - testing_api_cmd_admin_add_incoming.c \ - testing_api_cmd_history_credit.c \ - testing_api_cmd_history_debit.c \ - testing_api_cmd_transfer.c \ - testing_api_helpers.c -libtalerbanktesting_la_LIBADD = \ - libtalerbank.la \ - $(top_builddir)/src/json/libtalerjson.la \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - -lmicrohttpd \ - $(XLIB) - if HAVE_LIBCURL libtalerbank_la_LIBADD += -lcurl else @@ -91,70 +72,3 @@ if HAVE_LIBGNURL libtalerbank_la_LIBADD += -lgnurl endif endif - -check_PROGRAMS = \ - test_bank_api_with_fakebank \ - test_bank_api_with_pybank - -if HAVE_TWISTER -check_PROGRAMS += \ - test_bank_api_with_pybank_twisted \ - test_bank_api_with_fakebank_twisted - - -test_bank_api_with_pybank_twisted_SOURCES = \ - test_bank_api_twisted.c -test_bank_api_with_pybank_twisted_LDADD = \ - $(top_builddir)/src/lib/libtalertesting.la \ - libtalerbank.la \ - libtalerbanktesting.la \ - libtalerfakebank.la \ - $(top_builddir)/src/lib/libtalerexchange.la \ - $(top_builddir)/src/json/libtalerjson.la \ - -ltalertwistertesting \ - -lgnunetjson \ - -lgnunetcurl \ - -lgnunetutil \ - -ljansson - - -test_bank_api_with_fakebank_twisted_SOURCES = \ - test_bank_api_twisted.c -test_bank_api_with_fakebank_twisted_LDADD = \ - $(top_builddir)/src/lib/libtalertesting.la \ - libtalerbank.la \ - libtalerbanktesting.la \ - libtalerfakebank.la \ - $(top_builddir)/src/lib/libtalerexchange.la \ - $(top_builddir)/src/json/libtalerjson.la \ - -ltalertwistertesting \ - -lgnunetjson \ - -lgnunetcurl \ - -lgnunetutil \ - -ljansson -endif - -TESTS = \ - $(check_PROGRAMS) - -test_bank_api_with_pybank_SOURCES = \ - test_bank_api.c -test_bank_api_with_pybank_LDADD = \ - $(top_builddir)/src/lib/libtalertesting.la \ - libtalerbanktesting.la \ - -ltalerexchange \ - -lgnunetutil \ - libtalerbank.la - -test_bank_api_with_fakebank_SOURCES = \ - test_bank_api.c -test_bank_api_with_fakebank_LDADD = \ - $(top_builddir)/src/lib/libtalertesting.la \ - libtalerbanktesting.la \ - -ltalerexchange \ - -lgnunetutil \ - libtalerbank.la - -EXTRA_DIST = \ - bank.conf \ - bank_twisted.conf diff --git a/src/bank-lib/bank.conf b/src/bank-lib/bank.conf deleted file mode 100644 index 906b95fc5..000000000 --- a/src/bank-lib/bank.conf +++ /dev/null @@ -1,13 +0,0 @@ -[taler] -currency = KUDOS - -[account-1] -URL = payto://x-taler-bank/localhost:8081/1 - -[bank] -SERVE = http -HTTP_PORT = 8081 -DATABASE = postgres:///talercheck - -[exchange-wire-test] -bank_url = http://localhost:8081/ diff --git a/src/bank-lib/bank_twisted.conf b/src/bank-lib/bank_twisted.conf deleted file mode 100644 index 71bcaee00..000000000 --- a/src/bank-lib/bank_twisted.conf +++ /dev/null @@ -1,39 +0,0 @@ - -[twister] -# HTTP listen port for twister -HTTP_PORT = 8888 -SERVE = tcp - -# HTTP Destination for twister. The test-Webserver needs -# to listen on the port used here. Note: no trailing '/'! -DESTINATION_BASE_URL = "http://localhost:8081" - -# Control port for TCP -# PORT = 8889 -HOSTNAME = localhost -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; - -# Control port for UNIX -UNIXPATH = /tmp/taler-service-twister.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES - -# Launching of twister by ARM -# BINARY = taler-service-twister -# AUTOSTART = NO -# FORCESTART = NO - -[taler] -currency = KUDOS - -[bank] -serve = http -http_port = 8081 -database = postgres:///talercheck - -[account-1] -URL = payto://x-taler-bank/localhost:8081/1 - -[exchange-wire-test] -bank_url = http://localhost:8081/ diff --git a/src/bank-lib/test_bank_api_twisted.c b/src/bank-lib/test_bank_api_twisted.c deleted file mode 100644 index ad8fd71d4..000000000 --- a/src/bank-lib/test_bank_api_twisted.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 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 - -*/ -/** - * @file exchange/test_bank_api_with_fakebank_twisted.c - * @author Marcello Stanisci - * @author Sree Harsha Totakura - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_signatures.h" -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include -#include -#include "taler_bank_service.h" -#include "taler_fakebank_lib.h" -#include "taler_testing_lib.h" -#include -#include "taler_testing_bank_lib.h" -#include - -/** - * Configuration file we use. One (big) configuration is used - * for the various components for this test. - */ -#define CONFIG_FILE "bank_twisted.conf" - -/** - * True when the test runs against Fakebank. - */ -static int with_fakebank; - -/** - * (real) Twister URL. Used at startup time to check if it runs. - */ -static char *twister_url; - -/** - * Account URL of the twister where all the connections to the - * bank that have to be proxied should be addressed to. - */ -static char *twisted_account_url; - -/** - * Authentication data to use. - */ -static struct TALER_BANK_AuthenticationData auth; - -/** - * URL of the bank. - */ -static char *bank_url; - -/** - * Twister process. - */ -static struct GNUNET_OS_Process *twisterd; - -/** - * Python bank process handle. - */ -static struct GNUNET_OS_Process *bankd; - - -/** - * Main function that will tell - * the interpreter what commands to run. - * - * @param cls closure - */ -static void -run (void *cls, - struct TALER_TESTING_Interpreter *is) -{ - struct TALER_TESTING_Command commands[] = { - /** - * Can't use the "wait service" CMD here because the - * fakebank runs inside the same process of the test. - */ - TALER_TESTING_cmd_wait_service ("wait-service", - twister_url), - TALER_TESTING_cmd_bank_credits ("history-0", - twisted_account_url, - &auth, - NULL, - 5), - TALER_TESTING_cmd_end () - }; - - GNUNET_asprintf (&twisted_account_url, - "%s/%s", - twister_url, - "alice"); - // FIXME: init 'auth'! - if (GNUNET_YES == with_fakebank) - TALER_TESTING_run_with_fakebank (is, - commands, - bank_url); - else - TALER_TESTING_run (is, - commands); -} - - -/** - * Kill, wait, and destroy convenience function. - * - * @param process process to purge. - */ -static void -purge_process (struct GNUNET_OS_Process *process) -{ - GNUNET_OS_process_kill (process, SIGINT); - GNUNET_OS_process_wait (process); - GNUNET_OS_process_destroy (process); -} - - -int -main (int argc, - char *const *argv) -{ - unsigned int ret; - - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-bank-api-with-(fake)bank-twisted", - "DEBUG", - NULL); - if (NULL == (twister_url = TALER_TESTING_prepare_twister - (CONFIG_FILE))) - { - GNUNET_break (0); - return 77; - } - if (NULL == (twisterd = TALER_TESTING_run_twister (CONFIG_FILE))) - { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; - } - - with_fakebank = TALER_TESTING_has_in_name (argv[0], - "_with_fakebank"); - - if (GNUNET_YES == with_fakebank) - { - TALER_LOG_DEBUG ("Running against the Fakebank.\n"); - if (NULL == (bank_url = TALER_TESTING_prepare_fakebank - (CONFIG_FILE, - "account-1"))) - { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; - } - } - else - { - TALER_LOG_DEBUG ("Running against the Pybank.\n"); - if (NULL == (bank_url = TALER_TESTING_prepare_bank - (CONFIG_FILE))) - { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; - } - - if (NULL == (bankd = TALER_TESTING_run_bank (CONFIG_FILE, - bank_url))) - { - GNUNET_break (0); - GNUNET_free (twister_url); - GNUNET_free (bank_url); - return 77; - } - } - - ret = TALER_TESTING_setup (&run, - NULL, - CONFIG_FILE, - NULL, - GNUNET_NO); - purge_process (twisterd); - - if (GNUNET_NO == with_fakebank) - { - GNUNET_OS_process_kill (bankd, - SIGKILL); - GNUNET_OS_process_wait (bankd); - GNUNET_OS_process_destroy (bankd); - } - - GNUNET_free (twister_url); - GNUNET_free (bank_url); - - if (GNUNET_OK == ret) - return 0; - - return 1; -} - - -/* end of test_bank_api_twisted.c */ diff --git a/src/bank-lib/testing_api_cmd_admin_add_incoming.c b/src/bank-lib/testing_api_cmd_admin_add_incoming.c deleted file mode 100644 index 770b2e384..000000000 --- a/src/bank-lib/testing_api_cmd_admin_add_incoming.c +++ /dev/null @@ -1,609 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018-2020 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 - -*/ -/** - * @file exchange-lib/testing_api_cmd_admin_add_incoming.c - * @brief implementation of a bank /admin/add-incoming command - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "backoff.h" -#include "taler_json_lib.h" -#include -#include "taler_bank_service.h" -#include "taler_fakebank_lib.h" -#include "taler_signatures.h" -#include "taler_testing_lib.h" -#include "taler_testing_bank_lib.h" - - -/** - * State for a "fakebank transfer" CMD. - */ -struct AdminAddIncomingState -{ - - /** - * Label of any command that can trait-offer a reserve priv. - */ - const char *reserve_reference; - - /** - * Wire transfer amount. - */ - struct TALER_Amount amount; - - /** - * Base URL of the credited account. - */ - const char *exchange_credit_url; - - /** - * Money sender account URL. - */ - const char *payto_debit_account; - - /** - * Username to use for authentication. - */ - struct TALER_BANK_AuthenticationData auth; - - /** - * Set (by the interpreter) to the reserve's private key - * we used to make a wire transfer subject line with. - */ - struct TALER_ReservePrivateKeyP reserve_priv; - - /** - * Reserve public key matching @e reserve_priv. - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * Handle to the pending request at the fakebank. - */ - struct TALER_BANK_AdminAddIncomingHandle *aih; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Set to the wire transfer's unique ID. - */ - uint64_t serial_id; - - /** - * Timestamp of the transaction (as returned from the bank). - */ - struct GNUNET_TIME_Absolute timestamp; - - /** - * Merchant instance. Sometimes used to get the tip reserve - * private key by reading the appropriate config section. - */ - const char *instance; - - /** - * Configuration filename. Used to get the tip reserve key - * filename (used to obtain a public key to write in the - * transfer subject). - */ - const char *config_filename; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Was this command modified via - * #TALER_TESTING_cmd_admin_add_incoming_with_retry to - * enable retries? - */ - int do_retry; -}; - - -/** - * Run the "fakebank transfer" CMD. - * - * @param cls closure. - * @param cmd CMD being run. - * @param is interpreter state. - */ -static void -admin_add_incoming_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #admin_add_incoming_run. - * - * @param cls a `struct AdminAddIncomingState` - */ -static void -do_retry (void *cls) -{ - struct AdminAddIncomingState *fts = cls; - - fts->retry_task = NULL; - admin_add_incoming_run (fts, - NULL, - fts->is); -} - - -/** - * This callback will process the fakebank response to the wire - * transfer. It just checks whether the HTTP response code is - * acceptable. - * - * @param cls closure with the interpreter state - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for - * successful status request; 0 if the exchange's reply is - * bogus (fails to follow the protocol) - * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param serial_id unique ID of the wire transfer - * @param timestamp time stamp of the transaction made. - * @param json raw response - */ -static void -confirmation_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t serial_id, - struct GNUNET_TIME_Absolute timestamp, - const json_t *json) -{ - struct AdminAddIncomingState *fts = cls; - struct TALER_TESTING_Interpreter *is = fts->is; - - fts->aih = NULL; - if (MHD_HTTP_OK != http_status) - { - if (GNUNET_YES == fts->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log - (GNUNET_ERROR_TYPE_INFO, - "Retrying fakebank transfer failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - fts->backoff = GNUNET_TIME_UNIT_ZERO; - else - fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); - fts->retry_task = GNUNET_SCHEDULER_add_delayed - (fts->backoff, - &do_retry, - fts); - return; - } - } - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fakebank returned HTTP status %u/%d\n", - http_status, - (int) ec); - TALER_TESTING_interpreter_fail (is); - return; - } - - fts->serial_id = serial_id; - fts->timestamp = timestamp; - TALER_TESTING_interpreter_next (is); -} - - -/** - * Run the "fakebank transfer" CMD. - * - * @param cls closure. - * @param cmd CMD being run. - * @param is interpreter state. - */ -static void -admin_add_incoming_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct AdminAddIncomingState *fts = cls; - - /* Use reserve public key as subject */ - if (NULL != fts->reserve_reference) - { - const struct TALER_TESTING_Command *ref; - const struct TALER_ReservePrivateKeyP *reserve_priv; - - ref = TALER_TESTING_interpreter_lookup_command - (is, fts->reserve_reference); - if (NULL == ref) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_priv (ref, - 0, - &reserve_priv)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv; - } - else - { - if (NULL != fts->instance) - { - char *section; - char *keys; - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - struct GNUNET_CONFIGURATION_Handle *cfg; - - GNUNET_assert (NULL != fts->config_filename); - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - fts->config_filename)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - GNUNET_asprintf (§ion, - "instance-%s", - fts->instance); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename - (cfg, - section, - "TIP_RESERVE_PRIV_FILENAME", - &keys)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Configuration fails to specify reserve" - " private key filename in section %s\n", - section); - GNUNET_free (section); - TALER_TESTING_interpreter_fail (is); - return; - } - priv = GNUNET_CRYPTO_eddsa_key_create_from_file (keys); - GNUNET_free (keys); - if (NULL == priv) - { - GNUNET_log_config_invalid - (GNUNET_ERROR_TYPE_ERROR, - section, - "TIP_RESERVE_PRIV_FILENAME", - "Failed to read private key"); - GNUNET_free (section); - TALER_TESTING_interpreter_fail (is); - return; - } - fts->reserve_priv.eddsa_priv = *priv; - GNUNET_free (section); - GNUNET_free (priv); - GNUNET_CONFIGURATION_destroy (cfg); - } - else - { - /* No referenced reserve, no instance to take priv - * from, no explicit subject given: create new key! */ - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - - priv = GNUNET_CRYPTO_eddsa_key_create (); - fts->reserve_priv.eddsa_priv = *priv; - GNUNET_free (priv); - } - } - GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv, - &fts->reserve_pub.eddsa_pub); - fts->is = is; - fts->aih - = TALER_BANK_admin_add_incoming - (TALER_TESTING_interpreter_get_context (is), - fts->exchange_credit_url, - &fts->auth, - &fts->reserve_pub, - &fts->amount, - fts->payto_debit_account, - &confirmation_cb, - fts); - if (NULL == fts->aih) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } -} - - -/** - * Free the state of a "/admin/add-incoming" CMD, and possibly - * cancel a pending operation thereof. - * - * @param cls closure - * @param cmd current CMD being cleaned up. - */ -static void -admin_add_incoming_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct AdminAddIncomingState *fts = cls; - - if (NULL != fts->aih) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %s did not complete\n", - cmd->label); - TALER_BANK_admin_add_incoming_cancel (fts->aih); - fts->aih = NULL; - } - if (NULL != fts->retry_task) - { - GNUNET_SCHEDULER_cancel (fts->retry_task); - fts->retry_task = NULL; - } - GNUNET_free (fts); -} - - -/** - * Offer internal data from a "/admin/add-incoming" CMD to other - * commands. - * - * @param cls closure. - * @param ret[out] result - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -admin_add_incoming_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct AdminAddIncomingState *fts = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_url (1, fts->payto_debit_account), - TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), - TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->exchange_credit_url), - TALER_TESTING_make_trait_amount_obj (0, &fts->amount), - TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), - TALER_TESTING_make_trait_reserve_priv (0, - &fts->reserve_priv), - TALER_TESTING_make_trait_reserve_pub (0, - &fts->reserve_pub), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Create admin/add-incoming command. - * - * @param label command label. - * @param amount amount to transfer. - * @param exchange_base_url base URL of the account that receives this - * wire transer (which account receives money). - * @param payto_debit_account which account sends money. - * @param auth authentication data - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_admin_add_incoming - (const char *label, - const char *amount, - const char *exchange_base_url, - const struct TALER_BANK_AuthenticationData *auth, - const char *payto_debit_account) -{ - struct AdminAddIncomingState *fts; - - fts = GNUNET_new (struct AdminAddIncomingState); - fts->exchange_credit_url = exchange_base_url; - fts->payto_debit_account = payto_debit_account; - fts->auth = *auth; - if (GNUNET_OK != - TALER_string_to_amount (amount, - &fts->amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %s\n", - amount, - label); - GNUNET_assert (0); - } - - { - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &admin_add_incoming_run, - .cleanup = &admin_add_incoming_cleanup, - .traits = &admin_add_incoming_traits - }; - - return cmd; - } -} - - -/** - * Create "/admin/add-incoming" CMD, letting the caller specify - * a reference to a command that can offer a reserve private key. - * This private key will then be used to construct the subject line - * of the wire transfer. - * - * @param label command label. - * @param amount the amount to transfer. - * @param account_bank_url base URL of the exchange account receiving the money - * @param payto_debit_account which account sends money - * @param auth authentication data - * @param ref reference to a command that can offer a reserve - * private key. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_admin_add_incoming_with_ref - (const char *label, - const char *amount, - const char *account_base_url, - const struct TALER_BANK_AuthenticationData *auth, - const char *payto_debit_account, - const char *ref) -{ - struct AdminAddIncomingState *fts; - - fts = GNUNET_new (struct AdminAddIncomingState); - fts->exchange_credit_url = account_base_url; - fts->payto_debit_account = payto_debit_account; - fts->auth = *auth; - fts->reserve_reference = ref; - if (GNUNET_OK != - TALER_string_to_amount (amount, - &fts->amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %s\n", - amount, - label); - GNUNET_assert (0); - } - { - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &admin_add_incoming_run, - .cleanup = &admin_add_incoming_cleanup, - .traits = &admin_add_incoming_traits - }; - - return cmd; - } -} - - -/** - * Create "/admin/add-incoming" CMD, letting the caller specifying - * the merchant instance. This version is useful when a tip - * reserve should be topped up, in fact the interpreter will need - * the "tipping instance" in order to get the instance public key - * and make a wire transfer subject out of it. - * - * @param label command label. - * @param amount amount to transfer. - * @param account_bank_url base URL of the exchange bank account - * that receives the wire transfer - * @param payto_debit_account which account (expressed as a number) - * gives money - * @param auth authentication data - * @param instance the instance that runs the tipping. Under this - * instance, the configuration file will provide the private - * key of the tipping reserve. This data will then used to - * construct the wire transfer subject line. - * @param config_filename configuration file to use. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_admin_add_incoming_with_instance - (const char *label, - const char *amount, - const char *account_base_url, - const struct TALER_BANK_AuthenticationData *auth, - const char *payto_debit_account, - const char *instance, - const char *config_filename) -{ - struct AdminAddIncomingState *fts; - - fts = GNUNET_new (struct AdminAddIncomingState); - fts->exchange_credit_url = account_base_url; - fts->payto_debit_account = payto_debit_account; - fts->auth = *auth; - fts->instance = instance; - fts->config_filename = config_filename; - if (GNUNET_OK != - TALER_string_to_amount (amount, - &fts->amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %s\n", - amount, - label); - GNUNET_assert (0); - } - { - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &admin_add_incoming_run, - .cleanup = &admin_add_incoming_cleanup, - .traits = &admin_add_incoming_traits - }; - - return cmd; - } -} - - -/** - * Modify a fakebank transfer command to enable retries when the - * reserve is not yet full or we get other transient errors from the - * fakebank. - * - * @param cmd a fakebank transfer command - * @return the command with retries enabled - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_admin_add_incoming_retry (struct TALER_TESTING_Command cmd) -{ - struct AdminAddIncomingState *fts; - - GNUNET_assert (&admin_add_incoming_run == cmd.run); - fts = cmd.cls; - fts->do_retry = GNUNET_YES; - return cmd; -} - - -/* end of testing_api_cmd_admin_add_incoming.c */ diff --git a/src/bank-lib/testing_api_cmd_history_credit.c b/src/bank-lib/testing_api_cmd_history_credit.c deleted file mode 100644 index fefb2dda7..000000000 --- a/src/bank-lib/testing_api_cmd_history_credit.c +++ /dev/null @@ -1,765 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018-2020 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 - -*/ -/** - * @file bank-lib/testing_api_cmd_history.c - * @brief command to check the /history API from the bank. - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "taler_testing_lib.h" -#include "taler_testing_bank_lib.h" -#include "taler_fakebank_lib.h" -#include "taler_bank_service.h" -#include "taler_fakebank_lib.h" - - -/** - * State for a "history" CMD. - */ -struct HistoryState -{ - /** - * Base URL of the account offering the "history" operation. - */ - char *account_url; - - /** - * Reference to command defining the - * first row number we want in the result. - */ - const char *start_row_reference; - - /** - * How many rows we want in the result, _at most_, - * and ascending/descending. - */ - long long num_results; - - /** - * Handle to a pending "history" operation. - */ - struct TALER_BANK_CreditHistoryHandle *hh; - - /** - * Authentication data for the operation. - */ - struct TALER_BANK_AuthenticationData auth; - - /** - * Expected number of results (= rows). - */ - uint64_t results_obtained; - - /** - * Set to GNUNET_YES if the callback detects something - * unexpected. - */ - int failed; - -}; - - -/** - * Item in the transaction history, as reconstructed from the - * command history. - */ -struct History -{ - - /** - * Wire details. - */ - struct TALER_BANK_CreditDetails details; - - /** - * Serial ID of the wire transfer. - */ - uint64_t row_id; - - /** - * URL to free. - */ - char *url; -}; - - -/** - * Offer internal data to other commands. - * - * @param cls closure. - * @param ret[out] set to the wanted data. - * @param trait name of the trait. - * @param index index number of the traits to be returned. - * - * @return #GNUNET_OK on success - */ -static int -history_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - (void) cls; - (void) ret; - (void) trait; - (void) index; - /* Must define this function because some callbacks - * look for certain traits on _all_ the commands. */ - return GNUNET_SYSERR; -} - - -/** - * Free history @a h of length @a h_len. - * - * @param h history array to free. - * @param h_len number of entries in @a h. - */ -static void -free_history (struct History *h, - uint64_t h_len) -{ - for (uint64_t off = 0; off= hs->num_results; -} - - -/** - * This function constructs the list of history elements that - * interest the account number of the caller. It has two main - * loops: the first to figure out how many history elements have - * to be allocated, and the second to actually populate every - * element. - * - * @param is interpreter state (supposedly having the - * current CMD pointing at a "history" CMD). - * @param[out] rh history array to initialize. - * - * @return number of entries in @a rh. - */ -static uint64_t -build_history (struct TALER_TESTING_Interpreter *is, - struct History **rh) -{ - struct HistoryState *hs = is->commands[is->ip].cls; - uint64_t total; - struct History *h; - const struct TALER_TESTING_Command *add_incoming_cmd; - int inc; - unsigned int start; - unsigned int end; - - /** - * @var turns GNUNET_YES whenever either no 'start' value was - * given for the history query, or the given value is found - * in the list of all the CMDs. - */int ok; - const uint64_t *row_id_start = NULL; - - if (NULL != hs->start_row_reference) - { - TALER_LOG_INFO - ("`%s': start row given via reference `%s'\n", - TALER_TESTING_interpreter_get_current_label (is), - hs->start_row_reference); - add_incoming_cmd = TALER_TESTING_interpreter_lookup_command - (is, hs->start_row_reference); - GNUNET_assert (NULL != add_incoming_cmd); - GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64 - (add_incoming_cmd, 0, &row_id_start)); - } - - GNUNET_assert (0 != hs->num_results); - if (0 == is->ip) - { - TALER_LOG_DEBUG ("Checking history at first CMD..\n"); - *rh = NULL; - return 0; - } - - /* AKA 'delta'. */ - if (hs->num_results > 0) - { - inc = 1; /* _inc_rement */ - start = 0; - end = is->ip - 1; - } - else - { - inc = -1; - start = is->ip - 1; - end = 0; - } - - total = 0; - ok = GNUNET_NO; - - if (NULL == row_id_start) - ok = GNUNET_YES; - - /* This loop counts how many commands _later than "start"_ belong - * to the history of the caller. This is stored in the @var total - * variable. */ - for (unsigned int off = start; off != end + inc; off += inc) - { - const struct TALER_TESTING_Command *pos = &is->commands[off]; - const uint64_t *row_id; - const char *credit_account; - const char *debit_account; - - /** - * The following command allows us to skip over those CMDs - * that do not offer a "row_id" trait. Such skipped CMDs are - * not interesting for building a history. - */if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (pos, - 0, - &row_id)) - continue; - - /* Seek "/history" starting row. */ - if (NULL != row_id_start) - { - if (*row_id_start == *row_id) - { - /* Doesn't count, start is excluded from output. */ - total = 0; - ok = GNUNET_YES; - continue; - } - } - - /* when 'start' was _not_ given, then ok == GNUNET_YES */ - if (GNUNET_NO == ok) - continue; /* skip until we find the marker */ - - TALER_LOG_DEBUG ("Found first row\n"); - - if (build_history_hit_limit (total, - hs, - pos)) - { - TALER_LOG_DEBUG ("Hit history limit\n"); - break; - } - - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT - (pos, &credit_account)); - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT - (pos, &debit_account)); - - TALER_LOG_INFO ("Potential history element:" - " %s->%s; my account: %s\n", - debit_account, - credit_account, - hs->account_url); - - if (0 == strcasecmp (hs->account_url, - credit_account)) - { - TALER_LOG_INFO ("+1 my history\n"); - total++; /* found matching record */ - } - } - - GNUNET_assert (GNUNET_YES == ok); - - if (0 == total) - { - TALER_LOG_DEBUG ("Checking history at first CMD.. (2)\n"); - *rh = NULL; - return 0; - } - - - GNUNET_assert (total < UINT_MAX); - h = GNUNET_new_array ((unsigned int) total, - struct History); - total = 0; - ok = GNUNET_NO; - if (NULL == row_id_start) - ok = GNUNET_YES; - - /** - * This loop _only_ populates the array of history elements. - */ - for (unsigned int off = start; off != end + inc; off += inc) - { - const struct TALER_TESTING_Command *pos = &is->commands[off]; - const uint64_t *row_id; - char *bank_hostname; - const char *credit_account; - const char *debit_account; - - if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID - (pos, &row_id)) - continue; - - if (NULL != row_id_start) - { - - if (*row_id_start == *row_id) - { - /** - * Warning: this zeroing is superfluous, as - * total doesn't get incremented if 'start' - * was given and couldn't be found. - */total = 0; - ok = GNUNET_YES; - continue; - } - } - - TALER_LOG_INFO ("Found first row (2)\n"); - - if (GNUNET_NO == ok) - { - TALER_LOG_INFO ("Skip on `%s'\n", - pos->label); - continue; /* skip until we find the marker */ - } - - if (build_history_hit_limit (total, - hs, - pos)) - { - TALER_LOG_INFO ("Hit history limit (2)\n"); - break; - } - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT - (pos, &credit_account)); - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT - (pos, &debit_account)); - - TALER_LOG_INFO ("Potential history bit:" - " %s->%s; my account: %s\n", - debit_account, - credit_account, - hs->account_url); - - /** - * Discard transactions where the audited account played - * _both_ the credit and the debit roles, but _only if_ - * the audit goes on both directions.. This needs more - * explaination! - */if (0 == strcasecmp (hs->account_url, - credit_account)) - { - GNUNET_break (0); - continue; - } - - bank_hostname = strchr (hs->account_url, ':'); - GNUNET_assert (NULL != bank_hostname); - bank_hostname += 3; - - /* Next two blocks only put the 'direction' and 'banking' - * information. */ - - /* Asked for credit, and account got the credit. */ - if (0 == strcasecmp (hs->account_url, - credit_account)) - { - h[total].url = GNUNET_strdup (debit_account); - h[total].details.account_url = h[total].url; - } - - /* This block _completes_ the information of the current item, - * with amount / subject / exchange URL. */ - if (0 == strcasecmp (hs->account_url, - credit_account)) - { - const struct TALER_Amount *amount; - const struct TALER_ReservePublicKeyP *reserve_pub; - const char *account_url; - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_amount_obj - (pos, 0, &amount)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_reserve_pub - (pos, 0, &reserve_pub)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_url - (pos, 1, - &account_url)); - h[total].details.amount = *amount; - h[total].row_id = *row_id; - h[total].details.reserve_pub = *reserve_pub; - h[total].details.account_url = account_url; - TALER_LOG_INFO ("+1-bit of my history\n"); - total++; - } - } - *rh = h; - return total; -} - - -/** - * Compute how many results we expect to be returned for - * the current command at @a is. - * - * @param is the interpreter state to inspect. - * @return number of results expected. - */ -static uint64_t -compute_result_count (struct TALER_TESTING_Interpreter *is) -{ - uint64_t total; - struct History *h; - - total = build_history (is, &h); - free_history (h, total); - return total; -} - - -/** - * Check that the "/history" response matches the - * CMD whose offset in the list of CMDs is @a off. - * - * @param is the interpreter state. - * @param off the offset (of the CMD list) where the command - * to check is. - * @param dir the expected direction of the transaction. - * @param details the expected transaction details. - * - * @return #GNUNET_OK if the transaction is what we expect. - */ -static int -check_result (struct TALER_TESTING_Interpreter *is, - unsigned int off, - const struct TALER_BANK_CreditDetails *details) -{ - uint64_t total; - struct History *h; - - total = build_history (is, &h); - if (off >= total) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Test says history has at most %u" - " results, but got result #%u to check\n", - (unsigned int) total, - off); - print_expected (h, - total, - off); - return GNUNET_SYSERR; - } - if ( (0 != GNUNET_memcmp (&h[off].details.reserve_pub, - &details->reserve_pub)) || - (0 != TALER_amount_cmp (&h[off].details.amount, - &details->amount)) || - (0 != strcasecmp (h[off].details.account_url, - details->account_url)) ) - { - GNUNET_break (0); - print_expected (h, - total, - off); - free_history (h, - total); - return GNUNET_SYSERR; - } - free_history (h, - total); - return GNUNET_OK; -} - - -/** - * This callback will (1) check that the HTTP response code - * is acceptable and (2) that the history is consistent. The - * consistency is checked by going through all the past CMDs, - * reconstructing then the expected history as of those, and - * finally check it against what the bank returned. - * - * @param cls closure. - * @param http_status HTTP response code, #MHD_HTTP_OK (200) - * for successful status request 0 if the bank's reply is - * bogus (fails to follow the protocol), - * #MHD_HTTP_NO_CONTENT if there are no more results; on - * success the last callback is always of this status - * (even if `abs(num_results)` were already returned). - * @param ec taler status code. - * @param dir direction of the transfer. - * @param row_id monotonically increasing counter corresponding to - * the transaction. - * @param details details about the wire transfer. - * @param json detailed response from the HTTPD, or NULL if - * reply was not in JSON. - * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration - */ -static int -history_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t row_id, - const struct TALER_BANK_CreditDetails *details, - const json_t *json) -{ - struct TALER_TESTING_Interpreter *is = cls; - struct HistoryState *hs = is->commands[is->ip].cls; - - (void) row_id; - if (MHD_HTTP_OK != http_status) - { - hs->hh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unwanted response code from /history: %u\n", - http_status); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - if (NULL == details) - { - hs->hh = NULL; - if ( (hs->results_obtained != compute_result_count (is)) || - (GNUNET_YES == hs->failed) ) - { - uint64_t total; - struct History *h; - - GNUNET_break (0); - total = build_history (is, &h); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expected history of length %llu, got %llu;" - " HTTP status code: %u/%d, failed: %d\n", - (unsigned long long) total, - (unsigned long long) hs->results_obtained, - http_status, - (int) ec, - hs->failed); - print_expected (h, - total, - UINT_MAX); - free_history (h, - total); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - TALER_TESTING_interpreter_next (is); - return GNUNET_OK; - } - - /* check current element */ - if (GNUNET_OK != check_result (is, - hs->results_obtained, - details)) - { - char *acc; - - GNUNET_break (0); - acc = json_dumps (json, - JSON_COMPACT); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Result %u was `%s'\n", - (unsigned int) hs->results_obtained++, - acc); - if (NULL != acc) - free (acc); - hs->failed = GNUNET_YES; - return GNUNET_SYSERR; - } - hs->results_obtained++; - return GNUNET_OK; -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -history_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct HistoryState *hs = cls; - uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX; - const uint64_t *row_ptr; - - (void) cmd; - /* Get row_id from trait. */ - if (NULL != hs->start_row_reference) - { - const struct TALER_TESTING_Command *history_cmd; - - history_cmd = TALER_TESTING_interpreter_lookup_command - (is, hs->start_row_reference); - - if (NULL == history_cmd) - TALER_TESTING_FAIL (is); - - if (GNUNET_OK != - TALER_TESTING_get_trait_uint64 (history_cmd, - 0, - &row_ptr)) - TALER_TESTING_FAIL (is); - else - row_id = *row_ptr; - TALER_LOG_DEBUG ("row id (from trait) is %llu\n", - (unsigned long long) row_id); - } - - hs->hh = TALER_BANK_credit_history (is->ctx, - hs->account_url, - &hs->auth, - row_id, - hs->num_results, - &history_cb, - is); - GNUNET_assert (NULL != hs->hh); -} - - -/** - * Free the state from a "history" CMD, and possibly cancel - * a pending operation thereof. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -history_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct HistoryState *hs = cls; - - (void) cmd; - if (NULL != hs->hh) - { - TALER_LOG_WARNING ("/history did not complete\n"); - TALER_BANK_credit_history_cancel (hs->hh); - } - GNUNET_free (hs->account_url); - GNUNET_free (hs); -} - - -/** - * Make a "history" CMD. - * - * @param label command label. - * @param account_url base URL of the account offering the "history" - * operation. - * @param start_row_reference reference to a command that can - * offer a row identifier, to be used as the starting row - * to accept in the result. - * @param num_results how many rows we want in the result. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_bank_credits (const char *label, - const char *account_url, - const struct - TALER_BANK_AuthenticationData *auth, - const char *start_row_reference, - long long num_results) -{ - struct HistoryState *hs; - - hs = GNUNET_new (struct HistoryState); - hs->account_url = GNUNET_strdup (account_url); - hs->start_row_reference = start_row_reference; - hs->num_results = num_results; - hs->auth = *auth; - { - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = hs, - .run = &history_run, - .cleanup = &history_cleanup, - .traits = &history_traits - }; - - return cmd; - } -} - - -/* end of testing_api_cmd_credit_history.c */ diff --git a/src/bank-lib/testing_api_cmd_history_debit.c b/src/bank-lib/testing_api_cmd_history_debit.c deleted file mode 100644 index 96f989c04..000000000 --- a/src/bank-lib/testing_api_cmd_history_debit.c +++ /dev/null @@ -1,766 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018-2020 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 - -*/ - -/** - * @file bank-lib/testing_api_cmd_history.c - * @brief command to check the /history API from the bank. - * @author Marcello Stanisci - */ - -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_exchange_service.h" -#include "taler_testing_lib.h" -#include "taler_testing_bank_lib.h" -#include "taler_fakebank_lib.h" -#include "taler_bank_service.h" -#include "taler_fakebank_lib.h" - - -/** - * State for a "history" CMD. - */ -struct HistoryState -{ - /** - * Base URL of the account offering the "history" operation. - */ - const char *account_url; - - /** - * Reference to command defining the - * first row number we want in the result. - */ - const char *start_row_reference; - - /** - * How many rows we want in the result, _at most_, - * and ascending/descending. - */ - long long num_results; - - /** - * Login data to use to authenticate. - */ - struct TALER_BANK_AuthenticationData auth; - - /** - * Handle to a pending "history" operation. - */ - struct TALER_BANK_DebitHistoryHandle *hh; - - /** - * Expected number of results (= rows). - */ - uint64_t results_obtained; - - /** - * Set to #GNUNET_YES if the callback detects something - * unexpected. - */ - int failed; - -}; - - -/** - * Item in the transaction history, as reconstructed from the - * command history. - */ -struct History -{ - - /** - * Wire details. - */ - struct TALER_BANK_DebitDetails details; - - /** - * Serial ID of the wire transfer. - */ - uint64_t row_id; - - /** - * URL to free. - */ - char *url; -}; - - -/** - * Offer internal data to other commands. - * - * @param cls closure. - * @param ret[out] set to the wanted data. - * @param trait name of the trait. - * @param index index number of the traits to be returned. - * - * @return #GNUNET_OK on success - */ -static int -history_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - (void) cls; - (void) ret; - (void) trait; - (void) index; - /* Must define this function because some callbacks - * look for certain traits on _all_ the commands. */ - return GNUNET_SYSERR; -} - - -/** - * Free history @a h of length @a h_len. - * - * @param h history array to free. - * @param h_len number of entries in @a h. - */ -static void -free_history (struct History *h, - uint64_t h_len) -{ - for (uint64_t off = 0; off= hs->num_results; -} - - -/** - * This function constructs the list of history elements that - * interest the account number of the caller. It has two main - * loops: the first to figure out how many history elements have - * to be allocated, and the second to actually populate every - * element. - * - * @param is interpreter state (supposedly having the - * current CMD pointing at a "history" CMD). - * @param[out] rh history array to initialize. - * - * @return number of entries in @a rh. - */ -static uint64_t -build_history (struct TALER_TESTING_Interpreter *is, - struct History **rh) -{ - struct HistoryState *hs = is->commands[is->ip].cls; - uint64_t total; - struct History *h; - const struct TALER_TESTING_Command *add_incoming_cmd; - int inc; - unsigned int start; - unsigned int end; - - /** - * @var turns GNUNET_YES whenever either no 'start' value was - * given for the history query, or the given value is found - * in the list of all the CMDs. - */int ok; - const uint64_t *row_id_start = NULL; - - if (NULL != hs->start_row_reference) - { - TALER_LOG_INFO - ("`%s': start row given via reference `%s'\n", - TALER_TESTING_interpreter_get_current_label (is), - hs->start_row_reference); - add_incoming_cmd = TALER_TESTING_interpreter_lookup_command - (is, hs->start_row_reference); - GNUNET_assert (NULL != add_incoming_cmd); - GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64 - (add_incoming_cmd, 0, &row_id_start)); - } - - GNUNET_assert (0 != hs->num_results); - if (0 == is->ip) - { - TALER_LOG_DEBUG ("Checking history at first CMD..\n"); - *rh = NULL; - return 0; - } - - /* AKA 'delta'. */ - if (hs->num_results > 0) - { - inc = 1; /* _inc_rement */ - start = 0; - end = is->ip - 1; - } - else - { - inc = -1; - start = is->ip - 1; - end = 0; - } - - total = 0; - ok = GNUNET_NO; - - if (NULL == row_id_start) - ok = GNUNET_YES; - - /* This loop counts how many commands _later than "start"_ belong - * to the history of the caller. This is stored in the @var total - * variable. */ - for (unsigned int off = start; off != end + inc; off += inc) - { - const struct TALER_TESTING_Command *pos = &is->commands[off]; - const uint64_t *row_id; - const char *debit_account; - const char *credit_account; - - /** - * The following command allows us to skip over those CMDs - * that do not offer a "row_id" trait. Such skipped CMDs are - * not interesting for building a history. - */if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (pos, - 0, - &row_id)) - continue; - - /* Seek "/history" starting row. */ - if (NULL != row_id_start) - { - if (*row_id_start == *row_id) - { - /* Doesn't count, start is excluded from output. */ - total = 0; - ok = GNUNET_YES; - continue; - } - } - - /* when 'start' was _not_ given, then ok == GNUNET_YES */ - if (GNUNET_NO == ok) - continue; /* skip until we find the marker */ - - TALER_LOG_DEBUG ("Found first row\n"); - - if (build_history_hit_limit (total, - hs, - pos)) - { - TALER_LOG_DEBUG ("Hit history limit\n"); - break; - } - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT - (pos, &debit_account)); - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT - (pos, &credit_account)); - - TALER_LOG_INFO ("Potential history element:" - " %s->%s; my account: %s\n", - debit_account, - credit_account, - hs->account_url); - - if (0 == strcasecmp (hs->account_url, - debit_account)) - { - TALER_LOG_INFO ("+1 my history\n"); - total++; /* found matching record */ - } - } - - GNUNET_assert (GNUNET_YES == ok); - - if (0 == total) - { - TALER_LOG_DEBUG ("Checking history at first CMD.. (2)\n"); - *rh = NULL; - return 0; - } - - - GNUNET_assert (total < UINT_MAX); - h = GNUNET_new_array ((unsigned int) total, - struct History); - total = 0; - ok = GNUNET_NO; - if (NULL == row_id_start) - ok = GNUNET_YES; - - /** - * This loop _only_ populates the array of history elements. - */ - for (unsigned int off = start; off != end + inc; off += inc) - { - const struct TALER_TESTING_Command *pos = &is->commands[off]; - const uint64_t *row_id; - char *bank_hostname; - const char *credit_account; - const char *debit_account; - - if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID - (pos, &row_id)) - continue; - - if (NULL != row_id_start) - { - - if (*row_id_start == *row_id) - { - /** - * Warning: this zeroing is superfluous, as - * total doesn't get incremented if 'start' - * was given and couldn't be found. - */total = 0; - ok = GNUNET_YES; - continue; - } - } - - TALER_LOG_INFO ("Found first row (2)\n"); - - if (GNUNET_NO == ok) - { - TALER_LOG_INFO ("Skip on `%s'\n", - pos->label); - continue; /* skip until we find the marker */ - } - - if (build_history_hit_limit (total, - hs, - pos)) - { - TALER_LOG_INFO ("Hit history limit (2)\n"); - break; - } - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT - (pos, &debit_account)); - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT - (pos, &credit_account)); - - TALER_LOG_INFO ("Potential history bit:" - " %s->%s; my account: %s\n", - debit_account, - credit_account, - hs->account_url); - - /** - * Discard transactions where the audited account played - * _both_ the debit and the debit roles, but _only if_ - * the audit goes on both directions.. This needs more - * explaination! - */if (0 == strcasecmp (hs->account_url, - debit_account)) - { - GNUNET_break (0); - continue; - } - - bank_hostname = strchr (hs->account_url, ':'); - GNUNET_assert (NULL != bank_hostname); - bank_hostname += 3; - - /* Next two blocks only put the 'direction' and 'banking' - * information. */ - - /* Asked for debit, and account got the debit. */ - if (0 == strcasecmp (hs->account_url, - debit_account)) - { - h[total].url = GNUNET_strdup (credit_account); - h[total].details.account_url = h[total].url; - } - - /* This block _completes_ the information of the current item, - * with amount / subject / exchange URL. */ - if (0 == strcasecmp (hs->account_url, - debit_account)) - { - const struct TALER_Amount *amount; - const struct TALER_WireTransferIdentifierRawP *wtid; - const char *account_url; - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_amount_obj - (pos, 0, &amount)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_wtid - (pos, 0, &wtid)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_url - (pos, 1, - &account_url)); - h[total].details.amount = *amount; - h[total].row_id = *row_id; - h[total].details.wtid = *wtid; - h[total].details.account_url = account_url; - TALER_LOG_INFO ("+1-bit of my history\n"); - total++; - } - } - *rh = h; - return total; -} - - -/** - * Compute how many results we expect to be returned for - * the current command at @a is. - * - * @param is the interpreter state to inspect. - * @return number of results expected. - */ -static uint64_t -compute_result_count (struct TALER_TESTING_Interpreter *is) -{ - uint64_t total; - struct History *h; - - total = build_history (is, &h); - free_history (h, total); - return total; -} - - -/** - * Check that the "/history" response matches the - * CMD whose offset in the list of CMDs is @a off. - * - * @param is the interpreter state. - * @param off the offset (of the CMD list) where the command - * to check is. - * @param dir the expected direction of the transaction. - * @param details the expected transaction details. - * - * @return #GNUNET_OK if the transaction is what we expect. - */ -static int -check_result (struct TALER_TESTING_Interpreter *is, - unsigned int off, - const struct TALER_BANK_DebitDetails *details) -{ - uint64_t total; - struct History *h; - - total = build_history (is, &h); - if (off >= total) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Test says history has at most %u" - " results, but got result #%u to check\n", - (unsigned int) total, - off); - print_expected (h, - total, - off); - return GNUNET_SYSERR; - } - if ( (0 != GNUNET_memcmp (&h[off].details.wtid, - &details->wtid)) || - (0 != TALER_amount_cmp (&h[off].details.amount, - &details->amount)) || - (0 != strcasecmp (h[off].details.account_url, - details->account_url)) ) - { - GNUNET_break (0); - print_expected (h, - total, - off); - free_history (h, - total); - return GNUNET_SYSERR; - } - free_history (h, - total); - return GNUNET_OK; -} - - -/** - * This callback will (1) check that the HTTP response code - * is acceptable and (2) that the history is consistent. The - * consistency is checked by going through all the past CMDs, - * reconstructing then the expected history as of those, and - * finally check it against what the bank returned. - * - * @param cls closure. - * @param http_status HTTP response code, #MHD_HTTP_OK (200) - * for successful status request 0 if the bank's reply is - * bogus (fails to follow the protocol), - * #MHD_HTTP_NO_CONTENT if there are no more results; on - * success the last callback is always of this status - * (even if `abs(num_results)` were already returned). - * @param ec taler status code. - * @param dir direction of the transfer. - * @param row_id monotonically increasing counter corresponding to - * the transaction. - * @param details details about the wire transfer. - * @param json detailed response from the HTTPD, or NULL if - * reply was not in JSON. - * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration - */ -static int -history_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t row_id, - const struct TALER_BANK_DebitDetails *details, - const json_t *json) -{ - struct TALER_TESTING_Interpreter *is = cls; - struct HistoryState *hs = is->commands[is->ip].cls; - - (void) row_id; - if (MHD_HTTP_OK != http_status) - { - hs->hh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unwanted response code from /history: %u\n", - http_status); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - if (NULL == details) - { - hs->hh = NULL; - if ( (hs->results_obtained != compute_result_count (is)) || - (GNUNET_YES == hs->failed) ) - { - uint64_t total; - struct History *h; - - GNUNET_break (0); - total = build_history (is, &h); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expected history of length %llu, got %llu;" - " HTTP status code: %u/%d, failed: %d\n", - (unsigned long long) total, - (unsigned long long) hs->results_obtained, - http_status, - (int) ec, - hs->failed); - print_expected (h, - total, - UINT_MAX); - free_history (h, - total); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - TALER_TESTING_interpreter_next (is); - return GNUNET_OK; - } - - /* check current element */ - if (GNUNET_OK != check_result (is, - hs->results_obtained, - details)) - { - char *acc; - - GNUNET_break (0); - acc = json_dumps (json, - JSON_COMPACT); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Result %u was `%s'\n", - (unsigned int) hs->results_obtained++, - acc); - if (NULL != acc) - free (acc); - hs->failed = GNUNET_YES; - return GNUNET_SYSERR; - } - hs->results_obtained++; - return GNUNET_OK; -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -history_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct HistoryState *hs = cls; - uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX; - const uint64_t *row_ptr; - - (void) cmd; - /* Get row_id from trait. */ - if (NULL != hs->start_row_reference) - { - const struct TALER_TESTING_Command *history_cmd; - - history_cmd = TALER_TESTING_interpreter_lookup_command - (is, hs->start_row_reference); - - if (NULL == history_cmd) - TALER_TESTING_FAIL (is); - - if (GNUNET_OK != - TALER_TESTING_get_trait_uint64 (history_cmd, - 0, - &row_ptr)) - TALER_TESTING_FAIL (is); - else - row_id = *row_ptr; - TALER_LOG_DEBUG ("row id (from trait) is %llu\n", - (unsigned long long) row_id); - } - - hs->hh = TALER_BANK_debit_history (is->ctx, - hs->account_url, - &hs->auth, - row_id, - hs->num_results, - &history_cb, - is); - GNUNET_assert (NULL != hs->hh); -} - - -/** - * Free the state from a "history" CMD, and possibly cancel - * a pending operation thereof. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -history_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct HistoryState *hs = cls; - - (void) cmd; - if (NULL != hs->hh) - { - TALER_LOG_WARNING ("/history did not complete\n"); - TALER_BANK_debit_history_cancel (hs->hh); - } - GNUNET_free (hs); -} - - -/** - * Make a "history" CMD. - * - * @param label command label. - * @param account_url base URL of the account offering the "history" - * operation. - * @param auth login data to use - * @param start_row_reference reference to a command that can - * offer a row identifier, to be used as the starting row - * to accept in the result. - * @param num_results how many rows we want in the result. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_bank_debits (const char *label, - const char *account_url, - const struct TALER_BANK_AuthenticationData *auth, - const char *start_row_reference, - long long num_results) -{ - struct HistoryState *hs; - - hs = GNUNET_new (struct HistoryState); - hs->account_url = account_url; - hs->start_row_reference = start_row_reference; - hs->num_results = num_results; - hs->auth = *auth; - - { - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = hs, - .run = &history_run, - .cleanup = &history_cleanup, - .traits = &history_traits - }; - - return cmd; - } -} - - -/* end of testing_api_cmd_history_debit.c */ diff --git a/src/bank-lib/testing_api_cmd_transfer.c b/src/bank-lib/testing_api_cmd_transfer.c deleted file mode 100644 index d5a3872ed..000000000 --- a/src/bank-lib/testing_api_cmd_transfer.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018-2020 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 - -*/ -/** - * @file exchange-lib/testing_api_cmd_transfer.c - * @brief implementation of a bank /transfer command - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "backoff.h" -#include "taler_json_lib.h" -#include -#include "taler_bank_service.h" -#include "taler_fakebank_lib.h" -#include "taler_signatures.h" -#include "taler_testing_lib.h" -#include "taler_testing_bank_lib.h" - - -/** - * State for a "transfer" CMD. - */ -struct TransferState -{ - - /** - * Wire transfer amount. - */ - struct TALER_Amount amount; - - /** - * Base URL of the debit account. - */ - const char *account_debit_url; - - /** - * Money receiver account URL. - */ - const char *payto_credit_account; - - /** - * Username to use for authentication. - */ - struct TALER_BANK_AuthenticationData auth; - - /** - * Base URL of the exchange. - */ - const char *exchange_base_url; - - /** - * Wire transfer identifier to use. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * Handle to the pending request at the fakebank. - */ - struct TALER_BANK_WireExecuteHandle *weh; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Set to the wire transfer's unique ID. - */ - uint64_t serial_id; - - /** - * Timestamp of the transaction (as returned from the bank). - */ - struct GNUNET_TIME_Absolute timestamp; - - /** - * Configuration filename. Used to get the tip reserve key - * filename (used to obtain a public key to write in the - * transfer subject). - */ - const char *config_filename; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Was this command modified via - * #TALER_TESTING_cmd_admin_add_incoming_with_retry to - * enable retries? - */ - int do_retry; -}; - - -/** - * Run the "transfer" CMD. - * - * @param cls closure. - * @param cmd CMD being run. - * @param is interpreter state. - */ -static void -transfer_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #transfer_run. - * - * @param cls a `struct TransferState` - */ -static void -do_retry (void *cls) -{ - struct TransferState *fts = cls; - - fts->retry_task = NULL; - transfer_run (fts, - NULL, - fts->is); -} - - -/** - * This callback will process the fakebank response to the wire - * transfer. It just checks whether the HTTP response code is - * acceptable. - * - * @param cls closure with the interpreter state - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for - * successful status request; 0 if the exchange's reply is - * bogus (fails to follow the protocol) - * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param serial_id unique ID of the wire transfer - * @param timestamp time stamp of the transaction made. - */ -static void -confirmation_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t serial_id, - struct GNUNET_TIME_Absolute timestamp) -{ - struct TransferState *fts = cls; - struct TALER_TESTING_Interpreter *is = fts->is; - - fts->weh = NULL; - if (MHD_HTTP_OK != http_status) - { - if (GNUNET_YES == fts->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying transfer failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - fts->backoff = GNUNET_TIME_UNIT_ZERO; - else - fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); - fts->retry_task = GNUNET_SCHEDULER_add_delayed - (fts->backoff, - &do_retry, - fts); - return; - } - } - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fakebank returned HTTP status %u/%d\n", - http_status, - (int) ec); - TALER_TESTING_interpreter_fail (is); - return; - } - - fts->serial_id = serial_id; - fts->timestamp = timestamp; - TALER_TESTING_interpreter_next (is); -} - - -/** - * Run the "transfer" CMD. - * - * @param cls closure. - * @param cmd CMD being run. - * @param is interpreter state. - */ -static void -transfer_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct TransferState *fts = cls; - void *buf; - size_t buf_size; - - TALER_BANK_prepare_wire_transfer (fts->payto_credit_account, - &fts->amount, - fts->exchange_base_url, - &fts->wtid, - &buf, - &buf_size); - fts->is = is; - fts->weh - = TALER_BANK_execute_wire_transfer - (TALER_TESTING_interpreter_get_context (is), - fts->account_debit_url, - &fts->auth, - buf, - buf_size, - &confirmation_cb, - fts); - GNUNET_free (buf); - if (NULL == fts->weh) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } -} - - -/** - * Free the state of a "fakebank transfer" CMD, and possibly - * cancel a pending operation thereof. - * - * @param cls closure - * @param cmd current CMD being cleaned up. - */ -static void -transfer_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct TransferState *fts = cls; - - if (NULL != fts->weh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %s did not complete\n", - cmd->label); - TALER_BANK_execute_wire_transfer_cancel (fts->weh); - fts->weh = NULL; - } - if (NULL != fts->retry_task) - { - GNUNET_SCHEDULER_cancel (fts->retry_task); - fts->retry_task = NULL; - } - GNUNET_free (fts); -} - - -/** - * Offer internal data from a "fakebank transfer" CMD to other - * commands. - * - * @param cls closure. - * @param ret[out] result - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -transfer_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct TransferState *fts = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_url (1, fts->account_debit_url), - TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), - TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->payto_credit_account), - TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT (fts->account_debit_url), - TALER_TESTING_make_trait_amount_obj (0, &fts->amount), - TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), - TALER_TESTING_make_trait_wtid (0, - &fts->wtid), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Create transfer command. - * - * @param label command label. - * @param amount amount to transfer. - * @param account_base_url base URL of the account that implements this - * wire transer (which account gives money). - * @param auth authentication data to use - * @param payto_credit_account which account receives money. - * @param wtid wire transfer identifier to use - * @param exchange_base_url exchange URL to use - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_transfer - (const char *label, - const char *amount, - const char *account_base_url, - const struct TALER_BANK_AuthenticationData *auth, - const char *payto_credit_account, - const struct TALER_WireTransferIdentifierRawP *wtid, - const char *exchange_base_url) -{ - struct TransferState *fts; - - fts = GNUNET_new (struct TransferState); - fts->account_debit_url = account_base_url; - fts->exchange_base_url = exchange_base_url; - fts->payto_credit_account = payto_credit_account; - fts->auth = *auth; - fts->wtid = *wtid; - if (GNUNET_OK != - TALER_string_to_amount (amount, - &fts->amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %s\n", - amount, - label); - GNUNET_assert (0); - } - - { - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &transfer_run, - .cleanup = &transfer_cleanup, - .traits = &transfer_traits - }; - - return cmd; - } -} - - -/** - * Modify a transfer command to enable retries when the reserve is not yet - * full or we get other transient errors from the bank. - * - * @param cmd a fakebank transfer command - * @return the command with retries enabled - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd) -{ - struct TransferState *fts; - - GNUNET_assert (&transfer_run == cmd.run); - fts = cmd.cls; - fts->do_retry = GNUNET_YES; - return cmd; -} - - -/* end of testing_api_cmd_transfer.c */ diff --git a/src/bank-lib/testing_api_helpers.c b/src/bank-lib/testing_api_helpers.c deleted file mode 100644 index 64976edbb..000000000 --- a/src/bank-lib/testing_api_helpers.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 - -*/ - -/** - * @file bank-lib/testing_api_helpers.c - * @brief convenience functions for bank-lib tests. - * @author Marcello Stanisci - */ - -#include "platform.h" -#include -#include "taler_testing_bank_lib.h" -#include "taler_fakebank_lib.h" - - -#define BANK_FAIL() \ - do {GNUNET_break (0); return NULL; } while (0) - - -/** - * Keep each bank account credentials at index: - * bank account number - 1 - */ -struct TALER_BANK_AuthenticationData AUTHS[] = { - - /* Bank credentials */ - {.method = TALER_BANK_AUTH_BASIC, - .details.basic.username = TALER_TESTING_BANK_USERNAME, - .details.basic.password = TALER_TESTING_BANK_PASSWORD}, - - /* Exchange credentials */ - {.method = TALER_BANK_AUTH_BASIC, - .details.basic.username = TALER_TESTING_EXCHANGE_USERNAME, - .details.basic.password = TALER_TESTING_EXCHANGE_PASSWORD }, - - /* User credentials */ - {.method = TALER_BANK_AUTH_BASIC, - .details.basic.username = TALER_TESTING_USER_USERNAME, - .details.basic.password = TALER_TESTING_USER_PASSWORD } -}; - - -/** - * Runs the Fakebank by guessing / extracting the portnumber - * from the base URL. - * - * @param bank_url bank's base URL. - * @return the fakebank process handle, or NULL if any - * error occurs. - */ -struct TALER_FAKEBANK_Handle * -TALER_TESTING_run_fakebank (const char *bank_url) -{ - const char *port; - long pnum; - struct TALER_FAKEBANK_Handle *fakebankd; - - port = strrchr (bank_url, - (unsigned char) ':'); - if (NULL == port) - pnum = 80; - else - pnum = strtol (port + 1, NULL, 10); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting Fakebank on port %u (%s)\n", - (unsigned int) pnum, - bank_url); - fakebankd = TALER_FAKEBANK_start ((uint16_t) pnum); - if (NULL == fakebankd) - { - GNUNET_break (0); - return NULL; - } - return fakebankd; -} - - -/** - * Look for substring in a programs' name. - * - * @param prog program's name to look into - * @param marker chunk to find in @a prog - */ -int -TALER_TESTING_has_in_name (const char *prog_name, - const char *marker) -{ - size_t name_pos; - size_t pos; - - if (! prog_name || ! marker) - return GNUNET_NO; - - pos = 0; - name_pos = 0; - while (prog_name[pos]) - { - if ('/' == prog_name[pos]) - name_pos = pos + 1; - pos++; - } - if (name_pos == pos) - return GNUNET_YES; - return strstr (prog_name + name_pos, marker) != NULL; -} - - -/** - * Start the (Python) bank process. Assume the port - * is available and the database is clean. Use the "prepare - * bank" function to do such tasks. - * - * @param config_filename configuration filename. - * @param bank_url base URL of the bank, used by `wget' to check - * that the bank was started right. - * - * @return the process, or NULL if the process could not - * be started. - */ -struct GNUNET_OS_Process * -TALER_TESTING_run_bank (const char *config_filename, - const char *bank_url) -{ - struct GNUNET_OS_Process *bank_proc; - unsigned int iter; - char *wget_cmd; - char *database; - char *serve_cfg; - char *serve_arg; - struct GNUNET_CONFIGURATION_Handle *cfg; - - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - config_filename)) - { - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - exit (77); - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "bank", - "database", - &database)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "bank", - "database"); - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - exit (77); - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "bank", - "serve", - &serve_cfg)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "bank", - "serve"); - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (database); - exit (77); - } - GNUNET_CONFIGURATION_destroy (cfg); - - serve_arg = "serve-http"; - if (0 != strcmp ("http", serve_cfg)) - serve_arg = "serve-uwsgi"; - GNUNET_free (serve_cfg); - bank_proc = GNUNET_OS_start_process - (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-bank-manage-testing", - "taler-bank-manage-testing", - config_filename, - database, - serve_arg, NULL); - GNUNET_free (database); - if (NULL == bank_proc) - { - BANK_FAIL (); - } - - GNUNET_asprintf (&wget_cmd, - "wget -q -t 2 -T 1 %s -o /dev/null -O /dev/null", - bank_url); - - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `taler-bank-manage' to be ready"); - iter = 0; - do - { - if (10 == iter) - { - fprintf ( - stderr, - "Failed to launch `taler-bank-manage' (or `wget')\n"); - GNUNET_OS_process_kill (bank_proc, - SIGTERM); - GNUNET_OS_process_wait (bank_proc); - GNUNET_OS_process_destroy (bank_proc); - GNUNET_free (wget_cmd); - BANK_FAIL (); - } - fprintf (stderr, "."); - sleep (1); - iter++; - } - while (0 != system (wget_cmd)); - GNUNET_free (wget_cmd); - fprintf (stderr, "\n"); - - return bank_proc; - -} - - -/** - * Prepare the bank execution. Check if the port is available - * and reset database. - * - * @param config_filename configuration file name. - * - * @return the base url, or NULL upon errors. Must be freed - * by the caller. - */ -char * -TALER_TESTING_prepare_bank (const char *config_filename) -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - unsigned long long port; - struct GNUNET_OS_Process *dbreset_proc; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - char *base_url; - char *database; - - cfg = GNUNET_CONFIGURATION_create (); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, config_filename)) - { - GNUNET_CONFIGURATION_destroy (cfg); - BANK_FAIL (); - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "bank", - "DATABASE", - &database)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "bank", - "DATABASE"); - GNUNET_CONFIGURATION_destroy (cfg); - BANK_FAIL (); - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "bank", - "HTTP_PORT", - &port)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "bank", - "HTTP_PORT"); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (database); - BANK_FAIL (); - } - GNUNET_CONFIGURATION_destroy (cfg); - - if (GNUNET_OK != GNUNET_NETWORK_test_port_free - (IPPROTO_TCP, (uint16_t) port)) - { - fprintf (stderr, - "Required port %llu not available, skipping.\n", - port); - BANK_FAIL (); - } - - /* DB preparation */ - if (NULL == - (dbreset_proc = GNUNET_OS_start_process ( - GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-bank-manage", - "taler-bank-manage", - "-c", "bank.conf", - "--with-db", database, - "django", - "flush", - "--no-input", NULL))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to flush the bank db.\n"); - GNUNET_free (database); - BANK_FAIL (); - } - GNUNET_free (database); - - if (GNUNET_SYSERR == - GNUNET_OS_process_wait_status (dbreset_proc, - &type, - &code)) - { - GNUNET_OS_process_destroy (dbreset_proc); - BANK_FAIL (); - } - if ( (type == GNUNET_OS_PROCESS_EXITED) && - (0 != code) ) - { - fprintf (stderr, - "Failed to setup database\n"); - BANK_FAIL (); - } - if ( (type != GNUNET_OS_PROCESS_EXITED) || - (0 != code) ) - { - fprintf (stderr, - "Unexpected error running" - " `taler-bank-manage django flush..'!\n"); - BANK_FAIL (); - } - - GNUNET_OS_process_destroy (dbreset_proc); - - GNUNET_asprintf (&base_url, - "http://localhost:%llu/", - port); - return base_url; -} - - -/* end of testing_api_helpers.c */ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index d46506600..231e049a0 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -9,8 +9,7 @@ endif lib_LTLIBRARIES = \ libtalerauditor.la \ libtalerexchange.la \ - libtalertesting.la \ - libtalerauditortesting.la + libtalertesting.la libtalerexchange_la_LDFLAGS = \ -version-info 4:0:0 \ @@ -72,46 +71,57 @@ libtalertesting_la_LDFLAGS = \ -no-undefined libtalertesting_la_SOURCES = \ exchange_api_curl_defaults.c \ + testing_api_cmd_auditor_deposit_confirmation.c \ + testing_api_cmd_auditor_exchanges.c \ + testing_api_cmd_auditor_exec_auditor.c \ + testing_api_cmd_auditor_exec_auditor_dbinit.c \ + testing_api_cmd_auditor_exec_wire_auditor.c \ + testing_api_cmd_bank_admin_add_incoming.c \ + testing_api_cmd_bank_check.c \ + testing_api_cmd_bank_check_empty.c \ + testing_api_cmd_bank_history_credit.c \ + testing_api_cmd_bank_history_debit.c \ + testing_api_cmd_bank_transfer.c \ + testing_api_cmd_batch.c \ + testing_api_cmd_check_keys.c \ + testing_api_cmd_deposit.c \ testing_api_cmd_exec_aggregator.c \ testing_api_cmd_exec_wirewatch.c \ testing_api_cmd_exec_keyup.c \ testing_api_cmd_exec_auditor-sign.c \ - testing_api_cmd_withdraw.c \ - testing_api_cmd_wire.c \ + testing_api_cmd_payback.c \ testing_api_cmd_refund.c \ - testing_api_cmd_status.c \ - testing_api_cmd_deposit.c \ - testing_api_cmd_sleep.c \ testing_api_cmd_refresh.c \ - testing_api_cmd_track.c \ - testing_api_cmd_bank_check.c \ - testing_api_cmd_bank_check_empty.c \ - testing_api_cmd_payback.c \ - testing_api_cmd_signal.c \ - testing_api_cmd_check_keys.c \ - testing_api_cmd_batch.c \ testing_api_cmd_serialize_keys.c \ - testing_api_helpers.c \ + testing_api_cmd_signal.c \ + testing_api_cmd_sleep.c \ + testing_api_cmd_status.c \ + testing_api_cmd_track.c \ + testing_api_cmd_wire.c \ + testing_api_cmd_withdraw.c \ + testing_api_helpers_auditor.c \ + testing_api_helpers_bank.c \ + testing_api_helpers_exchange.c \ testing_api_loop.c \ testing_api_traits.c \ + testing_api_trait_amount.c \ testing_api_trait_blinding_key.c \ + testing_api_trait_cmd.c \ testing_api_trait_coin_priv.c \ testing_api_trait_denom_pub.c \ testing_api_trait_denom_sig.c \ testing_api_trait_exchange_pub.c \ testing_api_trait_exchange_sig.c \ + testing_api_trait_fresh_coin.c \ testing_api_trait_json.c \ + testing_api_trait_key_peer.c \ + testing_api_trait_number.c \ testing_api_trait_process.c \ testing_api_trait_reserve_pub.c \ testing_api_trait_reserve_priv.c \ - testing_api_trait_number.c \ - testing_api_trait_fresh_coin.c \ testing_api_trait_string.c \ - testing_api_trait_key_peer.c \ - testing_api_trait_wtid.c \ - testing_api_trait_amount.c \ - testing_api_trait_cmd.c \ - testing_api_trait_time.c + testing_api_trait_time.c \ + testing_api_trait_wtid.c libtalertesting_la_LIBADD = \ libtalerexchange.la \ $(top_builddir)/src/wire/libtalerwire.la \ @@ -125,30 +135,6 @@ libtalertesting_la_LIBADD = \ -ljansson \ $(XLIB) -libtalerauditortesting_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined -libtalerauditortesting_la_SOURCES = \ - testing_auditor_api_helpers.c \ - testing_auditor_api_cmd_deposit_confirmation.c \ - testing_auditor_api_cmd_exchanges.c \ - testing_auditor_api_cmd_exec_auditor.c \ - testing_auditor_api_cmd_exec_auditor_dbinit.c \ - testing_auditor_api_cmd_exec_wire_auditor.c -libtalerauditortesting_la_LIBADD = \ - libtalerauditor.la \ - libtalerexchange.la \ - libtalertesting.la \ - $(top_builddir)/src/wire/libtalerwire.la \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetcurl \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - $(XLIB) - - if HAVE_LIBCURL libtalerexchange_la_LIBADD += -lcurl else @@ -158,15 +144,36 @@ endif endif check_PROGRAMS = \ - test_exchange_api_keys_cherry_picking \ - test_exchange_api_overlapping_keys_bug \ - test_exchange_api \ test_auditor_api_version \ - test_auditor_api + test_auditor_api \ + test_bank_api_with_fakebank \ + test_bank_api_with_pybank \ + test_exchange_api \ + test_exchange_api_keys_cherry_picking \ + test_exchange_api_overlapping_keys_bug if HAVE_TWISTER check_PROGRAMS += \ - test_exchange_api_twisted + test_exchange_api_twisted \ + test_bank_api_with_pybank_twisted \ + test_bank_api_with_fakebank_twisted +endif + +test_bank_api_with_pybank_SOURCES = \ + test_bank_api.c +test_bank_api_with_pybank_LDADD = \ + libtalertesting.la \ + libtalerexchange.la \ + -lgnunetutil \ + $(top_builddir)/src/bank-lib/libtalerbank.la + +test_bank_api_with_fakebank_SOURCES = \ + test_bank_api.c +test_bank_api_with_fakebank_LDADD = \ + $(top_builddir)/src/lib/libtalertesting.la \ + -ltalerexchange \ + -lgnunetutil \ + libtalerbank.la test_exchange_api_twisted_SOURCES = \ test_exchange_api_twisted.c @@ -175,7 +182,6 @@ test_exchange_api_twisted_LDADD = \ libtalertesting.la \ libtalerexchange.la \ $(top_builddir)/src/bank-lib/libtalerfakebank.la \ - $(top_builddir)/src/bank-lib/libtalerbanktesting.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ @@ -185,7 +191,35 @@ test_exchange_api_twisted_LDADD = \ -lgnunetutil \ -ljansson -endif +test_bank_api_with_fakebank_twisted_SOURCES = \ + test_bank_api_twisted.c +test_bank_api_with_fakebank_twisted_LDADD = \ + $(top_builddir)/src/lib/libtalertesting.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(top_builddir)/src/json/libtalerjson.la \ + -ltalertwistertesting \ + -lgnunetjson \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson + +test_bank_api_with_pybank_twisted_SOURCES = \ + test_bank_api_twisted.c +test_bank_api_with_pybank_twisted_LDADD = \ + $(top_builddir)/src/lib/libtalertesting.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(top_builddir)/src/json/libtalerjson.la \ + -ltalertwistertesting \ + -lgnunetjson \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson + + AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; @@ -200,7 +234,6 @@ test_exchange_api_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/bank-lib/libtalerfakebank.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/bank-lib/libtalerbanktesting.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetcurl \ @@ -236,14 +269,12 @@ test_exchange_api_keys_cherry_picking_LDADD = \ test_auditor_api_SOURCES = \ test_auditor_api.c test_auditor_api_LDADD = \ - libtalerauditortesting.la \ libtalerauditor.la \ libtalertesting.la \ libtalerexchange.la \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/bank-lib/libtalerfakebank.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/bank-lib/libtalerbanktesting.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetcurl \ @@ -264,6 +295,8 @@ test_auditor_api_version_LDADD = \ EXTRA_DIST = \ + bank.conf \ + bank_twisted.conf \ test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv \ test_exchange_api_home/.config/taler/account-2.json \ test_exchange_api_keys_cherry_picking_home/.config/taler/x-taler-bank.json \ diff --git a/src/lib/bank.conf b/src/lib/bank.conf new file mode 100644 index 000000000..906b95fc5 --- /dev/null +++ b/src/lib/bank.conf @@ -0,0 +1,13 @@ +[taler] +currency = KUDOS + +[account-1] +URL = payto://x-taler-bank/localhost:8081/1 + +[bank] +SERVE = http +HTTP_PORT = 8081 +DATABASE = postgres:///talercheck + +[exchange-wire-test] +bank_url = http://localhost:8081/ diff --git a/src/lib/bank_twisted.conf b/src/lib/bank_twisted.conf new file mode 100644 index 000000000..71bcaee00 --- /dev/null +++ b/src/lib/bank_twisted.conf @@ -0,0 +1,39 @@ + +[twister] +# HTTP listen port for twister +HTTP_PORT = 8888 +SERVE = tcp + +# HTTP Destination for twister. The test-Webserver needs +# to listen on the port used here. Note: no trailing '/'! +DESTINATION_BASE_URL = "http://localhost:8081" + +# Control port for TCP +# PORT = 8889 +HOSTNAME = localhost +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; + +# Control port for UNIX +UNIXPATH = /tmp/taler-service-twister.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES + +# Launching of twister by ARM +# BINARY = taler-service-twister +# AUTOSTART = NO +# FORCESTART = NO + +[taler] +currency = KUDOS + +[bank] +serve = http +http_port = 8081 +database = postgres:///talercheck + +[account-1] +URL = payto://x-taler-bank/localhost:8081/1 + +[exchange-wire-test] +bank_url = http://localhost:8081/ diff --git a/src/lib/test_bank_api_twisted.c b/src/lib/test_bank_api_twisted.c new file mode 100644 index 000000000..ad8fd71d4 --- /dev/null +++ b/src/lib/test_bank_api_twisted.c @@ -0,0 +1,222 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 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 + +*/ +/** + * @file exchange/test_bank_api_with_fakebank_twisted.c + * @author Marcello Stanisci + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include +#include +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_testing_lib.h" +#include +#include "taler_testing_bank_lib.h" +#include + +/** + * Configuration file we use. One (big) configuration is used + * for the various components for this test. + */ +#define CONFIG_FILE "bank_twisted.conf" + +/** + * True when the test runs against Fakebank. + */ +static int with_fakebank; + +/** + * (real) Twister URL. Used at startup time to check if it runs. + */ +static char *twister_url; + +/** + * Account URL of the twister where all the connections to the + * bank that have to be proxied should be addressed to. + */ +static char *twisted_account_url; + +/** + * Authentication data to use. + */ +static struct TALER_BANK_AuthenticationData auth; + +/** + * URL of the bank. + */ +static char *bank_url; + +/** + * Twister process. + */ +static struct GNUNET_OS_Process *twisterd; + +/** + * Python bank process handle. + */ +static struct GNUNET_OS_Process *bankd; + + +/** + * Main function that will tell + * the interpreter what commands to run. + * + * @param cls closure + */ +static void +run (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + struct TALER_TESTING_Command commands[] = { + /** + * Can't use the "wait service" CMD here because the + * fakebank runs inside the same process of the test. + */ + TALER_TESTING_cmd_wait_service ("wait-service", + twister_url), + TALER_TESTING_cmd_bank_credits ("history-0", + twisted_account_url, + &auth, + NULL, + 5), + TALER_TESTING_cmd_end () + }; + + GNUNET_asprintf (&twisted_account_url, + "%s/%s", + twister_url, + "alice"); + // FIXME: init 'auth'! + if (GNUNET_YES == with_fakebank) + TALER_TESTING_run_with_fakebank (is, + commands, + bank_url); + else + TALER_TESTING_run (is, + commands); +} + + +/** + * Kill, wait, and destroy convenience function. + * + * @param process process to purge. + */ +static void +purge_process (struct GNUNET_OS_Process *process) +{ + GNUNET_OS_process_kill (process, SIGINT); + GNUNET_OS_process_wait (process); + GNUNET_OS_process_destroy (process); +} + + +int +main (int argc, + char *const *argv) +{ + unsigned int ret; + + /* These environment variables get in the way... */ + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup ("test-bank-api-with-(fake)bank-twisted", + "DEBUG", + NULL); + if (NULL == (twister_url = TALER_TESTING_prepare_twister + (CONFIG_FILE))) + { + GNUNET_break (0); + return 77; + } + if (NULL == (twisterd = TALER_TESTING_run_twister (CONFIG_FILE))) + { + GNUNET_break (0); + GNUNET_free (twister_url); + return 77; + } + + with_fakebank = TALER_TESTING_has_in_name (argv[0], + "_with_fakebank"); + + if (GNUNET_YES == with_fakebank) + { + TALER_LOG_DEBUG ("Running against the Fakebank.\n"); + if (NULL == (bank_url = TALER_TESTING_prepare_fakebank + (CONFIG_FILE, + "account-1"))) + { + GNUNET_break (0); + GNUNET_free (twister_url); + return 77; + } + } + else + { + TALER_LOG_DEBUG ("Running against the Pybank.\n"); + if (NULL == (bank_url = TALER_TESTING_prepare_bank + (CONFIG_FILE))) + { + GNUNET_break (0); + GNUNET_free (twister_url); + return 77; + } + + if (NULL == (bankd = TALER_TESTING_run_bank (CONFIG_FILE, + bank_url))) + { + GNUNET_break (0); + GNUNET_free (twister_url); + GNUNET_free (bank_url); + return 77; + } + } + + ret = TALER_TESTING_setup (&run, + NULL, + CONFIG_FILE, + NULL, + GNUNET_NO); + purge_process (twisterd); + + if (GNUNET_NO == with_fakebank) + { + GNUNET_OS_process_kill (bankd, + SIGKILL); + GNUNET_OS_process_wait (bankd); + GNUNET_OS_process_destroy (bankd); + } + + GNUNET_free (twister_url); + GNUNET_free (bank_url); + + if (GNUNET_OK == ret) + return 0; + + return 1; +} + + +/* end of test_bank_api_twisted.c */ diff --git a/src/lib/testing_api_cmd_auditor_deposit_confirmation.c b/src/lib/testing_api_cmd_auditor_deposit_confirmation.c new file mode 100644 index 000000000..3ea6390d8 --- /dev/null +++ b/src/lib/testing_api_cmd_auditor_deposit_confirmation.c @@ -0,0 +1,444 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 + +*/ + +/** + * @file auditor-lib/testing_auditor_api_cmd_deposit_confirmation.c + * @brief command for testing /deposit_confirmation. + * @author Christian Grothoff + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_auditor_service.h" +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "deposit confirmation" CMD. + */ +struct DepositConfirmationState +{ + + /** + * Reference to any command that is able to provide a deposit. + */ + const char *deposit_reference; + + /** + * What is the deposited amount without the fee (i.e. the + * amount we expect in the deposit confirmation)? + */ + const char *amount_without_fee; + + /** + * Which coin of the @e deposit_reference should we confirm. + */ + unsigned int coin_index; + + /** + * DepositConfirmation handle while operation is running. + */ + struct TALER_AUDITOR_DepositConfirmationHandle *dc; + + /** + * Auditor connection. + */ + struct TALER_AUDITOR_Handle *auditor; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Task scheduled to try later. + */ + struct GNUNET_SCHEDULER_Task *retry_task; + + /** + * How long do we wait until we retry? + */ + struct GNUNET_TIME_Relative backoff; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Should we retry on (transient) failures? + */ + int do_retry; + +}; + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +deposit_confirmation_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is); + + +/** + * Task scheduled to re-try #deposit_confirmation_run. + * + * @param cls a `struct DepositConfirmationState` + */ +static void +do_retry (void *cls) +{ + struct DepositConfirmationState *dcs = cls; + + dcs->retry_task = NULL; + deposit_confirmation_run (dcs, + NULL, + dcs->is); +} + + +/** + * Callback to analyze the /deposit-confirmation response, just used + * to check if the response code is acceptable. + * + * @param cls closure. + * @param http_status HTTP response code. + * @param ec taler-specific error code. + * @param obj raw response from the auditor. + */ +static void +deposit_confirmation_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *obj) +{ + struct DepositConfirmationState *dcs = cls; + + dcs->dc = NULL; + if (dcs->expected_response_code != http_status) + { + if (GNUNET_YES == dcs->do_retry) + { + if ( (0 == http_status) || + (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || + (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Retrying deposit confirmation failed with %u/%d\n", + http_status, + (int) ec); + /* on DB conflicts, do not use backoff */ + if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) + dcs->backoff = GNUNET_TIME_UNIT_ZERO; + else + dcs->backoff = EXCHANGE_LIB_BACKOFF (dcs->backoff); + dcs->retry_task = GNUNET_SCHEDULER_add_delayed (dcs->backoff, + &do_retry, + dcs); + return; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s in %s:%u\n", + http_status, + dcs->is->commands[dcs->is->ip].label, + __FILE__, + __LINE__); + json_dumpf (obj, stderr, 0); + TALER_TESTING_interpreter_fail (dcs->is); + return; + } + TALER_TESTING_interpreter_next (dcs->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +deposit_confirmation_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct DepositConfirmationState *dcs = cls; + const struct TALER_TESTING_Command *deposit_cmd; + struct GNUNET_HashCode h_wire; + struct GNUNET_HashCode h_contract_terms; + struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Absolute refund_deadline; + struct TALER_Amount amount_without_fee; + struct TALER_CoinSpendPublicKeyP coin_pub; + const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv; + struct TALER_MerchantPublicKeyP merchant_pub; + const struct TALER_ExchangePublicKeyP *exchange_pub; + const struct TALER_ExchangeSignatureP *exchange_sig; + const json_t *wire_details; + const json_t *contract_terms; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_EXCHANGE_Keys *keys; + const struct TALER_EXCHANGE_SigningPublicKey *spk; + + dcs->is = is; + GNUNET_assert (NULL != dcs->deposit_reference); + deposit_cmd + = TALER_TESTING_interpreter_lookup_command (is, + dcs->deposit_reference); + if (NULL == deposit_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_pub (deposit_cmd, + dcs->coin_index, + &exchange_pub)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_sig (deposit_cmd, + dcs->coin_index, + &exchange_sig)); + keys = TALER_EXCHANGE_get_keys (dcs->is->exchange); + GNUNET_assert (NULL != keys); + spk = TALER_EXCHANGE_get_exchange_signing_key_info (keys, + exchange_pub); + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_contract_terms (deposit_cmd, + dcs->coin_index, + &contract_terms)); + /* Very unlikely to fail */ + GNUNET_assert (NULL != contract_terms); + GNUNET_assert (GNUNET_OK == + TALER_JSON_hash (contract_terms, + &h_contract_terms)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_wire_details (deposit_cmd, + dcs->coin_index, + &wire_details)); + GNUNET_assert (GNUNET_OK == + TALER_JSON_merchant_wire_signature_hash (wire_details, + &h_wire)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_coin_priv (deposit_cmd, + dcs->coin_index, + &coin_priv)); + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &coin_pub.eddsa_pub); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_peer_key (deposit_cmd, + dcs->coin_index, + &merchant_priv)); + GNUNET_CRYPTO_eddsa_key_get_public (merchant_priv, + &merchant_pub.eddsa_pub); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (dcs->amount_without_fee, + &amount_without_fee)); + /* timestamp is mandatory */ + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_absolute_time ("timestamp", ×tamp), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (contract_terms, + spec, + NULL, NULL)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + } + /* refund deadline is optional, defaults to zero */ + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (contract_terms, + spec, + NULL, NULL)) + { + refund_deadline = timestamp; + } + } + dcs->dc = TALER_AUDITOR_deposit_confirmation + (dcs->auditor, + &h_wire, + &h_contract_terms, + timestamp, + refund_deadline, + &amount_without_fee, + &coin_pub, + &merchant_pub, + exchange_pub, + exchange_sig, + &keys->master_pub, + spk->valid_from, + spk->valid_until, + spk->valid_legal, + &spk->master_sig, + &deposit_confirmation_cb, + dcs); + + if (NULL == dcs->dc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + return; +} + + +/** + * Free the state of a "deposit_confirmation" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, a `struct DepositConfirmationState` + * @param cmd the command which is being cleaned up. + */ +static void +deposit_confirmation_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct DepositConfirmationState *dcs = cls; + + if (NULL != dcs->dc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + dcs->is->ip, + cmd->label); + TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc); + dcs->dc = NULL; + } + if (NULL != dcs->retry_task) + { + GNUNET_SCHEDULER_cancel (dcs->retry_task); + dcs->retry_task = NULL; + } + GNUNET_free (dcs); +} + + +/** + * Offer internal data to other commands. + * + * @param cls closure. + * @param ret[out] set to the wanted data. + * @param trait name of the trait. + * @param index index number of the traits to be returned. + * + * @return #GNUNET_OK on success + */ +static int +deposit_confirmation_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + /* Must define this function because some callbacks + * look for certain traits on _all_ the commands. */ + return GNUNET_SYSERR; +} + + +/** + * Create a "deposit-confirmation" command. + * + * @param label command label. + * @param auditor auditor connection. + * @param deposit_reference reference to any operation that can + * provide a coin. + * @param coin_index if @a deposit_reference offers an array of + * coins, this parameter selects which one in that array. + * This value is currently ignored, as only one-coin + * deposits are implemented. + * @param amount_without_fee deposited amount without the fee + * @param expected_response_code expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_deposit_confirmation + (const char *label, + struct TALER_AUDITOR_Handle *auditor, + const char *deposit_reference, + unsigned int coin_index, + const char *amount_without_fee, + unsigned int expected_response_code) +{ + struct TALER_TESTING_Command cmd = {0}; /* need explicit zeroing..*/ + struct DepositConfirmationState *dcs; + + dcs = GNUNET_new (struct DepositConfirmationState); + dcs->auditor = auditor; + dcs->deposit_reference = deposit_reference; + dcs->coin_index = coin_index; + dcs->amount_without_fee = amount_without_fee; + dcs->expected_response_code = expected_response_code; + + cmd.cls = dcs; + cmd.label = label; + cmd.run = &deposit_confirmation_run; + cmd.cleanup = &deposit_confirmation_cleanup; + cmd.traits = &deposit_confirmation_traits; + + return cmd; +} + + +/** + * Modify a deposit confirmation command to enable retries when we get + * transient errors from the auditor. + * + * @param cmd a deposit confirmation command + * @return the command with retries enabled + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_deposit_confirmation_with_retry (struct TALER_TESTING_Command + cmd) +{ + struct DepositConfirmationState *dcs; + + GNUNET_assert (&deposit_confirmation_run == cmd.run); + dcs = cmd.cls; + dcs->do_retry = GNUNET_YES; + return cmd; +} + + +/* end of testing_auditor_api_cmd_deposit_confirmation.c */ diff --git a/src/lib/testing_api_cmd_auditor_exchanges.c b/src/lib/testing_api_cmd_auditor_exchanges.c new file mode 100644 index 000000000..9cc07553d --- /dev/null +++ b/src/lib/testing_api_cmd_auditor_exchanges.c @@ -0,0 +1,358 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 + +*/ +/** + * @file auditor-lib/testing_auditor_api_cmd_exchanges.c + * @brief command for testing /exchanges. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_auditor_service.h" +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "deposit confirmation" CMD. + */ +struct ExchangesState +{ + + /** + * Exchanges handle while operation is running. + */ + struct TALER_AUDITOR_ListExchangesHandle *leh; + + /** + * Auditor connection. + */ + struct TALER_AUDITOR_Handle *auditor; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Task scheduled to try later. + */ + struct GNUNET_SCHEDULER_Task *retry_task; + + /** + * How long do we wait until we retry? + */ + struct GNUNET_TIME_Relative backoff; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * URL of the exchange expected to be included in the response. + */ + const char *exchange_url; + + /** + * Should we retry on (transient) failures? + */ + int do_retry; + +}; + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +exchanges_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is); + + +/** + * Task scheduled to re-try #exchanges_run. + * + * @param cls a `struct ExchangesState` + */ +static void +do_retry (void *cls) +{ + struct ExchangesState *es = cls; + + es->retry_task = NULL; + exchanges_run (es, + NULL, + es->is); +} + + +/** + * Callback to analyze the /exchanges response. + * + * @param cls closure. + * @param http_status HTTP response code. + * @param ec taler-specific error code. + * @param obj raw response from the auditor. + */ +static void +exchanges_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + unsigned int num_exchanges, + const struct TALER_AUDITOR_ExchangeInfo *ei, + const json_t *raw_response) +{ + struct ExchangesState *es = cls; + + es->leh = NULL; + if (es->expected_response_code != http_status) + { + if (GNUNET_YES == es->do_retry) + { + if ( (0 == http_status) || + (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || + (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Retrying list exchanges failed with %u/%d\n", + http_status, + (int) ec); + /* on DB conflicts, do not use backoff */ + if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) + es->backoff = GNUNET_TIME_UNIT_ZERO; + else + es->backoff = EXCHANGE_LIB_BACKOFF (es->backoff); + es->retry_task = GNUNET_SCHEDULER_add_delayed (es->backoff, + &do_retry, + es); + return; + } + } + GNUNET_log + (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to command %s in %s:%u\n", + http_status, + es->is->commands[es->is->ip].label, + __FILE__, + __LINE__); + json_dumpf (raw_response, stderr, 0); + TALER_TESTING_interpreter_fail (es->is); + return; + } + if (NULL != es->exchange_url) + { + unsigned int found = GNUNET_NO; + + for (unsigned int i = 0; + iexchange_url, + ei[i].exchange_url)) + found = GNUNET_YES; + if (GNUNET_NO == found) + { + TALER_LOG_ERROR + ("Exchange '%s' doesn't exist at this auditor\n", + es->exchange_url); + TALER_TESTING_interpreter_fail (es->is); + return; + } + + TALER_LOG_DEBUG ("Exchange '%s' exists at this auditor!\n", + es->exchange_url); + } + TALER_TESTING_interpreter_next (es->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +exchanges_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct ExchangesState *es = cls; + + es->is = is; + es->leh = TALER_AUDITOR_list_exchanges + (is->auditor, + &exchanges_cb, + es); + + if (NULL == es->leh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + return; +} + + +/** + * Free the state of a "exchanges" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, a `struct ExchangesState` + * @param cmd the command which is being cleaned up. + */ +static void +exchanges_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct ExchangesState *es = cls; + + if (NULL != es->leh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + es->is->ip, + cmd->label); + TALER_AUDITOR_list_exchanges_cancel (es->leh); + es->leh = NULL; + } + if (NULL != es->retry_task) + { + GNUNET_SCHEDULER_cancel (es->retry_task); + es->retry_task = NULL; + } + GNUNET_free (es); +} + + +/** + * Offer internal data to other commands. + * + * @param cls closure. + * @param ret[out] set to the wanted data. + * @param trait name of the trait. + * @param index index number of the traits to be returned. + * + * @return #GNUNET_OK on success + */ +static int +exchanges_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + /* Must define this function because some callbacks + * look for certain traits on _all_ the commands. */ + return GNUNET_SYSERR; +} + + +/** + * Create a "list exchanges" command. + * + * @param label command label. + * @param auditor auditor connection. + * @param expected_response_code expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exchanges + (const char *label, + struct TALER_AUDITOR_Handle *auditor, + unsigned int expected_response_code) +{ + struct TALER_TESTING_Command cmd = {0}; /* need explicit zeroing..*/ + struct ExchangesState *es; + + es = GNUNET_new (struct ExchangesState); + es->auditor = auditor; + es->expected_response_code = expected_response_code; + + cmd.cls = es; + cmd.label = label; + cmd.run = &exchanges_run; + cmd.cleanup = &exchanges_cleanup; + cmd.traits = &exchanges_traits; + + return cmd; +} + + +/** + * Create a "list exchanges" command and check whether + * a particular exchange belongs to the returned bundle. + * + * @param label command label. + * @param auditor auditor connection. + * @param expected_response_code expected HTTP response code. + * @param exchange_url URL of the exchange supposed to + * be included in the response. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exchanges_with_url + (const char *label, + unsigned int expected_response_code, + const char *exchange_url) +{ + struct TALER_TESTING_Command cmd = {0}; /* need explicit zeroing..*/ + struct ExchangesState *es; + + es = GNUNET_new (struct ExchangesState); + es->expected_response_code = expected_response_code; + es->exchange_url = exchange_url; + + cmd.cls = es; + cmd.label = label; + cmd.run = &exchanges_run; + cmd.cleanup = &exchanges_cleanup; + cmd.traits = &exchanges_traits; + + return cmd; +} + + +/** + * Modify an exchanges command to enable retries when we get + * transient errors from the auditor. + * + * @param cmd a deposit confirmation command + * @return the command with retries enabled + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exchanges_with_retry (struct TALER_TESTING_Command cmd) +{ + struct ExchangesState *es; + + GNUNET_assert (&exchanges_run == cmd.run); + es = cmd.cls; + es->do_retry = GNUNET_YES; + return cmd; +} + + +/* end of testing_auditor_api_cmd_exchanges.c */ diff --git a/src/lib/testing_api_cmd_auditor_exec_auditor.c b/src/lib/testing_api_cmd_auditor_exec_auditor.c new file mode 100644 index 000000000..cff234691 --- /dev/null +++ b/src/lib/testing_api_cmd_auditor_exec_auditor.c @@ -0,0 +1,163 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 +*/ + +/** + * @file auditor-lib/testing_auditor_api_cmd_exec_auditor.c + * @brief run the taler-auditor command + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "auditor_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "auditor" CMD. + */ +struct AuditorState +{ + + /** + * Process for the "auditor" command. + */ + struct GNUNET_OS_Process *auditor_proc; + + /** + * Configuration file used by the command. + */ + const char *config_filename; +}; + + +/** + * Run the command; calls the `taler-auditor' program. + * + * @param cls closure. + * @param cmd the commaind being run. + * @param is interpreter state. + */ +static void +auditor_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AuditorState *ks = cls; + + ks->auditor_proc + = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor", + "taler-auditor", + "-c", ks->config_filename, + NULL); + if (NULL == ks->auditor_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "auditor" CMD, and possibly kills its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +auditor_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AuditorState *ks = cls; + + if (NULL != ks->auditor_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ks->auditor_proc, + SIGKILL)); + GNUNET_OS_process_wait (ks->auditor_proc); + GNUNET_OS_process_destroy (ks->auditor_proc); + ks->auditor_proc = NULL; + } + GNUNET_free (ks); +} + + +/** + * Offer "auditor" CMD internal data to other commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +auditor_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct AuditorState *ks = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (0, &ks->auditor_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Make the "exec-auditor" CMD. + * + * @param label command label. + * @param config_filename configuration filename. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_auditor (const char *label, + const char *config_filename) +{ + struct TALER_TESTING_Command cmd; + struct AuditorState *ks; + + ks = GNUNET_new (struct AuditorState); + ks->config_filename = config_filename; + cmd.cls = ks; + cmd.label = label; + cmd.run = &auditor_run; + cmd.cleanup = &auditor_cleanup; + cmd.traits = &auditor_traits; + return cmd; +} + + +/* end of testing_auditor_api_cmd_exec_auditor.c */ diff --git a/src/lib/testing_api_cmd_auditor_exec_auditor_dbinit.c b/src/lib/testing_api_cmd_auditor_exec_auditor_dbinit.c new file mode 100644 index 000000000..3b7e3aa0b --- /dev/null +++ b/src/lib/testing_api_cmd_auditor_exec_auditor_dbinit.c @@ -0,0 +1,164 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 +*/ + +/** + * @file auditor-lib/testing_auditor_api_cmd_exec_auditor_dbinit.c + * @brief run the taler-auditor-dbinit "-r" command + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "auditor_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "auditor-dbinit" CMD. + */ +struct AuditorDbinitState +{ + + /** + * Process for the "auditor-dbinit" command. + */ + struct GNUNET_OS_Process *auditor_dbinit_proc; + + /** + * Configuration file used by the command. + */ + const char *config_filename; +}; + + +/** + * Run the command; calls the `taler-auditor-dbinit' program. + * + * @param cls closure. + * @param cmd the commaind being run. + * @param is interpreter state. + */ +static void +auditor_dbinit_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AuditorDbinitState *ks = cls; + + ks->auditor_dbinit_proc + = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor-dbinit", + "taler-auditor-dbinit", + "-c", ks->config_filename, + "-r", + NULL); + if (NULL == ks->auditor_dbinit_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "auditor-dbinit" CMD, and possibly kills its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +auditor_dbinit_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AuditorDbinitState *ks = cls; + + if (NULL != ks->auditor_dbinit_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ks->auditor_dbinit_proc, + SIGKILL)); + GNUNET_OS_process_wait (ks->auditor_dbinit_proc); + GNUNET_OS_process_destroy (ks->auditor_dbinit_proc); + ks->auditor_dbinit_proc = NULL; + } + GNUNET_free (ks); +} + + +/** + * Offer "auditor-dbinit" CMD internal data to other commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +auditor_dbinit_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct AuditorDbinitState *ks = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (0, &ks->auditor_dbinit_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Make the "exec-auditor-dbinit" CMD. + * + * @param label command label. + * @param config_filename configuration filename. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_auditor_dbinit (const char *label, + const char *config_filename) +{ + struct TALER_TESTING_Command cmd; + struct AuditorDbinitState *ks; + + ks = GNUNET_new (struct AuditorDbinitState); + ks->config_filename = config_filename; + cmd.cls = ks; + cmd.label = label; + cmd.run = &auditor_dbinit_run; + cmd.cleanup = &auditor_dbinit_cleanup; + cmd.traits = &auditor_dbinit_traits; + return cmd; +} + + +/* end of testing_auditor_api_cmd_exec_auditor_dbinit.c */ diff --git a/src/lib/testing_api_cmd_auditor_exec_wire_auditor.c b/src/lib/testing_api_cmd_auditor_exec_wire_auditor.c new file mode 100644 index 000000000..744420d3f --- /dev/null +++ b/src/lib/testing_api_cmd_auditor_exec_wire_auditor.c @@ -0,0 +1,163 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 +*/ + +/** + * @file auditor-lib/testing_auditor_api_cmd_exec_wire-auditor.c + * @brief run the taler-wire-auditor command + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "auditor_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "wire-auditor" CMD. + */ +struct WireAuditorState +{ + + /** + * Process for the "wire-auditor" command. + */ + struct GNUNET_OS_Process *wire_auditor_proc; + + /** + * Configuration file used by the command. + */ + const char *config_filename; +}; + + +/** + * Run the command; calls the `taler-wire-auditor' program. + * + * @param cls closure. + * @param cmd the commaind being run. + * @param is interpreter state. + */ +static void +wire_auditor_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct WireAuditorState *ks = cls; + + ks->wire_auditor_proc + = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-wire-auditor", + "taler-wire-auditor", + "-c", ks->config_filename, + NULL); + if (NULL == ks->wire_auditor_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "wire-auditor" CMD, and possibly kills its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +wire_auditor_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct WireAuditorState *ks = cls; + + if (NULL != ks->wire_auditor_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ks->wire_auditor_proc, + SIGKILL)); + GNUNET_OS_process_wait (ks->wire_auditor_proc); + GNUNET_OS_process_destroy (ks->wire_auditor_proc); + ks->wire_auditor_proc = NULL; + } + GNUNET_free (ks); +} + + +/** + * Offer "wire-auditor" CMD internal data to other commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +wire_auditor_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct WireAuditorState *ks = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (0, &ks->wire_auditor_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Make the "exec wire-auditor" CMD. + * + * @param label command label. + * @param config_filename configuration filename. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_wire_auditor (const char *label, + const char *config_filename) +{ + struct TALER_TESTING_Command cmd; + struct WireAuditorState *ks; + + ks = GNUNET_new (struct WireAuditorState); + ks->config_filename = config_filename; + cmd.cls = ks; + cmd.label = label; + cmd.run = &wire_auditor_run; + cmd.cleanup = &wire_auditor_cleanup; + cmd.traits = &wire_auditor_traits; + return cmd; +} + + +/* end of testing_auditor_api_cmd_exec_wire_auditor.c */ diff --git a/src/lib/testing_api_cmd_bank_admin_add_incoming.c b/src/lib/testing_api_cmd_bank_admin_add_incoming.c new file mode 100644 index 000000000..770b2e384 --- /dev/null +++ b/src/lib/testing_api_cmd_bank_admin_add_incoming.c @@ -0,0 +1,609 @@ +/* + This file is part of TALER + Copyright (C) 2018-2020 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 + +*/ +/** + * @file exchange-lib/testing_api_cmd_admin_add_incoming.c + * @brief implementation of a bank /admin/add-incoming command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "backoff.h" +#include "taler_json_lib.h" +#include +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" + + +/** + * State for a "fakebank transfer" CMD. + */ +struct AdminAddIncomingState +{ + + /** + * Label of any command that can trait-offer a reserve priv. + */ + const char *reserve_reference; + + /** + * Wire transfer amount. + */ + struct TALER_Amount amount; + + /** + * Base URL of the credited account. + */ + const char *exchange_credit_url; + + /** + * Money sender account URL. + */ + const char *payto_debit_account; + + /** + * Username to use for authentication. + */ + struct TALER_BANK_AuthenticationData auth; + + /** + * Set (by the interpreter) to the reserve's private key + * we used to make a wire transfer subject line with. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Reserve public key matching @e reserve_priv. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Handle to the pending request at the fakebank. + */ + struct TALER_BANK_AdminAddIncomingHandle *aih; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Set to the wire transfer's unique ID. + */ + uint64_t serial_id; + + /** + * Timestamp of the transaction (as returned from the bank). + */ + struct GNUNET_TIME_Absolute timestamp; + + /** + * Merchant instance. Sometimes used to get the tip reserve + * private key by reading the appropriate config section. + */ + const char *instance; + + /** + * Configuration filename. Used to get the tip reserve key + * filename (used to obtain a public key to write in the + * transfer subject). + */ + const char *config_filename; + + /** + * Task scheduled to try later. + */ + struct GNUNET_SCHEDULER_Task *retry_task; + + /** + * How long do we wait until we retry? + */ + struct GNUNET_TIME_Relative backoff; + + /** + * Was this command modified via + * #TALER_TESTING_cmd_admin_add_incoming_with_retry to + * enable retries? + */ + int do_retry; +}; + + +/** + * Run the "fakebank transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +admin_add_incoming_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is); + + +/** + * Task scheduled to re-try #admin_add_incoming_run. + * + * @param cls a `struct AdminAddIncomingState` + */ +static void +do_retry (void *cls) +{ + struct AdminAddIncomingState *fts = cls; + + fts->retry_task = NULL; + admin_add_incoming_run (fts, + NULL, + fts->is); +} + + +/** + * This callback will process the fakebank response to the wire + * transfer. It just checks whether the HTTP response code is + * acceptable. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for + * successful status request; 0 if the exchange's reply is + * bogus (fails to follow the protocol) + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param serial_id unique ID of the wire transfer + * @param timestamp time stamp of the transaction made. + * @param json raw response + */ +static void +confirmation_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t serial_id, + struct GNUNET_TIME_Absolute timestamp, + const json_t *json) +{ + struct AdminAddIncomingState *fts = cls; + struct TALER_TESTING_Interpreter *is = fts->is; + + fts->aih = NULL; + if (MHD_HTTP_OK != http_status) + { + if (GNUNET_YES == fts->do_retry) + { + if ( (0 == http_status) || + (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || + (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) + { + GNUNET_log + (GNUNET_ERROR_TYPE_INFO, + "Retrying fakebank transfer failed with %u/%d\n", + http_status, + (int) ec); + /* on DB conflicts, do not use backoff */ + if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) + fts->backoff = GNUNET_TIME_UNIT_ZERO; + else + fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); + fts->retry_task = GNUNET_SCHEDULER_add_delayed + (fts->backoff, + &do_retry, + fts); + return; + } + } + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fakebank returned HTTP status %u/%d\n", + http_status, + (int) ec); + TALER_TESTING_interpreter_fail (is); + return; + } + + fts->serial_id = serial_id; + fts->timestamp = timestamp; + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the "fakebank transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +admin_add_incoming_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AdminAddIncomingState *fts = cls; + + /* Use reserve public key as subject */ + if (NULL != fts->reserve_reference) + { + const struct TALER_TESTING_Command *ref; + const struct TALER_ReservePrivateKeyP *reserve_priv; + + ref = TALER_TESTING_interpreter_lookup_command + (is, fts->reserve_reference); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (ref, + 0, + &reserve_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv; + } + else + { + if (NULL != fts->instance) + { + char *section; + char *keys; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + struct GNUNET_CONFIGURATION_Handle *cfg; + + GNUNET_assert (NULL != fts->config_filename); + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (cfg, + fts->config_filename)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + + GNUNET_asprintf (§ion, + "instance-%s", + fts->instance); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename + (cfg, + section, + "TIP_RESERVE_PRIV_FILENAME", + &keys)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Configuration fails to specify reserve" + " private key filename in section %s\n", + section); + GNUNET_free (section); + TALER_TESTING_interpreter_fail (is); + return; + } + priv = GNUNET_CRYPTO_eddsa_key_create_from_file (keys); + GNUNET_free (keys); + if (NULL == priv) + { + GNUNET_log_config_invalid + (GNUNET_ERROR_TYPE_ERROR, + section, + "TIP_RESERVE_PRIV_FILENAME", + "Failed to read private key"); + GNUNET_free (section); + TALER_TESTING_interpreter_fail (is); + return; + } + fts->reserve_priv.eddsa_priv = *priv; + GNUNET_free (section); + GNUNET_free (priv); + GNUNET_CONFIGURATION_destroy (cfg); + } + else + { + /* No referenced reserve, no instance to take priv + * from, no explicit subject given: create new key! */ + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + + priv = GNUNET_CRYPTO_eddsa_key_create (); + fts->reserve_priv.eddsa_priv = *priv; + GNUNET_free (priv); + } + } + GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv, + &fts->reserve_pub.eddsa_pub); + fts->is = is; + fts->aih + = TALER_BANK_admin_add_incoming + (TALER_TESTING_interpreter_get_context (is), + fts->exchange_credit_url, + &fts->auth, + &fts->reserve_pub, + &fts->amount, + fts->payto_debit_account, + &confirmation_cb, + fts); + if (NULL == fts->aih) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "/admin/add-incoming" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure + * @param cmd current CMD being cleaned up. + */ +static void +admin_add_incoming_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AdminAddIncomingState *fts = cls; + + if (NULL != fts->aih) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %s did not complete\n", + cmd->label); + TALER_BANK_admin_add_incoming_cancel (fts->aih); + fts->aih = NULL; + } + if (NULL != fts->retry_task) + { + GNUNET_SCHEDULER_cancel (fts->retry_task); + fts->retry_task = NULL; + } + GNUNET_free (fts); +} + + +/** + * Offer internal data from a "/admin/add-incoming" CMD to other + * commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +admin_add_incoming_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct AdminAddIncomingState *fts = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_url (1, fts->payto_debit_account), + TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), + TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->exchange_credit_url), + TALER_TESTING_make_trait_amount_obj (0, &fts->amount), + TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), + TALER_TESTING_make_trait_reserve_priv (0, + &fts->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (0, + &fts->reserve_pub), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Create admin/add-incoming command. + * + * @param label command label. + * @param amount amount to transfer. + * @param exchange_base_url base URL of the account that receives this + * wire transer (which account receives money). + * @param payto_debit_account which account sends money. + * @param auth authentication data + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming + (const char *label, + const char *amount, + const char *exchange_base_url, + const struct TALER_BANK_AuthenticationData *auth, + const char *payto_debit_account) +{ + struct AdminAddIncomingState *fts; + + fts = GNUNET_new (struct AdminAddIncomingState); + fts->exchange_credit_url = exchange_base_url; + fts->payto_debit_account = payto_debit_account; + fts->auth = *auth; + if (GNUNET_OK != + TALER_string_to_amount (amount, + &fts->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + amount, + label); + GNUNET_assert (0); + } + + { + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &admin_add_incoming_run, + .cleanup = &admin_add_incoming_cleanup, + .traits = &admin_add_incoming_traits + }; + + return cmd; + } +} + + +/** + * Create "/admin/add-incoming" CMD, letting the caller specify + * a reference to a command that can offer a reserve private key. + * This private key will then be used to construct the subject line + * of the wire transfer. + * + * @param label command label. + * @param amount the amount to transfer. + * @param account_bank_url base URL of the exchange account receiving the money + * @param payto_debit_account which account sends money + * @param auth authentication data + * @param ref reference to a command that can offer a reserve + * private key. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming_with_ref + (const char *label, + const char *amount, + const char *account_base_url, + const struct TALER_BANK_AuthenticationData *auth, + const char *payto_debit_account, + const char *ref) +{ + struct AdminAddIncomingState *fts; + + fts = GNUNET_new (struct AdminAddIncomingState); + fts->exchange_credit_url = account_base_url; + fts->payto_debit_account = payto_debit_account; + fts->auth = *auth; + fts->reserve_reference = ref; + if (GNUNET_OK != + TALER_string_to_amount (amount, + &fts->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + amount, + label); + GNUNET_assert (0); + } + { + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &admin_add_incoming_run, + .cleanup = &admin_add_incoming_cleanup, + .traits = &admin_add_incoming_traits + }; + + return cmd; + } +} + + +/** + * Create "/admin/add-incoming" CMD, letting the caller specifying + * the merchant instance. This version is useful when a tip + * reserve should be topped up, in fact the interpreter will need + * the "tipping instance" in order to get the instance public key + * and make a wire transfer subject out of it. + * + * @param label command label. + * @param amount amount to transfer. + * @param account_bank_url base URL of the exchange bank account + * that receives the wire transfer + * @param payto_debit_account which account (expressed as a number) + * gives money + * @param auth authentication data + * @param instance the instance that runs the tipping. Under this + * instance, the configuration file will provide the private + * key of the tipping reserve. This data will then used to + * construct the wire transfer subject line. + * @param config_filename configuration file to use. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming_with_instance + (const char *label, + const char *amount, + const char *account_base_url, + const struct TALER_BANK_AuthenticationData *auth, + const char *payto_debit_account, + const char *instance, + const char *config_filename) +{ + struct AdminAddIncomingState *fts; + + fts = GNUNET_new (struct AdminAddIncomingState); + fts->exchange_credit_url = account_base_url; + fts->payto_debit_account = payto_debit_account; + fts->auth = *auth; + fts->instance = instance; + fts->config_filename = config_filename; + if (GNUNET_OK != + TALER_string_to_amount (amount, + &fts->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + amount, + label); + GNUNET_assert (0); + } + { + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &admin_add_incoming_run, + .cleanup = &admin_add_incoming_cleanup, + .traits = &admin_add_incoming_traits + }; + + return cmd; + } +} + + +/** + * Modify a fakebank transfer command to enable retries when the + * reserve is not yet full or we get other transient errors from the + * fakebank. + * + * @param cmd a fakebank transfer command + * @return the command with retries enabled + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming_retry (struct TALER_TESTING_Command cmd) +{ + struct AdminAddIncomingState *fts; + + GNUNET_assert (&admin_add_incoming_run == cmd.run); + fts = cmd.cls; + fts->do_retry = GNUNET_YES; + return cmd; +} + + +/* end of testing_api_cmd_admin_add_incoming.c */ diff --git a/src/lib/testing_api_cmd_bank_history_credit.c b/src/lib/testing_api_cmd_bank_history_credit.c new file mode 100644 index 000000000..fefb2dda7 --- /dev/null +++ b/src/lib/testing_api_cmd_bank_history_credit.c @@ -0,0 +1,765 @@ +/* + This file is part of TALER + Copyright (C) 2018-2020 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 + +*/ +/** + * @file bank-lib/testing_api_cmd_history.c + * @brief command to check the /history API from the bank. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" +#include "taler_fakebank_lib.h" +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" + + +/** + * State for a "history" CMD. + */ +struct HistoryState +{ + /** + * Base URL of the account offering the "history" operation. + */ + char *account_url; + + /** + * Reference to command defining the + * first row number we want in the result. + */ + const char *start_row_reference; + + /** + * How many rows we want in the result, _at most_, + * and ascending/descending. + */ + long long num_results; + + /** + * Handle to a pending "history" operation. + */ + struct TALER_BANK_CreditHistoryHandle *hh; + + /** + * Authentication data for the operation. + */ + struct TALER_BANK_AuthenticationData auth; + + /** + * Expected number of results (= rows). + */ + uint64_t results_obtained; + + /** + * Set to GNUNET_YES if the callback detects something + * unexpected. + */ + int failed; + +}; + + +/** + * Item in the transaction history, as reconstructed from the + * command history. + */ +struct History +{ + + /** + * Wire details. + */ + struct TALER_BANK_CreditDetails details; + + /** + * Serial ID of the wire transfer. + */ + uint64_t row_id; + + /** + * URL to free. + */ + char *url; +}; + + +/** + * Offer internal data to other commands. + * + * @param cls closure. + * @param ret[out] set to the wanted data. + * @param trait name of the trait. + * @param index index number of the traits to be returned. + * + * @return #GNUNET_OK on success + */ +static int +history_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + (void) cls; + (void) ret; + (void) trait; + (void) index; + /* Must define this function because some callbacks + * look for certain traits on _all_ the commands. */ + return GNUNET_SYSERR; +} + + +/** + * Free history @a h of length @a h_len. + * + * @param h history array to free. + * @param h_len number of entries in @a h. + */ +static void +free_history (struct History *h, + uint64_t h_len) +{ + for (uint64_t off = 0; off= hs->num_results; +} + + +/** + * This function constructs the list of history elements that + * interest the account number of the caller. It has two main + * loops: the first to figure out how many history elements have + * to be allocated, and the second to actually populate every + * element. + * + * @param is interpreter state (supposedly having the + * current CMD pointing at a "history" CMD). + * @param[out] rh history array to initialize. + * + * @return number of entries in @a rh. + */ +static uint64_t +build_history (struct TALER_TESTING_Interpreter *is, + struct History **rh) +{ + struct HistoryState *hs = is->commands[is->ip].cls; + uint64_t total; + struct History *h; + const struct TALER_TESTING_Command *add_incoming_cmd; + int inc; + unsigned int start; + unsigned int end; + + /** + * @var turns GNUNET_YES whenever either no 'start' value was + * given for the history query, or the given value is found + * in the list of all the CMDs. + */int ok; + const uint64_t *row_id_start = NULL; + + if (NULL != hs->start_row_reference) + { + TALER_LOG_INFO + ("`%s': start row given via reference `%s'\n", + TALER_TESTING_interpreter_get_current_label (is), + hs->start_row_reference); + add_incoming_cmd = TALER_TESTING_interpreter_lookup_command + (is, hs->start_row_reference); + GNUNET_assert (NULL != add_incoming_cmd); + GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64 + (add_incoming_cmd, 0, &row_id_start)); + } + + GNUNET_assert (0 != hs->num_results); + if (0 == is->ip) + { + TALER_LOG_DEBUG ("Checking history at first CMD..\n"); + *rh = NULL; + return 0; + } + + /* AKA 'delta'. */ + if (hs->num_results > 0) + { + inc = 1; /* _inc_rement */ + start = 0; + end = is->ip - 1; + } + else + { + inc = -1; + start = is->ip - 1; + end = 0; + } + + total = 0; + ok = GNUNET_NO; + + if (NULL == row_id_start) + ok = GNUNET_YES; + + /* This loop counts how many commands _later than "start"_ belong + * to the history of the caller. This is stored in the @var total + * variable. */ + for (unsigned int off = start; off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *pos = &is->commands[off]; + const uint64_t *row_id; + const char *credit_account; + const char *debit_account; + + /** + * The following command allows us to skip over those CMDs + * that do not offer a "row_id" trait. Such skipped CMDs are + * not interesting for building a history. + */if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (pos, + 0, + &row_id)) + continue; + + /* Seek "/history" starting row. */ + if (NULL != row_id_start) + { + if (*row_id_start == *row_id) + { + /* Doesn't count, start is excluded from output. */ + total = 0; + ok = GNUNET_YES; + continue; + } + } + + /* when 'start' was _not_ given, then ok == GNUNET_YES */ + if (GNUNET_NO == ok) + continue; /* skip until we find the marker */ + + TALER_LOG_DEBUG ("Found first row\n"); + + if (build_history_hit_limit (total, + hs, + pos)) + { + TALER_LOG_DEBUG ("Hit history limit\n"); + break; + } + + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (pos, &credit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_account)); + + TALER_LOG_INFO ("Potential history element:" + " %s->%s; my account: %s\n", + debit_account, + credit_account, + hs->account_url); + + if (0 == strcasecmp (hs->account_url, + credit_account)) + { + TALER_LOG_INFO ("+1 my history\n"); + total++; /* found matching record */ + } + } + + GNUNET_assert (GNUNET_YES == ok); + + if (0 == total) + { + TALER_LOG_DEBUG ("Checking history at first CMD.. (2)\n"); + *rh = NULL; + return 0; + } + + + GNUNET_assert (total < UINT_MAX); + h = GNUNET_new_array ((unsigned int) total, + struct History); + total = 0; + ok = GNUNET_NO; + if (NULL == row_id_start) + ok = GNUNET_YES; + + /** + * This loop _only_ populates the array of history elements. + */ + for (unsigned int off = start; off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *pos = &is->commands[off]; + const uint64_t *row_id; + char *bank_hostname; + const char *credit_account; + const char *debit_account; + + if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID + (pos, &row_id)) + continue; + + if (NULL != row_id_start) + { + + if (*row_id_start == *row_id) + { + /** + * Warning: this zeroing is superfluous, as + * total doesn't get incremented if 'start' + * was given and couldn't be found. + */total = 0; + ok = GNUNET_YES; + continue; + } + } + + TALER_LOG_INFO ("Found first row (2)\n"); + + if (GNUNET_NO == ok) + { + TALER_LOG_INFO ("Skip on `%s'\n", + pos->label); + continue; /* skip until we find the marker */ + } + + if (build_history_hit_limit (total, + hs, + pos)) + { + TALER_LOG_INFO ("Hit history limit (2)\n"); + break; + } + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (pos, &credit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_account)); + + TALER_LOG_INFO ("Potential history bit:" + " %s->%s; my account: %s\n", + debit_account, + credit_account, + hs->account_url); + + /** + * Discard transactions where the audited account played + * _both_ the credit and the debit roles, but _only if_ + * the audit goes on both directions.. This needs more + * explaination! + */if (0 == strcasecmp (hs->account_url, + credit_account)) + { + GNUNET_break (0); + continue; + } + + bank_hostname = strchr (hs->account_url, ':'); + GNUNET_assert (NULL != bank_hostname); + bank_hostname += 3; + + /* Next two blocks only put the 'direction' and 'banking' + * information. */ + + /* Asked for credit, and account got the credit. */ + if (0 == strcasecmp (hs->account_url, + credit_account)) + { + h[total].url = GNUNET_strdup (debit_account); + h[total].details.account_url = h[total].url; + } + + /* This block _completes_ the information of the current item, + * with amount / subject / exchange URL. */ + if (0 == strcasecmp (hs->account_url, + credit_account)) + { + const struct TALER_Amount *amount; + const struct TALER_ReservePublicKeyP *reserve_pub; + const char *account_url; + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_amount_obj + (pos, 0, &amount)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_reserve_pub + (pos, 0, &reserve_pub)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_url + (pos, 1, + &account_url)); + h[total].details.amount = *amount; + h[total].row_id = *row_id; + h[total].details.reserve_pub = *reserve_pub; + h[total].details.account_url = account_url; + TALER_LOG_INFO ("+1-bit of my history\n"); + total++; + } + } + *rh = h; + return total; +} + + +/** + * Compute how many results we expect to be returned for + * the current command at @a is. + * + * @param is the interpreter state to inspect. + * @return number of results expected. + */ +static uint64_t +compute_result_count (struct TALER_TESTING_Interpreter *is) +{ + uint64_t total; + struct History *h; + + total = build_history (is, &h); + free_history (h, total); + return total; +} + + +/** + * Check that the "/history" response matches the + * CMD whose offset in the list of CMDs is @a off. + * + * @param is the interpreter state. + * @param off the offset (of the CMD list) where the command + * to check is. + * @param dir the expected direction of the transaction. + * @param details the expected transaction details. + * + * @return #GNUNET_OK if the transaction is what we expect. + */ +static int +check_result (struct TALER_TESTING_Interpreter *is, + unsigned int off, + const struct TALER_BANK_CreditDetails *details) +{ + uint64_t total; + struct History *h; + + total = build_history (is, &h); + if (off >= total) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Test says history has at most %u" + " results, but got result #%u to check\n", + (unsigned int) total, + off); + print_expected (h, + total, + off); + return GNUNET_SYSERR; + } + if ( (0 != GNUNET_memcmp (&h[off].details.reserve_pub, + &details->reserve_pub)) || + (0 != TALER_amount_cmp (&h[off].details.amount, + &details->amount)) || + (0 != strcasecmp (h[off].details.account_url, + details->account_url)) ) + { + GNUNET_break (0); + print_expected (h, + total, + off); + free_history (h, + total); + return GNUNET_SYSERR; + } + free_history (h, + total); + return GNUNET_OK; +} + + +/** + * This callback will (1) check that the HTTP response code + * is acceptable and (2) that the history is consistent. The + * consistency is checked by going through all the past CMDs, + * reconstructing then the expected history as of those, and + * finally check it against what the bank returned. + * + * @param cls closure. + * @param http_status HTTP response code, #MHD_HTTP_OK (200) + * for successful status request 0 if the bank's reply is + * bogus (fails to follow the protocol), + * #MHD_HTTP_NO_CONTENT if there are no more results; on + * success the last callback is always of this status + * (even if `abs(num_results)` were already returned). + * @param ec taler status code. + * @param dir direction of the transfer. + * @param row_id monotonically increasing counter corresponding to + * the transaction. + * @param details details about the wire transfer. + * @param json detailed response from the HTTPD, or NULL if + * reply was not in JSON. + * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration + */ +static int +history_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t row_id, + const struct TALER_BANK_CreditDetails *details, + const json_t *json) +{ + struct TALER_TESTING_Interpreter *is = cls; + struct HistoryState *hs = is->commands[is->ip].cls; + + (void) row_id; + if (MHD_HTTP_OK != http_status) + { + hs->hh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unwanted response code from /history: %u\n", + http_status); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + if (NULL == details) + { + hs->hh = NULL; + if ( (hs->results_obtained != compute_result_count (is)) || + (GNUNET_YES == hs->failed) ) + { + uint64_t total; + struct History *h; + + GNUNET_break (0); + total = build_history (is, &h); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected history of length %llu, got %llu;" + " HTTP status code: %u/%d, failed: %d\n", + (unsigned long long) total, + (unsigned long long) hs->results_obtained, + http_status, + (int) ec, + hs->failed); + print_expected (h, + total, + UINT_MAX); + free_history (h, + total); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + TALER_TESTING_interpreter_next (is); + return GNUNET_OK; + } + + /* check current element */ + if (GNUNET_OK != check_result (is, + hs->results_obtained, + details)) + { + char *acc; + + GNUNET_break (0); + acc = json_dumps (json, + JSON_COMPACT); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Result %u was `%s'\n", + (unsigned int) hs->results_obtained++, + acc); + if (NULL != acc) + free (acc); + hs->failed = GNUNET_YES; + return GNUNET_SYSERR; + } + hs->results_obtained++; + return GNUNET_OK; +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +history_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct HistoryState *hs = cls; + uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX; + const uint64_t *row_ptr; + + (void) cmd; + /* Get row_id from trait. */ + if (NULL != hs->start_row_reference) + { + const struct TALER_TESTING_Command *history_cmd; + + history_cmd = TALER_TESTING_interpreter_lookup_command + (is, hs->start_row_reference); + + if (NULL == history_cmd) + TALER_TESTING_FAIL (is); + + if (GNUNET_OK != + TALER_TESTING_get_trait_uint64 (history_cmd, + 0, + &row_ptr)) + TALER_TESTING_FAIL (is); + else + row_id = *row_ptr; + TALER_LOG_DEBUG ("row id (from trait) is %llu\n", + (unsigned long long) row_id); + } + + hs->hh = TALER_BANK_credit_history (is->ctx, + hs->account_url, + &hs->auth, + row_id, + hs->num_results, + &history_cb, + is); + GNUNET_assert (NULL != hs->hh); +} + + +/** + * Free the state from a "history" CMD, and possibly cancel + * a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +history_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct HistoryState *hs = cls; + + (void) cmd; + if (NULL != hs->hh) + { + TALER_LOG_WARNING ("/history did not complete\n"); + TALER_BANK_credit_history_cancel (hs->hh); + } + GNUNET_free (hs->account_url); + GNUNET_free (hs); +} + + +/** + * Make a "history" CMD. + * + * @param label command label. + * @param account_url base URL of the account offering the "history" + * operation. + * @param start_row_reference reference to a command that can + * offer a row identifier, to be used as the starting row + * to accept in the result. + * @param num_results how many rows we want in the result. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_bank_credits (const char *label, + const char *account_url, + const struct + TALER_BANK_AuthenticationData *auth, + const char *start_row_reference, + long long num_results) +{ + struct HistoryState *hs; + + hs = GNUNET_new (struct HistoryState); + hs->account_url = GNUNET_strdup (account_url); + hs->start_row_reference = start_row_reference; + hs->num_results = num_results; + hs->auth = *auth; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .cls = hs, + .run = &history_run, + .cleanup = &history_cleanup, + .traits = &history_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_credit_history.c */ diff --git a/src/lib/testing_api_cmd_bank_history_debit.c b/src/lib/testing_api_cmd_bank_history_debit.c new file mode 100644 index 000000000..96f989c04 --- /dev/null +++ b/src/lib/testing_api_cmd_bank_history_debit.c @@ -0,0 +1,766 @@ +/* + This file is part of TALER + Copyright (C) 2018-2020 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 + +*/ + +/** + * @file bank-lib/testing_api_cmd_history.c + * @brief command to check the /history API from the bank. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" +#include "taler_fakebank_lib.h" +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" + + +/** + * State for a "history" CMD. + */ +struct HistoryState +{ + /** + * Base URL of the account offering the "history" operation. + */ + const char *account_url; + + /** + * Reference to command defining the + * first row number we want in the result. + */ + const char *start_row_reference; + + /** + * How many rows we want in the result, _at most_, + * and ascending/descending. + */ + long long num_results; + + /** + * Login data to use to authenticate. + */ + struct TALER_BANK_AuthenticationData auth; + + /** + * Handle to a pending "history" operation. + */ + struct TALER_BANK_DebitHistoryHandle *hh; + + /** + * Expected number of results (= rows). + */ + uint64_t results_obtained; + + /** + * Set to #GNUNET_YES if the callback detects something + * unexpected. + */ + int failed; + +}; + + +/** + * Item in the transaction history, as reconstructed from the + * command history. + */ +struct History +{ + + /** + * Wire details. + */ + struct TALER_BANK_DebitDetails details; + + /** + * Serial ID of the wire transfer. + */ + uint64_t row_id; + + /** + * URL to free. + */ + char *url; +}; + + +/** + * Offer internal data to other commands. + * + * @param cls closure. + * @param ret[out] set to the wanted data. + * @param trait name of the trait. + * @param index index number of the traits to be returned. + * + * @return #GNUNET_OK on success + */ +static int +history_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + (void) cls; + (void) ret; + (void) trait; + (void) index; + /* Must define this function because some callbacks + * look for certain traits on _all_ the commands. */ + return GNUNET_SYSERR; +} + + +/** + * Free history @a h of length @a h_len. + * + * @param h history array to free. + * @param h_len number of entries in @a h. + */ +static void +free_history (struct History *h, + uint64_t h_len) +{ + for (uint64_t off = 0; off= hs->num_results; +} + + +/** + * This function constructs the list of history elements that + * interest the account number of the caller. It has two main + * loops: the first to figure out how many history elements have + * to be allocated, and the second to actually populate every + * element. + * + * @param is interpreter state (supposedly having the + * current CMD pointing at a "history" CMD). + * @param[out] rh history array to initialize. + * + * @return number of entries in @a rh. + */ +static uint64_t +build_history (struct TALER_TESTING_Interpreter *is, + struct History **rh) +{ + struct HistoryState *hs = is->commands[is->ip].cls; + uint64_t total; + struct History *h; + const struct TALER_TESTING_Command *add_incoming_cmd; + int inc; + unsigned int start; + unsigned int end; + + /** + * @var turns GNUNET_YES whenever either no 'start' value was + * given for the history query, or the given value is found + * in the list of all the CMDs. + */int ok; + const uint64_t *row_id_start = NULL; + + if (NULL != hs->start_row_reference) + { + TALER_LOG_INFO + ("`%s': start row given via reference `%s'\n", + TALER_TESTING_interpreter_get_current_label (is), + hs->start_row_reference); + add_incoming_cmd = TALER_TESTING_interpreter_lookup_command + (is, hs->start_row_reference); + GNUNET_assert (NULL != add_incoming_cmd); + GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64 + (add_incoming_cmd, 0, &row_id_start)); + } + + GNUNET_assert (0 != hs->num_results); + if (0 == is->ip) + { + TALER_LOG_DEBUG ("Checking history at first CMD..\n"); + *rh = NULL; + return 0; + } + + /* AKA 'delta'. */ + if (hs->num_results > 0) + { + inc = 1; /* _inc_rement */ + start = 0; + end = is->ip - 1; + } + else + { + inc = -1; + start = is->ip - 1; + end = 0; + } + + total = 0; + ok = GNUNET_NO; + + if (NULL == row_id_start) + ok = GNUNET_YES; + + /* This loop counts how many commands _later than "start"_ belong + * to the history of the caller. This is stored in the @var total + * variable. */ + for (unsigned int off = start; off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *pos = &is->commands[off]; + const uint64_t *row_id; + const char *debit_account; + const char *credit_account; + + /** + * The following command allows us to skip over those CMDs + * that do not offer a "row_id" trait. Such skipped CMDs are + * not interesting for building a history. + */if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (pos, + 0, + &row_id)) + continue; + + /* Seek "/history" starting row. */ + if (NULL != row_id_start) + { + if (*row_id_start == *row_id) + { + /* Doesn't count, start is excluded from output. */ + total = 0; + ok = GNUNET_YES; + continue; + } + } + + /* when 'start' was _not_ given, then ok == GNUNET_YES */ + if (GNUNET_NO == ok) + continue; /* skip until we find the marker */ + + TALER_LOG_DEBUG ("Found first row\n"); + + if (build_history_hit_limit (total, + hs, + pos)) + { + TALER_LOG_DEBUG ("Hit history limit\n"); + break; + } + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (pos, &credit_account)); + + TALER_LOG_INFO ("Potential history element:" + " %s->%s; my account: %s\n", + debit_account, + credit_account, + hs->account_url); + + if (0 == strcasecmp (hs->account_url, + debit_account)) + { + TALER_LOG_INFO ("+1 my history\n"); + total++; /* found matching record */ + } + } + + GNUNET_assert (GNUNET_YES == ok); + + if (0 == total) + { + TALER_LOG_DEBUG ("Checking history at first CMD.. (2)\n"); + *rh = NULL; + return 0; + } + + + GNUNET_assert (total < UINT_MAX); + h = GNUNET_new_array ((unsigned int) total, + struct History); + total = 0; + ok = GNUNET_NO; + if (NULL == row_id_start) + ok = GNUNET_YES; + + /** + * This loop _only_ populates the array of history elements. + */ + for (unsigned int off = start; off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *pos = &is->commands[off]; + const uint64_t *row_id; + char *bank_hostname; + const char *credit_account; + const char *debit_account; + + if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID + (pos, &row_id)) + continue; + + if (NULL != row_id_start) + { + + if (*row_id_start == *row_id) + { + /** + * Warning: this zeroing is superfluous, as + * total doesn't get incremented if 'start' + * was given and couldn't be found. + */total = 0; + ok = GNUNET_YES; + continue; + } + } + + TALER_LOG_INFO ("Found first row (2)\n"); + + if (GNUNET_NO == ok) + { + TALER_LOG_INFO ("Skip on `%s'\n", + pos->label); + continue; /* skip until we find the marker */ + } + + if (build_history_hit_limit (total, + hs, + pos)) + { + TALER_LOG_INFO ("Hit history limit (2)\n"); + break; + } + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (pos, &credit_account)); + + TALER_LOG_INFO ("Potential history bit:" + " %s->%s; my account: %s\n", + debit_account, + credit_account, + hs->account_url); + + /** + * Discard transactions where the audited account played + * _both_ the debit and the debit roles, but _only if_ + * the audit goes on both directions.. This needs more + * explaination! + */if (0 == strcasecmp (hs->account_url, + debit_account)) + { + GNUNET_break (0); + continue; + } + + bank_hostname = strchr (hs->account_url, ':'); + GNUNET_assert (NULL != bank_hostname); + bank_hostname += 3; + + /* Next two blocks only put the 'direction' and 'banking' + * information. */ + + /* Asked for debit, and account got the debit. */ + if (0 == strcasecmp (hs->account_url, + debit_account)) + { + h[total].url = GNUNET_strdup (credit_account); + h[total].details.account_url = h[total].url; + } + + /* This block _completes_ the information of the current item, + * with amount / subject / exchange URL. */ + if (0 == strcasecmp (hs->account_url, + debit_account)) + { + const struct TALER_Amount *amount; + const struct TALER_WireTransferIdentifierRawP *wtid; + const char *account_url; + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_amount_obj + (pos, 0, &amount)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_wtid + (pos, 0, &wtid)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_url + (pos, 1, + &account_url)); + h[total].details.amount = *amount; + h[total].row_id = *row_id; + h[total].details.wtid = *wtid; + h[total].details.account_url = account_url; + TALER_LOG_INFO ("+1-bit of my history\n"); + total++; + } + } + *rh = h; + return total; +} + + +/** + * Compute how many results we expect to be returned for + * the current command at @a is. + * + * @param is the interpreter state to inspect. + * @return number of results expected. + */ +static uint64_t +compute_result_count (struct TALER_TESTING_Interpreter *is) +{ + uint64_t total; + struct History *h; + + total = build_history (is, &h); + free_history (h, total); + return total; +} + + +/** + * Check that the "/history" response matches the + * CMD whose offset in the list of CMDs is @a off. + * + * @param is the interpreter state. + * @param off the offset (of the CMD list) where the command + * to check is. + * @param dir the expected direction of the transaction. + * @param details the expected transaction details. + * + * @return #GNUNET_OK if the transaction is what we expect. + */ +static int +check_result (struct TALER_TESTING_Interpreter *is, + unsigned int off, + const struct TALER_BANK_DebitDetails *details) +{ + uint64_t total; + struct History *h; + + total = build_history (is, &h); + if (off >= total) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Test says history has at most %u" + " results, but got result #%u to check\n", + (unsigned int) total, + off); + print_expected (h, + total, + off); + return GNUNET_SYSERR; + } + if ( (0 != GNUNET_memcmp (&h[off].details.wtid, + &details->wtid)) || + (0 != TALER_amount_cmp (&h[off].details.amount, + &details->amount)) || + (0 != strcasecmp (h[off].details.account_url, + details->account_url)) ) + { + GNUNET_break (0); + print_expected (h, + total, + off); + free_history (h, + total); + return GNUNET_SYSERR; + } + free_history (h, + total); + return GNUNET_OK; +} + + +/** + * This callback will (1) check that the HTTP response code + * is acceptable and (2) that the history is consistent. The + * consistency is checked by going through all the past CMDs, + * reconstructing then the expected history as of those, and + * finally check it against what the bank returned. + * + * @param cls closure. + * @param http_status HTTP response code, #MHD_HTTP_OK (200) + * for successful status request 0 if the bank's reply is + * bogus (fails to follow the protocol), + * #MHD_HTTP_NO_CONTENT if there are no more results; on + * success the last callback is always of this status + * (even if `abs(num_results)` were already returned). + * @param ec taler status code. + * @param dir direction of the transfer. + * @param row_id monotonically increasing counter corresponding to + * the transaction. + * @param details details about the wire transfer. + * @param json detailed response from the HTTPD, or NULL if + * reply was not in JSON. + * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration + */ +static int +history_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t row_id, + const struct TALER_BANK_DebitDetails *details, + const json_t *json) +{ + struct TALER_TESTING_Interpreter *is = cls; + struct HistoryState *hs = is->commands[is->ip].cls; + + (void) row_id; + if (MHD_HTTP_OK != http_status) + { + hs->hh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unwanted response code from /history: %u\n", + http_status); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + if (NULL == details) + { + hs->hh = NULL; + if ( (hs->results_obtained != compute_result_count (is)) || + (GNUNET_YES == hs->failed) ) + { + uint64_t total; + struct History *h; + + GNUNET_break (0); + total = build_history (is, &h); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected history of length %llu, got %llu;" + " HTTP status code: %u/%d, failed: %d\n", + (unsigned long long) total, + (unsigned long long) hs->results_obtained, + http_status, + (int) ec, + hs->failed); + print_expected (h, + total, + UINT_MAX); + free_history (h, + total); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + TALER_TESTING_interpreter_next (is); + return GNUNET_OK; + } + + /* check current element */ + if (GNUNET_OK != check_result (is, + hs->results_obtained, + details)) + { + char *acc; + + GNUNET_break (0); + acc = json_dumps (json, + JSON_COMPACT); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Result %u was `%s'\n", + (unsigned int) hs->results_obtained++, + acc); + if (NULL != acc) + free (acc); + hs->failed = GNUNET_YES; + return GNUNET_SYSERR; + } + hs->results_obtained++; + return GNUNET_OK; +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +history_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct HistoryState *hs = cls; + uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX; + const uint64_t *row_ptr; + + (void) cmd; + /* Get row_id from trait. */ + if (NULL != hs->start_row_reference) + { + const struct TALER_TESTING_Command *history_cmd; + + history_cmd = TALER_TESTING_interpreter_lookup_command + (is, hs->start_row_reference); + + if (NULL == history_cmd) + TALER_TESTING_FAIL (is); + + if (GNUNET_OK != + TALER_TESTING_get_trait_uint64 (history_cmd, + 0, + &row_ptr)) + TALER_TESTING_FAIL (is); + else + row_id = *row_ptr; + TALER_LOG_DEBUG ("row id (from trait) is %llu\n", + (unsigned long long) row_id); + } + + hs->hh = TALER_BANK_debit_history (is->ctx, + hs->account_url, + &hs->auth, + row_id, + hs->num_results, + &history_cb, + is); + GNUNET_assert (NULL != hs->hh); +} + + +/** + * Free the state from a "history" CMD, and possibly cancel + * a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +history_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct HistoryState *hs = cls; + + (void) cmd; + if (NULL != hs->hh) + { + TALER_LOG_WARNING ("/history did not complete\n"); + TALER_BANK_debit_history_cancel (hs->hh); + } + GNUNET_free (hs); +} + + +/** + * Make a "history" CMD. + * + * @param label command label. + * @param account_url base URL of the account offering the "history" + * operation. + * @param auth login data to use + * @param start_row_reference reference to a command that can + * offer a row identifier, to be used as the starting row + * to accept in the result. + * @param num_results how many rows we want in the result. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_bank_debits (const char *label, + const char *account_url, + const struct TALER_BANK_AuthenticationData *auth, + const char *start_row_reference, + long long num_results) +{ + struct HistoryState *hs; + + hs = GNUNET_new (struct HistoryState); + hs->account_url = account_url; + hs->start_row_reference = start_row_reference; + hs->num_results = num_results; + hs->auth = *auth; + + { + struct TALER_TESTING_Command cmd = { + .label = label, + .cls = hs, + .run = &history_run, + .cleanup = &history_cleanup, + .traits = &history_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_history_debit.c */ diff --git a/src/lib/testing_api_cmd_bank_transfer.c b/src/lib/testing_api_cmd_bank_transfer.c new file mode 100644 index 000000000..d5a3872ed --- /dev/null +++ b/src/lib/testing_api_cmd_bank_transfer.c @@ -0,0 +1,394 @@ +/* + This file is part of TALER + Copyright (C) 2018-2020 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 + +*/ +/** + * @file exchange-lib/testing_api_cmd_transfer.c + * @brief implementation of a bank /transfer command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "backoff.h" +#include "taler_json_lib.h" +#include +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" + + +/** + * State for a "transfer" CMD. + */ +struct TransferState +{ + + /** + * Wire transfer amount. + */ + struct TALER_Amount amount; + + /** + * Base URL of the debit account. + */ + const char *account_debit_url; + + /** + * Money receiver account URL. + */ + const char *payto_credit_account; + + /** + * Username to use for authentication. + */ + struct TALER_BANK_AuthenticationData auth; + + /** + * Base URL of the exchange. + */ + const char *exchange_base_url; + + /** + * Wire transfer identifier to use. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /** + * Handle to the pending request at the fakebank. + */ + struct TALER_BANK_WireExecuteHandle *weh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Set to the wire transfer's unique ID. + */ + uint64_t serial_id; + + /** + * Timestamp of the transaction (as returned from the bank). + */ + struct GNUNET_TIME_Absolute timestamp; + + /** + * Configuration filename. Used to get the tip reserve key + * filename (used to obtain a public key to write in the + * transfer subject). + */ + const char *config_filename; + + /** + * Task scheduled to try later. + */ + struct GNUNET_SCHEDULER_Task *retry_task; + + /** + * How long do we wait until we retry? + */ + struct GNUNET_TIME_Relative backoff; + + /** + * Was this command modified via + * #TALER_TESTING_cmd_admin_add_incoming_with_retry to + * enable retries? + */ + int do_retry; +}; + + +/** + * Run the "transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +transfer_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is); + + +/** + * Task scheduled to re-try #transfer_run. + * + * @param cls a `struct TransferState` + */ +static void +do_retry (void *cls) +{ + struct TransferState *fts = cls; + + fts->retry_task = NULL; + transfer_run (fts, + NULL, + fts->is); +} + + +/** + * This callback will process the fakebank response to the wire + * transfer. It just checks whether the HTTP response code is + * acceptable. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for + * successful status request; 0 if the exchange's reply is + * bogus (fails to follow the protocol) + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param serial_id unique ID of the wire transfer + * @param timestamp time stamp of the transaction made. + */ +static void +confirmation_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t serial_id, + struct GNUNET_TIME_Absolute timestamp) +{ + struct TransferState *fts = cls; + struct TALER_TESTING_Interpreter *is = fts->is; + + fts->weh = NULL; + if (MHD_HTTP_OK != http_status) + { + if (GNUNET_YES == fts->do_retry) + { + if ( (0 == http_status) || + (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || + (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Retrying transfer failed with %u/%d\n", + http_status, + (int) ec); + /* on DB conflicts, do not use backoff */ + if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) + fts->backoff = GNUNET_TIME_UNIT_ZERO; + else + fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); + fts->retry_task = GNUNET_SCHEDULER_add_delayed + (fts->backoff, + &do_retry, + fts); + return; + } + } + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fakebank returned HTTP status %u/%d\n", + http_status, + (int) ec); + TALER_TESTING_interpreter_fail (is); + return; + } + + fts->serial_id = serial_id; + fts->timestamp = timestamp; + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the "transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +transfer_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct TransferState *fts = cls; + void *buf; + size_t buf_size; + + TALER_BANK_prepare_wire_transfer (fts->payto_credit_account, + &fts->amount, + fts->exchange_base_url, + &fts->wtid, + &buf, + &buf_size); + fts->is = is; + fts->weh + = TALER_BANK_execute_wire_transfer + (TALER_TESTING_interpreter_get_context (is), + fts->account_debit_url, + &fts->auth, + buf, + buf_size, + &confirmation_cb, + fts); + GNUNET_free (buf); + if (NULL == fts->weh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "fakebank transfer" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure + * @param cmd current CMD being cleaned up. + */ +static void +transfer_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct TransferState *fts = cls; + + if (NULL != fts->weh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %s did not complete\n", + cmd->label); + TALER_BANK_execute_wire_transfer_cancel (fts->weh); + fts->weh = NULL; + } + if (NULL != fts->retry_task) + { + GNUNET_SCHEDULER_cancel (fts->retry_task); + fts->retry_task = NULL; + } + GNUNET_free (fts); +} + + +/** + * Offer internal data from a "fakebank transfer" CMD to other + * commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +transfer_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct TransferState *fts = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_url (1, fts->account_debit_url), + TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), + TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->payto_credit_account), + TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT (fts->account_debit_url), + TALER_TESTING_make_trait_amount_obj (0, &fts->amount), + TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), + TALER_TESTING_make_trait_wtid (0, + &fts->wtid), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Create transfer command. + * + * @param label command label. + * @param amount amount to transfer. + * @param account_base_url base URL of the account that implements this + * wire transer (which account gives money). + * @param auth authentication data to use + * @param payto_credit_account which account receives money. + * @param wtid wire transfer identifier to use + * @param exchange_base_url exchange URL to use + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_transfer + (const char *label, + const char *amount, + const char *account_base_url, + const struct TALER_BANK_AuthenticationData *auth, + const char *payto_credit_account, + const struct TALER_WireTransferIdentifierRawP *wtid, + const char *exchange_base_url) +{ + struct TransferState *fts; + + fts = GNUNET_new (struct TransferState); + fts->account_debit_url = account_base_url; + fts->exchange_base_url = exchange_base_url; + fts->payto_credit_account = payto_credit_account; + fts->auth = *auth; + fts->wtid = *wtid; + if (GNUNET_OK != + TALER_string_to_amount (amount, + &fts->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + amount, + label); + GNUNET_assert (0); + } + + { + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &transfer_run, + .cleanup = &transfer_cleanup, + .traits = &transfer_traits + }; + + return cmd; + } +} + + +/** + * Modify a transfer command to enable retries when the reserve is not yet + * full or we get other transient errors from the bank. + * + * @param cmd a fakebank transfer command + * @return the command with retries enabled + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd) +{ + struct TransferState *fts; + + GNUNET_assert (&transfer_run == cmd.run); + fts = cmd.cls; + fts->do_retry = GNUNET_YES; + return cmd; +} + + +/* end of testing_api_cmd_transfer.c */ diff --git a/src/lib/testing_api_helpers.c b/src/lib/testing_api_helpers.c deleted file mode 100644 index cc8430784..000000000 --- a/src/lib/testing_api_helpers.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 - -*/ - -/** - * @file exchange-lib/testing_api_helpers.c - * @brief helper functions - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "exchange_api_handle.h" -#include "taler_signatures.h" -#include "taler_testing_lib.h" - - -/** - * Remove files from previous runs - * - * @param config_name configuration filename. - */ -void -TALER_TESTING_cleanup_files (const char *config_name) -{ - if (GNUNET_OK != - GNUNET_CONFIGURATION_parse_and_run (config_name, - &TALER_TESTING_cleanup_files_cfg, - NULL)) - exit (77); -} - - -/** - * Remove files from previous runs - * - * @param cls NULL - * @param cfg configuration - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_cleanup_files_cfg (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char *dir; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "keydir", - &dir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "keydir"); - return GNUNET_SYSERR; - } - if (GNUNET_YES == - GNUNET_DISK_directory_test (dir, - GNUNET_NO)) - GNUNET_break (GNUNET_OK == - GNUNET_DISK_directory_remove (dir)); - GNUNET_free (dir); - // TODO: auditor-specific clean-up here! - return GNUNET_OK; -} - - -/** - * Run `taler-exchange-keyup`. - * - * @param config_filename configuration file to use - * @param output_filename where to write the output for the auditor - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_run_keyup (const char *config_filename, - const char *output_filename) -{ - struct GNUNET_OS_Process *proc; - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-keyup", - "taler-exchange-keyup", - "-c", config_filename, - "-o", output_filename, - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); - return GNUNET_SYSERR; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - return GNUNET_OK; -} - - -/** - * Run `taler-auditor-sign`. - * - * @param config_filename configuration file to use - * @param exchange_master_pub master public key of the exchange - * @param auditor_base_url what is the base URL of the auditor - * @param signdata_in where is the information from taler-exchange-keyup - * @param signdata_out where to write the output for the exchange - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_run_auditor_sign (const char *config_filename, - const char *exchange_master_pub, - const char *auditor_base_url, - const char *signdata_in, - const char *signdata_out) -{ - struct GNUNET_OS_Process *proc; - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-auditor-sign", - "taler-auditor-sign", - "-c", config_filename, - "-u", auditor_base_url, - "-m", exchange_master_pub, - "-r", signdata_in, - "-o", signdata_out, - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-auditor-sign`, is your PATH correct?\n"); - return GNUNET_SYSERR; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - return GNUNET_OK; -} - - -/** - * Run `taler-auditor-exchange`. - * - * @param config_filename configuration file to use - * @param exchange_master_pub master public key of the exchange - * @param exchange_base_url what is the base URL of the exchange - * @param do_remove #GNUNET_NO to add exchange, #GNUNET_YES to remove - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_run_auditor_exchange (const char *config_filename, - const char *exchange_master_pub, - const char *exchange_base_url, - int do_remove) -{ - struct GNUNET_OS_Process *proc; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - - TALER_LOG_DEBUG ("Add exchange (%s,%s) to the auditor\n", - exchange_base_url, - exchange_master_pub); - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-auditor-exchange", - "taler-auditor-exchange", - "-c", config_filename, - "-u", exchange_base_url, - "-m", exchange_master_pub, - (GNUNET_YES == do_remove) - ? "-r" - : NULL, - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-auditor-exchange`, is your PATH correct?\n"); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - GNUNET_OS_process_wait_status (proc, - &type, - &code)); - GNUNET_OS_process_destroy (proc); - if ( (0 != code) || - (GNUNET_OS_PROCESS_EXITED != type) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "taler-auditor-exchange terminated with error (%d/%d)\n", - (int) type, - (int) code); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Run `taler-exchange-dbinit -r` (reset exchange database). - * - * @param config_filename configuration file to use - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_exchange_db_reset (const char *config_filename) -{ - struct GNUNET_OS_Process *proc; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-dbinit", - "taler-exchange-dbinit", - "-c", config_filename, - "-r", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-dbinit`, is your PATH correct?\n"); - return GNUNET_NO; - } - if (GNUNET_SYSERR == - GNUNET_OS_process_wait_status (proc, - &type, - &code)) - { - GNUNET_break (0); - GNUNET_OS_process_destroy (proc); - return GNUNET_SYSERR; - } - GNUNET_OS_process_destroy (proc); - if ( (type == GNUNET_OS_PROCESS_EXITED) && - (0 != code) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup (exchange) database, exit code %d\n", - (int) code); - return GNUNET_NO; - } - if ( (type != GNUNET_OS_PROCESS_EXITED) || - (0 != code) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected error (%d/%d) running `taler-exchange-dbinit'!\n", - (int) type, - (int) code); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Run `taler-auditor-dbinit -r` (reset auditor database). - * - * @param config_filename configuration file to use - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_auditor_db_reset (const char *config_filename) -{ - struct GNUNET_OS_Process *proc; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-auditor-dbinit", - "taler-auditor-dbinit", - "-c", config_filename, - "-r", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-auditor-dbinit`, is your PATH correct?\n"); - return GNUNET_NO; - } - if (GNUNET_SYSERR == - GNUNET_OS_process_wait_status (proc, - &type, - &code)) - { - GNUNET_break (0); - GNUNET_OS_process_destroy (proc); - return GNUNET_SYSERR; - } - GNUNET_OS_process_destroy (proc); - if ( (type == GNUNET_OS_PROCESS_EXITED) && - (0 != code) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup (auditor) database, exit code %d\n", - (int) code); - return GNUNET_NO; - } - if ( (type != GNUNET_OS_PROCESS_EXITED) || - (0 != code) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected error (%d/%d) running `taler-auditor-dbinit'!\n", - (int) type, - (int) code); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Type of closure for - * #sign_keys_for_exchange. - */ -struct SignInfo -{ - /** - * Set to the base URL of the exchange. To be free'd - * by the caller. - */ - char *exchange_base_url; - - /** - * Set to the auditor's base URL. To be free'd by the caller. - */ - char *auditor_base_url; - - /** - * Name of the configuration file to use. - */ - const char *config_filename; - - /** - * Must be set to input file with the data to be signed before - * calling #TALER_TESTING_sign_keys_for_exchange. - */ - const char *auditor_sign_input_filename; -}; - - -/** - * Sign the keys for an exchange given configuration @a cfg. - * The information to be signed must be in a file "auditor.in". - * - * @param cls[in,out] a `struct SignInfo` with - * further paramters - * @param cfg configuration to use - * @return #GNUNET_OK on success - */ -static int -sign_keys_for_exchange (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct SignInfo *si = cls; - char *test_home_dir; - char *signed_keys_out; - char *exchange_master_pub; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "BASE_URL", - &si->exchange_base_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "exchange", - "BASE_URL"); - si->exchange_base_url = NULL; - return GNUNET_NO; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "auditor", - "BASE_URL", - &si->auditor_base_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "auditor", - "BASE_URL"); - GNUNET_free (si->exchange_base_url); - si->exchange_base_url = NULL; - si->auditor_base_url = NULL; - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "paths", - "TALER_TEST_HOME", - &test_home_dir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "paths", - "TALER_TEST_HOME"); - GNUNET_free (si->exchange_base_url); - GNUNET_free (si->auditor_base_url); - si->exchange_base_url = NULL; - si->auditor_base_url = NULL; - return GNUNET_SYSERR; - } - - GNUNET_asprintf (&signed_keys_out, - "%s/.local/share/taler/auditors/auditor.out", - test_home_dir); - GNUNET_free (test_home_dir); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "MASTER_PUBLIC_KEY", - &exchange_master_pub)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "MASTER_PUBLIC_KEY"); - GNUNET_free (si->exchange_base_url); - GNUNET_free (si->auditor_base_url); - si->exchange_base_url = NULL; - si->auditor_base_url = NULL; - GNUNET_free (signed_keys_out); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_TESTING_run_auditor_exchange (si->config_filename, - exchange_master_pub, - si->exchange_base_url, - GNUNET_NO)) - { - GNUNET_free (si->exchange_base_url); - GNUNET_free (si->auditor_base_url); - si->exchange_base_url = NULL; - si->auditor_base_url = NULL; - return GNUNET_NO; - } - - if (GNUNET_OK != - TALER_TESTING_run_auditor_sign (si->config_filename, - exchange_master_pub, - si->auditor_base_url, - si->auditor_sign_input_filename, - signed_keys_out)) - { - GNUNET_free (si->exchange_base_url); - GNUNET_free (si->auditor_base_url); - si->exchange_base_url = NULL; - si->auditor_base_url = NULL; - return GNUNET_NO; - } - GNUNET_free (signed_keys_out); - GNUNET_free (exchange_master_pub); - return GNUNET_OK; -} - - -/** - * Prepare launching an exchange. Checks that the configured - * port is available, runs taler-exchange-keyup, - * taler-auditor-sign and taler-exchange-dbinit. Does NOT - * launch the exchange process itself. - * - * @param config_filename configuration file to use - * @param auditor_base_url[out] will be set to the auditor base url, - * if the config has any; otherwise it will be set to - * NULL. - * @param exchange_base_url[out] will be set to the exchange base url, - * if the config has any; otherwise it will be set to - * NULL. - * @return #GNUNET_OK on success, #GNUNET_NO if test should be - * skipped, #GNUNET_SYSERR on test failure - */ -int -TALER_TESTING_prepare_exchange (const char *config_filename, - char **auditor_base_url, - char **exchange_base_url) -{ - struct SignInfo si = { - .config_filename = config_filename, - .exchange_base_url = NULL, - .auditor_base_url = NULL, - .auditor_sign_input_filename = "auditor.in" - }; - - if (GNUNET_OK != - TALER_TESTING_run_keyup (config_filename, - si.auditor_sign_input_filename)) - return GNUNET_NO; - if (GNUNET_OK != - TALER_TESTING_exchange_db_reset (config_filename)) - return GNUNET_NO; - if (GNUNET_OK != - TALER_TESTING_auditor_db_reset (config_filename)) - return GNUNET_NO; - if (GNUNET_OK != - GNUNET_CONFIGURATION_parse_and_run (config_filename, - &sign_keys_for_exchange, - &si)) - return GNUNET_NO; - *exchange_base_url = si.exchange_base_url; - *auditor_base_url = si.auditor_base_url; - return GNUNET_OK; -} - - -/** - * Find denomination key matching the given amount. - * - * @param keys array of keys to search - * @param amount coin value to look for - * @return NULL if no matching key was found - */ -const struct TALER_EXCHANGE_DenomPublicKey * -TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_Amount *amount) -{ - struct GNUNET_TIME_Absolute now; - struct TALER_EXCHANGE_DenomPublicKey *pk; - char *str; - - now = GNUNET_TIME_absolute_get (); - for (unsigned int i = 0; inum_denom_keys; i++) - { - pk = &keys->denom_keys[i]; - if ( (0 == TALER_amount_cmp (amount, - &pk->value)) && - (now.abs_value_us >= pk->valid_from.abs_value_us) && - (now.abs_value_us < - pk->withdraw_valid_until.abs_value_us) ) - return pk; - } - /* do 2nd pass to check if expiration times are to blame for - * failure */ - str = TALER_amount_to_string (amount); - for (unsigned int i = 0; inum_denom_keys; i++) - { - pk = &keys->denom_keys[i]; - if ( (0 == TALER_amount_cmp (amount, - &pk->value)) && - ( (now.abs_value_us < pk->valid_from.abs_value_us) || - (now.abs_value_us > - pk->withdraw_valid_until.abs_value_us) ) ) - { - GNUNET_log - (GNUNET_ERROR_TYPE_WARNING, - "Have denomination key for `%s', but with wrong" - " expiration range %llu vs [%llu,%llu)\n", - str, - (unsigned long long) now.abs_value_us, - (unsigned long long) pk->valid_from.abs_value_us, - (unsigned long long) - pk->withdraw_valid_until.abs_value_us); - GNUNET_free (str); - return NULL; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No denomination key for amount %s found\n", - str); - GNUNET_free (str); - return NULL; -} - - -/** - * Wait for the exchange to have started. Waits for at - * most 10s, after that returns 77 to indicate an error. - * - * @param base_url what URL should we expect the exchange - * to be running at - * @return 0 on success - */ -int -TALER_TESTING_wait_exchange_ready (const char *base_url) -{ - char *wget_cmd; - unsigned int iter; - - GNUNET_asprintf (&wget_cmd, - "wget -q -t 1 -T 1 %skeys -o /dev/null -O /dev/null", - base_url); // make sure ends with '/' - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `taler-exchange-httpd' to be ready (check with: %s)\n", - wget_cmd); - iter = 0; - do - { - if (10 == iter) - { - fprintf (stderr, - "Failed to launch `taler-exchange-httpd' (or `wget')\n"); - GNUNET_free (wget_cmd); - return 77; - } - fprintf (stderr, ".\n"); - sleep (1); - iter++; - } - while (0 != system (wget_cmd)); - GNUNET_free (wget_cmd); - return 0; -} - - -/** - * Wait for the auditor to have started. Waits for at - * most 10s, after that returns 77 to indicate an error. - * - * @param base_url what URL should we expect the auditor - * to be running at - * @return 0 on success - */ -int -TALER_TESTING_wait_auditor_ready (const char *base_url) -{ - char *wget_cmd; - unsigned int iter; - - GNUNET_asprintf (&wget_cmd, - "wget -q -t 1 -T 1 %sversion -o /dev/null -O /dev/null", - base_url); // make sure ends with '/' - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `taler-auditor-httpd' to be ready\n"); - iter = 0; - do - { - if (10 == iter) - { - fprintf (stderr, - "Failed to launch `taler-auditor-httpd' (or `wget')\n"); - GNUNET_free (wget_cmd); - return 77; - } - fprintf (stderr, ".\n"); - sleep (1); - iter++; - } - while (0 != system (wget_cmd)); - GNUNET_free (wget_cmd); - return 0; -} - - -/** - * Initialize scheduler loop and curl context for the testcase - * including starting and stopping the exchange using the given - * configuration file. - * - * @param main_cb routine containing all the commands to run. - * @param main_cb_cls closure for @a main_cb, typically NULL. - * @param config_file configuration file for the test-suite. - * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. - * non-#GNUNET_OK codes are #GNUNET_SYSERR most of the - * time. - */ -int -TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb, - void *main_cb_cls, - const char *config_filename) -{ - struct TALER_TESTING_SetupContext setup_ctx = { - .config_filename = config_filename, - .main_cb = main_cb, - .main_cb_cls = main_cb_cls - }; - int result; - - result = - GNUNET_CONFIGURATION_parse_and_run (config_filename, - &TALER_TESTING_setup_with_exchange_cfg, - &setup_ctx); - if (GNUNET_OK != result) - return result; - return GNUNET_OK; -} - - -/** - * Initialize scheduler loop and curl context for the test case - * including starting and stopping the exchange using the given - * configuration file. - * - * @param cls must be a `struct TALER_TESTING_SetupContext *` - * @param cfg configuration to use. - * @return #GNUNET_OK if no errors occurred. - */ -int -TALER_TESTING_setup_with_exchange_cfg (void *cls, - const struct - GNUNET_CONFIGURATION_Handle *cfg) -{ - const struct TALER_TESTING_SetupContext *setup_ctx = cls; - struct GNUNET_OS_Process *exchanged; - unsigned long long port; - char *serve; - char *base_url; - int result; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "SERVE", - &serve)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "SERVE"); - return GNUNET_NO; - } - - if (0 == strcmp ("tcp", serve)) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "exchange", - "PORT", - &port)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "PORT"); - GNUNET_free (serve); - return GNUNET_NO; - } - - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - (uint16_t) port)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Required port %llu not available, skipping.\n", - port); - GNUNET_free (serve); - return GNUNET_NO; - } - } - GNUNET_free (serve); - exchanged = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-httpd", - "taler-exchange-httpd", - "-c", setup_ctx->config_filename, - "-i", - NULL); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "BASE_URL", - &base_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "BASE_URL"); - return GNUNET_NO; - } - - if (0 != TALER_TESTING_wait_exchange_ready (base_url)) - { - GNUNET_free (base_url); - return 77; - } - GNUNET_free (base_url); - - /* NOTE: this call blocks. */ - result = TALER_TESTING_setup (setup_ctx->main_cb, - setup_ctx->main_cb_cls, - setup_ctx->config_filename, - exchanged, - GNUNET_YES); - GNUNET_break (0 == - GNUNET_OS_process_kill (exchanged, - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (exchanged)); - GNUNET_OS_process_destroy (exchanged); - return result; -} - - -/** - * Initialize scheduler loop and curl context for the test case - * including starting and stopping the auditor and exchange using the - * given configuration file. - * - * @param cls must be a `struct TALER_TESTING_SetupContext *` - * @param cfg configuration to use. - * @return #GNUNET_OK if no errors occurred. - */ -int -TALER_TESTING_setup_with_auditor_and_exchange_cfg (void *cls, - const struct - GNUNET_CONFIGURATION_Handle * - cfg) -{ - const struct TALER_TESTING_SetupContext *setup_ctx = cls; - struct GNUNET_OS_Process *auditord; - unsigned long long port; - char *serve; - char *base_url; - int result; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "auditor", - "SERVE", - &serve)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "SERVE"); - return GNUNET_NO; - } - - if (0 == strcmp ("tcp", serve)) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "auditor", - "PORT", - &port)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "PORT"); - GNUNET_free (serve); - return GNUNET_NO; - } - - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - (uint16_t) port)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Required port %llu not available, skipping.\n", - port); - GNUNET_free (serve); - return GNUNET_NO; - } - } - GNUNET_free (serve); - auditord = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-auditor-httpd", - "taler-auditor-httpd", - "-c", setup_ctx->config_filename, - NULL); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "auditor", - "BASE_URL", - &base_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "BASE_URL"); - return GNUNET_NO; - } - - if (0 != TALER_TESTING_wait_auditor_ready (base_url)) - { - GNUNET_free (base_url); - GNUNET_break (0 == - GNUNET_OS_process_kill (auditord, - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (auditord)); - GNUNET_OS_process_destroy (auditord); - return 77; - } - GNUNET_free (base_url); - - /* NOTE: this call blocks. */ - result = TALER_TESTING_setup_with_exchange_cfg ((void *) setup_ctx, - cfg); - GNUNET_break (0 == - GNUNET_OS_process_kill (auditord, - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (auditord)); - GNUNET_OS_process_destroy (auditord); - return result; -} - - -/** - * Initialize scheduler loop and curl context for the test case - * including starting and stopping the auditor and exchange using the - * given configuration file. - * - * @param main_cb main method. - * @param main_cb_cls main method closure. - * @param config_filename configuration file name. Is is used - * by both this function and the exchange itself. In the - * first case it gives out the exchange port number and - * the exchange base URL so as to check whether the port - * is available and the exchange responds when requested - * at its base URL. - * @return #GNUNET_OK if no errors occurred. - */ -int -TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb, - void *main_cb_cls, - const char *config_file) -{ - struct TALER_TESTING_SetupContext setup_ctx = { - .config_filename = config_file, - .main_cb = main_cb, - .main_cb_cls = main_cb_cls - }; - - return GNUNET_CONFIGURATION_parse_and_run (config_file, - & - TALER_TESTING_setup_with_auditor_and_exchange_cfg, - &setup_ctx); -} - - -/** - * Test port in URL string for availability. - */ -int -TALER_TESTING_url_port_free (const char *url) -{ - const char *port; - long pnum; - - port = strrchr (url, - (unsigned char) ':'); - if (NULL == port) - pnum = 80; - else - pnum = strtol (port + 1, NULL, 10); - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - pnum)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Port %u not available.\n", - (unsigned int) pnum); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Allocate and return a piece of wire-details. Combines - * the @a account_no and the @a bank_url to a - * @a payto://-URL and adds some salt to create the JSON. - * - * @param account_no account number - * @param bank_url the bank_url (FIXME/WARNING: shouldn't this be a _hostname_ ??) - * @return JSON describing the account, including the - * payto://-URL of the account, must be manually decref'd - */ -json_t * -TALER_TESTING_make_wire_details (unsigned long long account_no, - const char *bank_url) -{ - char *payto; - json_t *ret; - int ends_slash; - - if (0 < strlen (bank_url)) - ends_slash = '/' == bank_url[strlen (bank_url) - 1]; - else - ends_slash = 0; - - GNUNET_asprintf (&payto, - (ends_slash) - ? "payto://x-taler-bank/%s%llu" - : "payto://x-taler-bank/%s/%llu", - bank_url, - account_no); - ret = json_pack ("{s:s, s:s}", - "url", payto, - "salt", - "test-salt (must be constant for aggregation tests)"); - GNUNET_free (payto); - return ret; -} - - -/** - * Prepare launching a fakebank. Check that the configuration - * file has the right option, and that the port is available. - * If everything is OK, return the configured URL of the fakebank. - * - * @param config_filename configuration file to use - * @param config_section which account to use (must match x-taler-bank) - * @return NULL on error, fakebank URL otherwise - */ -char * -TALER_TESTING_prepare_fakebank (const char *config_filename, - const char *config_section) -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - char *payto_url; - char *fakebank_url; - const char *start; - const char *end; - - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, - config_filename)) - return NULL; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - config_section, - "URL", - &payto_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - config_section, - "URL"); - GNUNET_CONFIGURATION_destroy (cfg); - return NULL; - } - GNUNET_CONFIGURATION_destroy (cfg); - if (0 != strncasecmp (payto_url, - "payto://x-taler-bank/", - strlen ("payto://x-taler-bank/"))) - { - GNUNET_log_config_invalid - (GNUNET_ERROR_TYPE_WARNING, - config_section, - "URL", - "expected `x-taler-bank' payto://-URL"); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (payto_url); - return NULL; - } - start = &payto_url [strlen ("payto://x-taler-bank/")]; - end = strchr (start, - (unsigned char) '/'); - if (NULL == end) - end = &start[strlen (start)]; - fakebank_url = GNUNET_strndup (start, - end - start); - GNUNET_free (payto_url); - if (GNUNET_OK != - TALER_TESTING_url_port_free (fakebank_url)) - { - GNUNET_free (fakebank_url); - return NULL; - } - return fakebank_url; -} - - -/* end of testing_api_helpers.c */ diff --git a/src/lib/testing_api_helpers_auditor.c b/src/lib/testing_api_helpers_auditor.c new file mode 100644 index 000000000..cf15131da --- /dev/null +++ b/src/lib/testing_api_helpers_auditor.c @@ -0,0 +1,232 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 + +*/ + +/** + * @file auditor-lib/testing_auditor_api_helpers.c + * @brief helper functions + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_testing_lib.h" +#include "taler_testing_auditor_lib.h" +#include "taler_auditor_service.h" + + +/** + * Closure for #cleanup_auditor. + */ +struct CleanupContext +{ + /** + * Where we find the state to clean up. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Next cleanup routine to call, NULL for none. + */ + GNUNET_SCHEDULER_TaskCallback fcb; + + /** + * Closure for @e fcb + */ + void *fcb_cls; +}; + + +/** + * Function to clean up the auditor connection. + * + * @param cls a `struct CleanupContext` + */ +static void +cleanup_auditor (void *cls) +{ + struct CleanupContext *cc = cls; + struct TALER_TESTING_Interpreter *is = cc->is; + + TALER_AUDITOR_disconnect (is->auditor); + is->auditor = NULL; + if (NULL != cc->fcb) + cc->fcb (cc->fcb_cls); + GNUNET_free (cc); +} + + +/** + * Closure for #auditor_main_wrapper() + */ +struct MainWrapperContext +{ + /** + * Main function to launch. + */ + TALER_TESTING_Main main_cb; + + /** + * Closure for @e main_cb. + */ + void *main_cb_cls; + + /** + * Configuration we use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Name of the configuration file. + */ + const char *config_filename; + +}; + + +/** + * Function called with information about the auditor. + * + * @param cls closure + * @param vi basic information about the auditor + * @param compat protocol compatibility information + */ +static void +auditor_version_cb + (void *cls, + const struct TALER_AUDITOR_VersionInformation *vi, + enum TALER_AUDITOR_VersionCompatibility compat) +{ + struct TALER_TESTING_Interpreter *is = cls; + + if (TALER_AUDITOR_VC_MATCH != compat) + { + TALER_TESTING_interpreter_fail (is); + return; + } + + is->auditor_working = GNUNET_YES; +} + + +/** + * Setup the @a is 'auditor' member before running the main test loop. + * + * @param cls must be a `struct MainWrapperContext *` + * @param is[in,out] interpreter state to setup + */ +static void +auditor_main_wrapper (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + struct MainWrapperContext *mwc = cls; + struct CleanupContext *cc; + char *auditor_base_url; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (mwc->cfg, + "auditor", + "BASE_URL", + &auditor_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "BASE_URL"); + return; + } + + is->auditor = TALER_AUDITOR_connect (is->ctx, + auditor_base_url, + &auditor_version_cb, + is); + GNUNET_free (auditor_base_url); + + if (NULL == is->auditor) + { + GNUNET_break (0); + return; + } + + cc = GNUNET_new (struct CleanupContext); + cc->is = is; + cc->fcb = is->final_cleanup_cb; + cc->fcb_cls = is->final_cleanup_cb; + is->final_cleanup_cb = cleanup_auditor; + is->final_cleanup_cb_cls = cc; + mwc->main_cb (mwc->main_cb_cls, + is); +} + + +/** + * Install signal handlers plus schedules the main wrapper + * around the "run" method. + * + * @param cls our `struct MainWrapperContext` + * @param cfg configuration we use + * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. + * non-GNUNET_OK codes are #GNUNET_SYSERR most of the + * times. + */ +static int +setup_with_cfg (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct MainWrapperContext *mwc = cls; + struct TALER_TESTING_SetupContext setup_ctx = { + .config_filename = mwc->config_filename, + .main_cb = &auditor_main_wrapper, + .main_cb_cls = mwc + }; + + mwc->cfg = cfg; + return TALER_TESTING_setup_with_auditor_and_exchange_cfg (&setup_ctx, + cfg); +} + + +/** + * Install signal handlers plus schedules the main wrapper + * around the "run" method. + * + * @param main_cb the "run" method which contains all the + * commands. + * @param main_cb_cls a closure for "run", typically NULL. + * @param config_filename configuration filename. + * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. + * non-GNUNET_OK codes are #GNUNET_SYSERR most of the + * times. + */ +int +TALER_TESTING_AUDITOR_setup (TALER_TESTING_Main main_cb, + void *main_cb_cls, + const char *config_filename) +{ + struct MainWrapperContext mwc = { + .main_cb = main_cb, + .main_cb_cls = main_cb_cls, + .config_filename = config_filename + }; + + return GNUNET_CONFIGURATION_parse_and_run (config_filename, + &setup_with_cfg, + &mwc); +} + + +/* end of testing_auditor_api_helpers.c */ diff --git a/src/lib/testing_api_helpers_bank.c b/src/lib/testing_api_helpers_bank.c new file mode 100644 index 000000000..64976edbb --- /dev/null +++ b/src/lib/testing_api_helpers_bank.c @@ -0,0 +1,361 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 + +*/ + +/** + * @file bank-lib/testing_api_helpers.c + * @brief convenience functions for bank-lib tests. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include +#include "taler_testing_bank_lib.h" +#include "taler_fakebank_lib.h" + + +#define BANK_FAIL() \ + do {GNUNET_break (0); return NULL; } while (0) + + +/** + * Keep each bank account credentials at index: + * bank account number - 1 + */ +struct TALER_BANK_AuthenticationData AUTHS[] = { + + /* Bank credentials */ + {.method = TALER_BANK_AUTH_BASIC, + .details.basic.username = TALER_TESTING_BANK_USERNAME, + .details.basic.password = TALER_TESTING_BANK_PASSWORD}, + + /* Exchange credentials */ + {.method = TALER_BANK_AUTH_BASIC, + .details.basic.username = TALER_TESTING_EXCHANGE_USERNAME, + .details.basic.password = TALER_TESTING_EXCHANGE_PASSWORD }, + + /* User credentials */ + {.method = TALER_BANK_AUTH_BASIC, + .details.basic.username = TALER_TESTING_USER_USERNAME, + .details.basic.password = TALER_TESTING_USER_PASSWORD } +}; + + +/** + * Runs the Fakebank by guessing / extracting the portnumber + * from the base URL. + * + * @param bank_url bank's base URL. + * @return the fakebank process handle, or NULL if any + * error occurs. + */ +struct TALER_FAKEBANK_Handle * +TALER_TESTING_run_fakebank (const char *bank_url) +{ + const char *port; + long pnum; + struct TALER_FAKEBANK_Handle *fakebankd; + + port = strrchr (bank_url, + (unsigned char) ':'); + if (NULL == port) + pnum = 80; + else + pnum = strtol (port + 1, NULL, 10); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Starting Fakebank on port %u (%s)\n", + (unsigned int) pnum, + bank_url); + fakebankd = TALER_FAKEBANK_start ((uint16_t) pnum); + if (NULL == fakebankd) + { + GNUNET_break (0); + return NULL; + } + return fakebankd; +} + + +/** + * Look for substring in a programs' name. + * + * @param prog program's name to look into + * @param marker chunk to find in @a prog + */ +int +TALER_TESTING_has_in_name (const char *prog_name, + const char *marker) +{ + size_t name_pos; + size_t pos; + + if (! prog_name || ! marker) + return GNUNET_NO; + + pos = 0; + name_pos = 0; + while (prog_name[pos]) + { + if ('/' == prog_name[pos]) + name_pos = pos + 1; + pos++; + } + if (name_pos == pos) + return GNUNET_YES; + return strstr (prog_name + name_pos, marker) != NULL; +} + + +/** + * Start the (Python) bank process. Assume the port + * is available and the database is clean. Use the "prepare + * bank" function to do such tasks. + * + * @param config_filename configuration filename. + * @param bank_url base URL of the bank, used by `wget' to check + * that the bank was started right. + * + * @return the process, or NULL if the process could not + * be started. + */ +struct GNUNET_OS_Process * +TALER_TESTING_run_bank (const char *config_filename, + const char *bank_url) +{ + struct GNUNET_OS_Process *bank_proc; + unsigned int iter; + char *wget_cmd; + char *database; + char *serve_cfg; + char *serve_arg; + struct GNUNET_CONFIGURATION_Handle *cfg; + + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (cfg, + config_filename)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + exit (77); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "bank", + "database", + &database)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "bank", + "database"); + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + exit (77); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "bank", + "serve", + &serve_cfg)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "bank", + "serve"); + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (database); + exit (77); + } + GNUNET_CONFIGURATION_destroy (cfg); + + serve_arg = "serve-http"; + if (0 != strcmp ("http", serve_cfg)) + serve_arg = "serve-uwsgi"; + GNUNET_free (serve_cfg); + bank_proc = GNUNET_OS_start_process + (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-bank-manage-testing", + "taler-bank-manage-testing", + config_filename, + database, + serve_arg, NULL); + GNUNET_free (database); + if (NULL == bank_proc) + { + BANK_FAIL (); + } + + GNUNET_asprintf (&wget_cmd, + "wget -q -t 2 -T 1 %s -o /dev/null -O /dev/null", + bank_url); + + /* give child time to start and bind against the socket */ + fprintf (stderr, + "Waiting for `taler-bank-manage' to be ready"); + iter = 0; + do + { + if (10 == iter) + { + fprintf ( + stderr, + "Failed to launch `taler-bank-manage' (or `wget')\n"); + GNUNET_OS_process_kill (bank_proc, + SIGTERM); + GNUNET_OS_process_wait (bank_proc); + GNUNET_OS_process_destroy (bank_proc); + GNUNET_free (wget_cmd); + BANK_FAIL (); + } + fprintf (stderr, "."); + sleep (1); + iter++; + } + while (0 != system (wget_cmd)); + GNUNET_free (wget_cmd); + fprintf (stderr, "\n"); + + return bank_proc; + +} + + +/** + * Prepare the bank execution. Check if the port is available + * and reset database. + * + * @param config_filename configuration file name. + * + * @return the base url, or NULL upon errors. Must be freed + * by the caller. + */ +char * +TALER_TESTING_prepare_bank (const char *config_filename) +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + unsigned long long port; + struct GNUNET_OS_Process *dbreset_proc; + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; + char *base_url; + char *database; + + cfg = GNUNET_CONFIGURATION_create (); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (cfg, config_filename)) + { + GNUNET_CONFIGURATION_destroy (cfg); + BANK_FAIL (); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "bank", + "DATABASE", + &database)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "bank", + "DATABASE"); + GNUNET_CONFIGURATION_destroy (cfg); + BANK_FAIL (); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "bank", + "HTTP_PORT", + &port)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "bank", + "HTTP_PORT"); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (database); + BANK_FAIL (); + } + GNUNET_CONFIGURATION_destroy (cfg); + + if (GNUNET_OK != GNUNET_NETWORK_test_port_free + (IPPROTO_TCP, (uint16_t) port)) + { + fprintf (stderr, + "Required port %llu not available, skipping.\n", + port); + BANK_FAIL (); + } + + /* DB preparation */ + if (NULL == + (dbreset_proc = GNUNET_OS_start_process ( + GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-bank-manage", + "taler-bank-manage", + "-c", "bank.conf", + "--with-db", database, + "django", + "flush", + "--no-input", NULL))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to flush the bank db.\n"); + GNUNET_free (database); + BANK_FAIL (); + } + GNUNET_free (database); + + if (GNUNET_SYSERR == + GNUNET_OS_process_wait_status (dbreset_proc, + &type, + &code)) + { + GNUNET_OS_process_destroy (dbreset_proc); + BANK_FAIL (); + } + if ( (type == GNUNET_OS_PROCESS_EXITED) && + (0 != code) ) + { + fprintf (stderr, + "Failed to setup database\n"); + BANK_FAIL (); + } + if ( (type != GNUNET_OS_PROCESS_EXITED) || + (0 != code) ) + { + fprintf (stderr, + "Unexpected error running" + " `taler-bank-manage django flush..'!\n"); + BANK_FAIL (); + } + + GNUNET_OS_process_destroy (dbreset_proc); + + GNUNET_asprintf (&base_url, + "http://localhost:%llu/", + port); + return base_url; +} + + +/* end of testing_api_helpers.c */ diff --git a/src/lib/testing_api_helpers_exchange.c b/src/lib/testing_api_helpers_exchange.c new file mode 100644 index 000000000..cc8430784 --- /dev/null +++ b/src/lib/testing_api_helpers_exchange.c @@ -0,0 +1,1080 @@ +/* + This file is part of TALER + Copyright (C) 2018 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 + +*/ + +/** + * @file exchange-lib/testing_api_helpers.c + * @brief helper functions + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * Remove files from previous runs + * + * @param config_name configuration filename. + */ +void +TALER_TESTING_cleanup_files (const char *config_name) +{ + if (GNUNET_OK != + GNUNET_CONFIGURATION_parse_and_run (config_name, + &TALER_TESTING_cleanup_files_cfg, + NULL)) + exit (77); +} + + +/** + * Remove files from previous runs + * + * @param cls NULL + * @param cfg configuration + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_cleanup_files_cfg (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *dir; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "exchange", + "keydir", + &dir)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "keydir"); + return GNUNET_SYSERR; + } + if (GNUNET_YES == + GNUNET_DISK_directory_test (dir, + GNUNET_NO)) + GNUNET_break (GNUNET_OK == + GNUNET_DISK_directory_remove (dir)); + GNUNET_free (dir); + // TODO: auditor-specific clean-up here! + return GNUNET_OK; +} + + +/** + * Run `taler-exchange-keyup`. + * + * @param config_filename configuration file to use + * @param output_filename where to write the output for the auditor + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_run_keyup (const char *config_filename, + const char *output_filename) +{ + struct GNUNET_OS_Process *proc; + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-keyup", + "taler-exchange-keyup", + "-c", config_filename, + "-o", output_filename, + NULL); + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); + return GNUNET_SYSERR; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + return GNUNET_OK; +} + + +/** + * Run `taler-auditor-sign`. + * + * @param config_filename configuration file to use + * @param exchange_master_pub master public key of the exchange + * @param auditor_base_url what is the base URL of the auditor + * @param signdata_in where is the information from taler-exchange-keyup + * @param signdata_out where to write the output for the exchange + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_run_auditor_sign (const char *config_filename, + const char *exchange_master_pub, + const char *auditor_base_url, + const char *signdata_in, + const char *signdata_out) +{ + struct GNUNET_OS_Process *proc; + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor-sign", + "taler-auditor-sign", + "-c", config_filename, + "-u", auditor_base_url, + "-m", exchange_master_pub, + "-r", signdata_in, + "-o", signdata_out, + NULL); + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to run `taler-auditor-sign`, is your PATH correct?\n"); + return GNUNET_SYSERR; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + return GNUNET_OK; +} + + +/** + * Run `taler-auditor-exchange`. + * + * @param config_filename configuration file to use + * @param exchange_master_pub master public key of the exchange + * @param exchange_base_url what is the base URL of the exchange + * @param do_remove #GNUNET_NO to add exchange, #GNUNET_YES to remove + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_run_auditor_exchange (const char *config_filename, + const char *exchange_master_pub, + const char *exchange_base_url, + int do_remove) +{ + struct GNUNET_OS_Process *proc; + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; + + TALER_LOG_DEBUG ("Add exchange (%s,%s) to the auditor\n", + exchange_base_url, + exchange_master_pub); + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor-exchange", + "taler-auditor-exchange", + "-c", config_filename, + "-u", exchange_base_url, + "-m", exchange_master_pub, + (GNUNET_YES == do_remove) + ? "-r" + : NULL, + NULL); + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to run `taler-auditor-exchange`, is your PATH correct?\n"); + return GNUNET_SYSERR; + } + GNUNET_assert (GNUNET_OK == + GNUNET_OS_process_wait_status (proc, + &type, + &code)); + GNUNET_OS_process_destroy (proc); + if ( (0 != code) || + (GNUNET_OS_PROCESS_EXITED != type) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "taler-auditor-exchange terminated with error (%d/%d)\n", + (int) type, + (int) code); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Run `taler-exchange-dbinit -r` (reset exchange database). + * + * @param config_filename configuration file to use + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_exchange_db_reset (const char *config_filename) +{ + struct GNUNET_OS_Process *proc; + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-dbinit", + "taler-exchange-dbinit", + "-c", config_filename, + "-r", + NULL); + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to run `taler-exchange-dbinit`, is your PATH correct?\n"); + return GNUNET_NO; + } + if (GNUNET_SYSERR == + GNUNET_OS_process_wait_status (proc, + &type, + &code)) + { + GNUNET_break (0); + GNUNET_OS_process_destroy (proc); + return GNUNET_SYSERR; + } + GNUNET_OS_process_destroy (proc); + if ( (type == GNUNET_OS_PROCESS_EXITED) && + (0 != code) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup (exchange) database, exit code %d\n", + (int) code); + return GNUNET_NO; + } + if ( (type != GNUNET_OS_PROCESS_EXITED) || + (0 != code) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected error (%d/%d) running `taler-exchange-dbinit'!\n", + (int) type, + (int) code); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Run `taler-auditor-dbinit -r` (reset auditor database). + * + * @param config_filename configuration file to use + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_auditor_db_reset (const char *config_filename) +{ + struct GNUNET_OS_Process *proc; + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; + + proc = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor-dbinit", + "taler-auditor-dbinit", + "-c", config_filename, + "-r", + NULL); + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to run `taler-auditor-dbinit`, is your PATH correct?\n"); + return GNUNET_NO; + } + if (GNUNET_SYSERR == + GNUNET_OS_process_wait_status (proc, + &type, + &code)) + { + GNUNET_break (0); + GNUNET_OS_process_destroy (proc); + return GNUNET_SYSERR; + } + GNUNET_OS_process_destroy (proc); + if ( (type == GNUNET_OS_PROCESS_EXITED) && + (0 != code) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup (auditor) database, exit code %d\n", + (int) code); + return GNUNET_NO; + } + if ( (type != GNUNET_OS_PROCESS_EXITED) || + (0 != code) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected error (%d/%d) running `taler-auditor-dbinit'!\n", + (int) type, + (int) code); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Type of closure for + * #sign_keys_for_exchange. + */ +struct SignInfo +{ + /** + * Set to the base URL of the exchange. To be free'd + * by the caller. + */ + char *exchange_base_url; + + /** + * Set to the auditor's base URL. To be free'd by the caller. + */ + char *auditor_base_url; + + /** + * Name of the configuration file to use. + */ + const char *config_filename; + + /** + * Must be set to input file with the data to be signed before + * calling #TALER_TESTING_sign_keys_for_exchange. + */ + const char *auditor_sign_input_filename; +}; + + +/** + * Sign the keys for an exchange given configuration @a cfg. + * The information to be signed must be in a file "auditor.in". + * + * @param cls[in,out] a `struct SignInfo` with + * further paramters + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +static int +sign_keys_for_exchange (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct SignInfo *si = cls; + char *test_home_dir; + char *signed_keys_out; + char *exchange_master_pub; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "BASE_URL", + &si->exchange_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "exchange", + "BASE_URL"); + si->exchange_base_url = NULL; + return GNUNET_NO; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "auditor", + "BASE_URL", + &si->auditor_base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "auditor", + "BASE_URL"); + GNUNET_free (si->exchange_base_url); + si->exchange_base_url = NULL; + si->auditor_base_url = NULL; + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "paths", + "TALER_TEST_HOME", + &test_home_dir)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "paths", + "TALER_TEST_HOME"); + GNUNET_free (si->exchange_base_url); + GNUNET_free (si->auditor_base_url); + si->exchange_base_url = NULL; + si->auditor_base_url = NULL; + return GNUNET_SYSERR; + } + + GNUNET_asprintf (&signed_keys_out, + "%s/.local/share/taler/auditors/auditor.out", + test_home_dir); + GNUNET_free (test_home_dir); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "MASTER_PUBLIC_KEY", + &exchange_master_pub)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "MASTER_PUBLIC_KEY"); + GNUNET_free (si->exchange_base_url); + GNUNET_free (si->auditor_base_url); + si->exchange_base_url = NULL; + si->auditor_base_url = NULL; + GNUNET_free (signed_keys_out); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_TESTING_run_auditor_exchange (si->config_filename, + exchange_master_pub, + si->exchange_base_url, + GNUNET_NO)) + { + GNUNET_free (si->exchange_base_url); + GNUNET_free (si->auditor_base_url); + si->exchange_base_url = NULL; + si->auditor_base_url = NULL; + return GNUNET_NO; + } + + if (GNUNET_OK != + TALER_TESTING_run_auditor_sign (si->config_filename, + exchange_master_pub, + si->auditor_base_url, + si->auditor_sign_input_filename, + signed_keys_out)) + { + GNUNET_free (si->exchange_base_url); + GNUNET_free (si->auditor_base_url); + si->exchange_base_url = NULL; + si->auditor_base_url = NULL; + return GNUNET_NO; + } + GNUNET_free (signed_keys_out); + GNUNET_free (exchange_master_pub); + return GNUNET_OK; +} + + +/** + * Prepare launching an exchange. Checks that the configured + * port is available, runs taler-exchange-keyup, + * taler-auditor-sign and taler-exchange-dbinit. Does NOT + * launch the exchange process itself. + * + * @param config_filename configuration file to use + * @param auditor_base_url[out] will be set to the auditor base url, + * if the config has any; otherwise it will be set to + * NULL. + * @param exchange_base_url[out] will be set to the exchange base url, + * if the config has any; otherwise it will be set to + * NULL. + * @return #GNUNET_OK on success, #GNUNET_NO if test should be + * skipped, #GNUNET_SYSERR on test failure + */ +int +TALER_TESTING_prepare_exchange (const char *config_filename, + char **auditor_base_url, + char **exchange_base_url) +{ + struct SignInfo si = { + .config_filename = config_filename, + .exchange_base_url = NULL, + .auditor_base_url = NULL, + .auditor_sign_input_filename = "auditor.in" + }; + + if (GNUNET_OK != + TALER_TESTING_run_keyup (config_filename, + si.auditor_sign_input_filename)) + return GNUNET_NO; + if (GNUNET_OK != + TALER_TESTING_exchange_db_reset (config_filename)) + return GNUNET_NO; + if (GNUNET_OK != + TALER_TESTING_auditor_db_reset (config_filename)) + return GNUNET_NO; + if (GNUNET_OK != + GNUNET_CONFIGURATION_parse_and_run (config_filename, + &sign_keys_for_exchange, + &si)) + return GNUNET_NO; + *exchange_base_url = si.exchange_base_url; + *auditor_base_url = si.auditor_base_url; + return GNUNET_OK; +} + + +/** + * Find denomination key matching the given amount. + * + * @param keys array of keys to search + * @param amount coin value to look for + * @return NULL if no matching key was found + */ +const struct TALER_EXCHANGE_DenomPublicKey * +TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_Amount *amount) +{ + struct GNUNET_TIME_Absolute now; + struct TALER_EXCHANGE_DenomPublicKey *pk; + char *str; + + now = GNUNET_TIME_absolute_get (); + for (unsigned int i = 0; inum_denom_keys; i++) + { + pk = &keys->denom_keys[i]; + if ( (0 == TALER_amount_cmp (amount, + &pk->value)) && + (now.abs_value_us >= pk->valid_from.abs_value_us) && + (now.abs_value_us < + pk->withdraw_valid_until.abs_value_us) ) + return pk; + } + /* do 2nd pass to check if expiration times are to blame for + * failure */ + str = TALER_amount_to_string (amount); + for (unsigned int i = 0; inum_denom_keys; i++) + { + pk = &keys->denom_keys[i]; + if ( (0 == TALER_amount_cmp (amount, + &pk->value)) && + ( (now.abs_value_us < pk->valid_from.abs_value_us) || + (now.abs_value_us > + pk->withdraw_valid_until.abs_value_us) ) ) + { + GNUNET_log + (GNUNET_ERROR_TYPE_WARNING, + "Have denomination key for `%s', but with wrong" + " expiration range %llu vs [%llu,%llu)\n", + str, + (unsigned long long) now.abs_value_us, + (unsigned long long) pk->valid_from.abs_value_us, + (unsigned long long) + pk->withdraw_valid_until.abs_value_us); + GNUNET_free (str); + return NULL; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No denomination key for amount %s found\n", + str); + GNUNET_free (str); + return NULL; +} + + +/** + * Wait for the exchange to have started. Waits for at + * most 10s, after that returns 77 to indicate an error. + * + * @param base_url what URL should we expect the exchange + * to be running at + * @return 0 on success + */ +int +TALER_TESTING_wait_exchange_ready (const char *base_url) +{ + char *wget_cmd; + unsigned int iter; + + GNUNET_asprintf (&wget_cmd, + "wget -q -t 1 -T 1 %skeys -o /dev/null -O /dev/null", + base_url); // make sure ends with '/' + /* give child time to start and bind against the socket */ + fprintf (stderr, + "Waiting for `taler-exchange-httpd' to be ready (check with: %s)\n", + wget_cmd); + iter = 0; + do + { + if (10 == iter) + { + fprintf (stderr, + "Failed to launch `taler-exchange-httpd' (or `wget')\n"); + GNUNET_free (wget_cmd); + return 77; + } + fprintf (stderr, ".\n"); + sleep (1); + iter++; + } + while (0 != system (wget_cmd)); + GNUNET_free (wget_cmd); + return 0; +} + + +/** + * Wait for the auditor to have started. Waits for at + * most 10s, after that returns 77 to indicate an error. + * + * @param base_url what URL should we expect the auditor + * to be running at + * @return 0 on success + */ +int +TALER_TESTING_wait_auditor_ready (const char *base_url) +{ + char *wget_cmd; + unsigned int iter; + + GNUNET_asprintf (&wget_cmd, + "wget -q -t 1 -T 1 %sversion -o /dev/null -O /dev/null", + base_url); // make sure ends with '/' + /* give child time to start and bind against the socket */ + fprintf (stderr, + "Waiting for `taler-auditor-httpd' to be ready\n"); + iter = 0; + do + { + if (10 == iter) + { + fprintf (stderr, + "Failed to launch `taler-auditor-httpd' (or `wget')\n"); + GNUNET_free (wget_cmd); + return 77; + } + fprintf (stderr, ".\n"); + sleep (1); + iter++; + } + while (0 != system (wget_cmd)); + GNUNET_free (wget_cmd); + return 0; +} + + +/** + * Initialize scheduler loop and curl context for the testcase + * including starting and stopping the exchange using the given + * configuration file. + * + * @param main_cb routine containing all the commands to run. + * @param main_cb_cls closure for @a main_cb, typically NULL. + * @param config_file configuration file for the test-suite. + * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. + * non-#GNUNET_OK codes are #GNUNET_SYSERR most of the + * time. + */ +int +TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb, + void *main_cb_cls, + const char *config_filename) +{ + struct TALER_TESTING_SetupContext setup_ctx = { + .config_filename = config_filename, + .main_cb = main_cb, + .main_cb_cls = main_cb_cls + }; + int result; + + result = + GNUNET_CONFIGURATION_parse_and_run (config_filename, + &TALER_TESTING_setup_with_exchange_cfg, + &setup_ctx); + if (GNUNET_OK != result) + return result; + return GNUNET_OK; +} + + +/** + * Initialize scheduler loop and curl context for the test case + * including starting and stopping the exchange using the given + * configuration file. + * + * @param cls must be a `struct TALER_TESTING_SetupContext *` + * @param cfg configuration to use. + * @return #GNUNET_OK if no errors occurred. + */ +int +TALER_TESTING_setup_with_exchange_cfg (void *cls, + const struct + GNUNET_CONFIGURATION_Handle *cfg) +{ + const struct TALER_TESTING_SetupContext *setup_ctx = cls; + struct GNUNET_OS_Process *exchanged; + unsigned long long port; + char *serve; + char *base_url; + int result; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "SERVE", + &serve)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "SERVE"); + return GNUNET_NO; + } + + if (0 == strcmp ("tcp", serve)) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "exchange", + "PORT", + &port)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "PORT"); + GNUNET_free (serve); + return GNUNET_NO; + } + + if (GNUNET_OK != + GNUNET_NETWORK_test_port_free (IPPROTO_TCP, + (uint16_t) port)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Required port %llu not available, skipping.\n", + port); + GNUNET_free (serve); + return GNUNET_NO; + } + } + GNUNET_free (serve); + exchanged = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-httpd", + "taler-exchange-httpd", + "-c", setup_ctx->config_filename, + "-i", + NULL); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "BASE_URL", + &base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL"); + return GNUNET_NO; + } + + if (0 != TALER_TESTING_wait_exchange_ready (base_url)) + { + GNUNET_free (base_url); + return 77; + } + GNUNET_free (base_url); + + /* NOTE: this call blocks. */ + result = TALER_TESTING_setup (setup_ctx->main_cb, + setup_ctx->main_cb_cls, + setup_ctx->config_filename, + exchanged, + GNUNET_YES); + GNUNET_break (0 == + GNUNET_OS_process_kill (exchanged, + SIGTERM)); + GNUNET_break (GNUNET_OK == + GNUNET_OS_process_wait (exchanged)); + GNUNET_OS_process_destroy (exchanged); + return result; +} + + +/** + * Initialize scheduler loop and curl context for the test case + * including starting and stopping the auditor and exchange using the + * given configuration file. + * + * @param cls must be a `struct TALER_TESTING_SetupContext *` + * @param cfg configuration to use. + * @return #GNUNET_OK if no errors occurred. + */ +int +TALER_TESTING_setup_with_auditor_and_exchange_cfg (void *cls, + const struct + GNUNET_CONFIGURATION_Handle * + cfg) +{ + const struct TALER_TESTING_SetupContext *setup_ctx = cls; + struct GNUNET_OS_Process *auditord; + unsigned long long port; + char *serve; + char *base_url; + int result; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "auditor", + "SERVE", + &serve)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "SERVE"); + return GNUNET_NO; + } + + if (0 == strcmp ("tcp", serve)) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "auditor", + "PORT", + &port)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "PORT"); + GNUNET_free (serve); + return GNUNET_NO; + } + + if (GNUNET_OK != + GNUNET_NETWORK_test_port_free (IPPROTO_TCP, + (uint16_t) port)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Required port %llu not available, skipping.\n", + port); + GNUNET_free (serve); + return GNUNET_NO; + } + } + GNUNET_free (serve); + auditord = GNUNET_OS_start_process (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor-httpd", + "taler-auditor-httpd", + "-c", setup_ctx->config_filename, + NULL); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "auditor", + "BASE_URL", + &base_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "BASE_URL"); + return GNUNET_NO; + } + + if (0 != TALER_TESTING_wait_auditor_ready (base_url)) + { + GNUNET_free (base_url); + GNUNET_break (0 == + GNUNET_OS_process_kill (auditord, + SIGTERM)); + GNUNET_break (GNUNET_OK == + GNUNET_OS_process_wait (auditord)); + GNUNET_OS_process_destroy (auditord); + return 77; + } + GNUNET_free (base_url); + + /* NOTE: this call blocks. */ + result = TALER_TESTING_setup_with_exchange_cfg ((void *) setup_ctx, + cfg); + GNUNET_break (0 == + GNUNET_OS_process_kill (auditord, + SIGTERM)); + GNUNET_break (GNUNET_OK == + GNUNET_OS_process_wait (auditord)); + GNUNET_OS_process_destroy (auditord); + return result; +} + + +/** + * Initialize scheduler loop and curl context for the test case + * including starting and stopping the auditor and exchange using the + * given configuration file. + * + * @param main_cb main method. + * @param main_cb_cls main method closure. + * @param config_filename configuration file name. Is is used + * by both this function and the exchange itself. In the + * first case it gives out the exchange port number and + * the exchange base URL so as to check whether the port + * is available and the exchange responds when requested + * at its base URL. + * @return #GNUNET_OK if no errors occurred. + */ +int +TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb, + void *main_cb_cls, + const char *config_file) +{ + struct TALER_TESTING_SetupContext setup_ctx = { + .config_filename = config_file, + .main_cb = main_cb, + .main_cb_cls = main_cb_cls + }; + + return GNUNET_CONFIGURATION_parse_and_run (config_file, + & + TALER_TESTING_setup_with_auditor_and_exchange_cfg, + &setup_ctx); +} + + +/** + * Test port in URL string for availability. + */ +int +TALER_TESTING_url_port_free (const char *url) +{ + const char *port; + long pnum; + + port = strrchr (url, + (unsigned char) ':'); + if (NULL == port) + pnum = 80; + else + pnum = strtol (port + 1, NULL, 10); + if (GNUNET_OK != + GNUNET_NETWORK_test_port_free (IPPROTO_TCP, + pnum)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Port %u not available.\n", + (unsigned int) pnum); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Allocate and return a piece of wire-details. Combines + * the @a account_no and the @a bank_url to a + * @a payto://-URL and adds some salt to create the JSON. + * + * @param account_no account number + * @param bank_url the bank_url (FIXME/WARNING: shouldn't this be a _hostname_ ??) + * @return JSON describing the account, including the + * payto://-URL of the account, must be manually decref'd + */ +json_t * +TALER_TESTING_make_wire_details (unsigned long long account_no, + const char *bank_url) +{ + char *payto; + json_t *ret; + int ends_slash; + + if (0 < strlen (bank_url)) + ends_slash = '/' == bank_url[strlen (bank_url) - 1]; + else + ends_slash = 0; + + GNUNET_asprintf (&payto, + (ends_slash) + ? "payto://x-taler-bank/%s%llu" + : "payto://x-taler-bank/%s/%llu", + bank_url, + account_no); + ret = json_pack ("{s:s, s:s}", + "url", payto, + "salt", + "test-salt (must be constant for aggregation tests)"); + GNUNET_free (payto); + return ret; +} + + +/** + * Prepare launching a fakebank. Check that the configuration + * file has the right option, and that the port is available. + * If everything is OK, return the configured URL of the fakebank. + * + * @param config_filename configuration file to use + * @param config_section which account to use (must match x-taler-bank) + * @return NULL on error, fakebank URL otherwise + */ +char * +TALER_TESTING_prepare_fakebank (const char *config_filename, + const char *config_section) +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *payto_url; + char *fakebank_url; + const char *start; + const char *end; + + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, + config_filename)) + return NULL; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + config_section, + "URL", + &payto_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + config_section, + "URL"); + GNUNET_CONFIGURATION_destroy (cfg); + return NULL; + } + GNUNET_CONFIGURATION_destroy (cfg); + if (0 != strncasecmp (payto_url, + "payto://x-taler-bank/", + strlen ("payto://x-taler-bank/"))) + { + GNUNET_log_config_invalid + (GNUNET_ERROR_TYPE_WARNING, + config_section, + "URL", + "expected `x-taler-bank' payto://-URL"); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (payto_url); + return NULL; + } + start = &payto_url [strlen ("payto://x-taler-bank/")]; + end = strchr (start, + (unsigned char) '/'); + if (NULL == end) + end = &start[strlen (start)]; + fakebank_url = GNUNET_strndup (start, + end - start); + GNUNET_free (payto_url); + if (GNUNET_OK != + TALER_TESTING_url_port_free (fakebank_url)) + { + GNUNET_free (fakebank_url); + return NULL; + } + return fakebank_url; +} + + +/* end of testing_api_helpers.c */ diff --git a/src/lib/testing_auditor_api_cmd_deposit_confirmation.c b/src/lib/testing_auditor_api_cmd_deposit_confirmation.c deleted file mode 100644 index 3ea6390d8..000000000 --- a/src/lib/testing_auditor_api_cmd_deposit_confirmation.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 - -*/ - -/** - * @file auditor-lib/testing_auditor_api_cmd_deposit_confirmation.c - * @brief command for testing /deposit_confirmation. - * @author Christian Grothoff - */ - -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_auditor_service.h" -#include "taler_testing_lib.h" -#include "taler_signatures.h" -#include "backoff.h" - - -/** - * State for a "deposit confirmation" CMD. - */ -struct DepositConfirmationState -{ - - /** - * Reference to any command that is able to provide a deposit. - */ - const char *deposit_reference; - - /** - * What is the deposited amount without the fee (i.e. the - * amount we expect in the deposit confirmation)? - */ - const char *amount_without_fee; - - /** - * Which coin of the @e deposit_reference should we confirm. - */ - unsigned int coin_index; - - /** - * DepositConfirmation handle while operation is running. - */ - struct TALER_AUDITOR_DepositConfirmationHandle *dc; - - /** - * Auditor connection. - */ - struct TALER_AUDITOR_Handle *auditor; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Expected HTTP response code. - */ - unsigned int expected_response_code; - - /** - * Should we retry on (transient) failures? - */ - int do_retry; - -}; - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -deposit_confirmation_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #deposit_confirmation_run. - * - * @param cls a `struct DepositConfirmationState` - */ -static void -do_retry (void *cls) -{ - struct DepositConfirmationState *dcs = cls; - - dcs->retry_task = NULL; - deposit_confirmation_run (dcs, - NULL, - dcs->is); -} - - -/** - * Callback to analyze the /deposit-confirmation response, just used - * to check if the response code is acceptable. - * - * @param cls closure. - * @param http_status HTTP response code. - * @param ec taler-specific error code. - * @param obj raw response from the auditor. - */ -static void -deposit_confirmation_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct DepositConfirmationState *dcs = cls; - - dcs->dc = NULL; - if (dcs->expected_response_code != http_status) - { - if (GNUNET_YES == dcs->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying deposit confirmation failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - dcs->backoff = GNUNET_TIME_UNIT_ZERO; - else - dcs->backoff = EXCHANGE_LIB_BACKOFF (dcs->backoff); - dcs->retry_task = GNUNET_SCHEDULER_add_delayed (dcs->backoff, - &do_retry, - dcs); - return; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - http_status, - dcs->is->commands[dcs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (obj, stderr, 0); - TALER_TESTING_interpreter_fail (dcs->is); - return; - } - TALER_TESTING_interpreter_next (dcs->is); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -deposit_confirmation_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct DepositConfirmationState *dcs = cls; - const struct TALER_TESTING_Command *deposit_cmd; - struct GNUNET_HashCode h_wire; - struct GNUNET_HashCode h_contract_terms; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_TIME_Absolute refund_deadline; - struct TALER_Amount amount_without_fee; - struct TALER_CoinSpendPublicKeyP coin_pub; - const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv; - struct TALER_MerchantPublicKeyP merchant_pub; - const struct TALER_ExchangePublicKeyP *exchange_pub; - const struct TALER_ExchangeSignatureP *exchange_sig; - const json_t *wire_details; - const json_t *contract_terms; - const struct TALER_CoinSpendPrivateKeyP *coin_priv; - const struct TALER_EXCHANGE_Keys *keys; - const struct TALER_EXCHANGE_SigningPublicKey *spk; - - dcs->is = is; - GNUNET_assert (NULL != dcs->deposit_reference); - deposit_cmd - = TALER_TESTING_interpreter_lookup_command (is, - dcs->deposit_reference); - if (NULL == deposit_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_exchange_pub (deposit_cmd, - dcs->coin_index, - &exchange_pub)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_exchange_sig (deposit_cmd, - dcs->coin_index, - &exchange_sig)); - keys = TALER_EXCHANGE_get_keys (dcs->is->exchange); - GNUNET_assert (NULL != keys); - spk = TALER_EXCHANGE_get_exchange_signing_key_info (keys, - exchange_pub); - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_contract_terms (deposit_cmd, - dcs->coin_index, - &contract_terms)); - /* Very unlikely to fail */ - GNUNET_assert (NULL != contract_terms); - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (contract_terms, - &h_contract_terms)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_wire_details (deposit_cmd, - dcs->coin_index, - &wire_details)); - GNUNET_assert (GNUNET_OK == - TALER_JSON_merchant_wire_signature_hash (wire_details, - &h_wire)); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_coin_priv (deposit_cmd, - dcs->coin_index, - &coin_priv)); - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_peer_key (deposit_cmd, - dcs->coin_index, - &merchant_priv)); - GNUNET_CRYPTO_eddsa_key_get_public (merchant_priv, - &merchant_pub.eddsa_pub); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (dcs->amount_without_fee, - &amount_without_fee)); - /* timestamp is mandatory */ - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_absolute_time ("timestamp", ×tamp), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (contract_terms, - spec, - NULL, NULL)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - } - /* refund deadline is optional, defaults to zero */ - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (contract_terms, - spec, - NULL, NULL)) - { - refund_deadline = timestamp; - } - } - dcs->dc = TALER_AUDITOR_deposit_confirmation - (dcs->auditor, - &h_wire, - &h_contract_terms, - timestamp, - refund_deadline, - &amount_without_fee, - &coin_pub, - &merchant_pub, - exchange_pub, - exchange_sig, - &keys->master_pub, - spk->valid_from, - spk->valid_until, - spk->valid_legal, - &spk->master_sig, - &deposit_confirmation_cb, - dcs); - - if (NULL == dcs->dc) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - return; -} - - -/** - * Free the state of a "deposit_confirmation" CMD, and possibly cancel a - * pending operation thereof. - * - * @param cls closure, a `struct DepositConfirmationState` - * @param cmd the command which is being cleaned up. - */ -static void -deposit_confirmation_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct DepositConfirmationState *dcs = cls; - - if (NULL != dcs->dc) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - dcs->is->ip, - cmd->label); - TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc); - dcs->dc = NULL; - } - if (NULL != dcs->retry_task) - { - GNUNET_SCHEDULER_cancel (dcs->retry_task); - dcs->retry_task = NULL; - } - GNUNET_free (dcs); -} - - -/** - * Offer internal data to other commands. - * - * @param cls closure. - * @param ret[out] set to the wanted data. - * @param trait name of the trait. - * @param index index number of the traits to be returned. - * - * @return #GNUNET_OK on success - */ -static int -deposit_confirmation_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - /* Must define this function because some callbacks - * look for certain traits on _all_ the commands. */ - return GNUNET_SYSERR; -} - - -/** - * Create a "deposit-confirmation" command. - * - * @param label command label. - * @param auditor auditor connection. - * @param deposit_reference reference to any operation that can - * provide a coin. - * @param coin_index if @a deposit_reference offers an array of - * coins, this parameter selects which one in that array. - * This value is currently ignored, as only one-coin - * deposits are implemented. - * @param amount_without_fee deposited amount without the fee - * @param expected_response_code expected HTTP response code. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_deposit_confirmation - (const char *label, - struct TALER_AUDITOR_Handle *auditor, - const char *deposit_reference, - unsigned int coin_index, - const char *amount_without_fee, - unsigned int expected_response_code) -{ - struct TALER_TESTING_Command cmd = {0}; /* need explicit zeroing..*/ - struct DepositConfirmationState *dcs; - - dcs = GNUNET_new (struct DepositConfirmationState); - dcs->auditor = auditor; - dcs->deposit_reference = deposit_reference; - dcs->coin_index = coin_index; - dcs->amount_without_fee = amount_without_fee; - dcs->expected_response_code = expected_response_code; - - cmd.cls = dcs; - cmd.label = label; - cmd.run = &deposit_confirmation_run; - cmd.cleanup = &deposit_confirmation_cleanup; - cmd.traits = &deposit_confirmation_traits; - - return cmd; -} - - -/** - * Modify a deposit confirmation command to enable retries when we get - * transient errors from the auditor. - * - * @param cmd a deposit confirmation command - * @return the command with retries enabled - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_deposit_confirmation_with_retry (struct TALER_TESTING_Command - cmd) -{ - struct DepositConfirmationState *dcs; - - GNUNET_assert (&deposit_confirmation_run == cmd.run); - dcs = cmd.cls; - dcs->do_retry = GNUNET_YES; - return cmd; -} - - -/* end of testing_auditor_api_cmd_deposit_confirmation.c */ diff --git a/src/lib/testing_auditor_api_cmd_exchanges.c b/src/lib/testing_auditor_api_cmd_exchanges.c deleted file mode 100644 index 9cc07553d..000000000 --- a/src/lib/testing_auditor_api_cmd_exchanges.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 - -*/ -/** - * @file auditor-lib/testing_auditor_api_cmd_exchanges.c - * @brief command for testing /exchanges. - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_auditor_service.h" -#include "taler_testing_lib.h" -#include "taler_signatures.h" -#include "backoff.h" - - -/** - * State for a "deposit confirmation" CMD. - */ -struct ExchangesState -{ - - /** - * Exchanges handle while operation is running. - */ - struct TALER_AUDITOR_ListExchangesHandle *leh; - - /** - * Auditor connection. - */ - struct TALER_AUDITOR_Handle *auditor; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Expected HTTP response code. - */ - unsigned int expected_response_code; - - /** - * URL of the exchange expected to be included in the response. - */ - const char *exchange_url; - - /** - * Should we retry on (transient) failures? - */ - int do_retry; - -}; - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -exchanges_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #exchanges_run. - * - * @param cls a `struct ExchangesState` - */ -static void -do_retry (void *cls) -{ - struct ExchangesState *es = cls; - - es->retry_task = NULL; - exchanges_run (es, - NULL, - es->is); -} - - -/** - * Callback to analyze the /exchanges response. - * - * @param cls closure. - * @param http_status HTTP response code. - * @param ec taler-specific error code. - * @param obj raw response from the auditor. - */ -static void -exchanges_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - unsigned int num_exchanges, - const struct TALER_AUDITOR_ExchangeInfo *ei, - const json_t *raw_response) -{ - struct ExchangesState *es = cls; - - es->leh = NULL; - if (es->expected_response_code != http_status) - { - if (GNUNET_YES == es->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying list exchanges failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - es->backoff = GNUNET_TIME_UNIT_ZERO; - else - es->backoff = EXCHANGE_LIB_BACKOFF (es->backoff); - es->retry_task = GNUNET_SCHEDULER_add_delayed (es->backoff, - &do_retry, - es); - return; - } - } - GNUNET_log - (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - http_status, - es->is->commands[es->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (raw_response, stderr, 0); - TALER_TESTING_interpreter_fail (es->is); - return; - } - if (NULL != es->exchange_url) - { - unsigned int found = GNUNET_NO; - - for (unsigned int i = 0; - iexchange_url, - ei[i].exchange_url)) - found = GNUNET_YES; - if (GNUNET_NO == found) - { - TALER_LOG_ERROR - ("Exchange '%s' doesn't exist at this auditor\n", - es->exchange_url); - TALER_TESTING_interpreter_fail (es->is); - return; - } - - TALER_LOG_DEBUG ("Exchange '%s' exists at this auditor!\n", - es->exchange_url); - } - TALER_TESTING_interpreter_next (es->is); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -exchanges_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct ExchangesState *es = cls; - - es->is = is; - es->leh = TALER_AUDITOR_list_exchanges - (is->auditor, - &exchanges_cb, - es); - - if (NULL == es->leh) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - return; -} - - -/** - * Free the state of a "exchanges" CMD, and possibly cancel a - * pending operation thereof. - * - * @param cls closure, a `struct ExchangesState` - * @param cmd the command which is being cleaned up. - */ -static void -exchanges_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct ExchangesState *es = cls; - - if (NULL != es->leh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - es->is->ip, - cmd->label); - TALER_AUDITOR_list_exchanges_cancel (es->leh); - es->leh = NULL; - } - if (NULL != es->retry_task) - { - GNUNET_SCHEDULER_cancel (es->retry_task); - es->retry_task = NULL; - } - GNUNET_free (es); -} - - -/** - * Offer internal data to other commands. - * - * @param cls closure. - * @param ret[out] set to the wanted data. - * @param trait name of the trait. - * @param index index number of the traits to be returned. - * - * @return #GNUNET_OK on success - */ -static int -exchanges_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - /* Must define this function because some callbacks - * look for certain traits on _all_ the commands. */ - return GNUNET_SYSERR; -} - - -/** - * Create a "list exchanges" command. - * - * @param label command label. - * @param auditor auditor connection. - * @param expected_response_code expected HTTP response code. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exchanges - (const char *label, - struct TALER_AUDITOR_Handle *auditor, - unsigned int expected_response_code) -{ - struct TALER_TESTING_Command cmd = {0}; /* need explicit zeroing..*/ - struct ExchangesState *es; - - es = GNUNET_new (struct ExchangesState); - es->auditor = auditor; - es->expected_response_code = expected_response_code; - - cmd.cls = es; - cmd.label = label; - cmd.run = &exchanges_run; - cmd.cleanup = &exchanges_cleanup; - cmd.traits = &exchanges_traits; - - return cmd; -} - - -/** - * Create a "list exchanges" command and check whether - * a particular exchange belongs to the returned bundle. - * - * @param label command label. - * @param auditor auditor connection. - * @param expected_response_code expected HTTP response code. - * @param exchange_url URL of the exchange supposed to - * be included in the response. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exchanges_with_url - (const char *label, - unsigned int expected_response_code, - const char *exchange_url) -{ - struct TALER_TESTING_Command cmd = {0}; /* need explicit zeroing..*/ - struct ExchangesState *es; - - es = GNUNET_new (struct ExchangesState); - es->expected_response_code = expected_response_code; - es->exchange_url = exchange_url; - - cmd.cls = es; - cmd.label = label; - cmd.run = &exchanges_run; - cmd.cleanup = &exchanges_cleanup; - cmd.traits = &exchanges_traits; - - return cmd; -} - - -/** - * Modify an exchanges command to enable retries when we get - * transient errors from the auditor. - * - * @param cmd a deposit confirmation command - * @return the command with retries enabled - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exchanges_with_retry (struct TALER_TESTING_Command cmd) -{ - struct ExchangesState *es; - - GNUNET_assert (&exchanges_run == cmd.run); - es = cmd.cls; - es->do_retry = GNUNET_YES; - return cmd; -} - - -/* end of testing_auditor_api_cmd_exchanges.c */ diff --git a/src/lib/testing_auditor_api_cmd_exec_auditor.c b/src/lib/testing_auditor_api_cmd_exec_auditor.c deleted file mode 100644 index cff234691..000000000 --- a/src/lib/testing_auditor_api_cmd_exec_auditor.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 -*/ - -/** - * @file auditor-lib/testing_auditor_api_cmd_exec_auditor.c - * @brief run the taler-auditor command - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "auditor_api_handle.h" -#include "taler_signatures.h" -#include "taler_testing_lib.h" - - -/** - * State for a "auditor" CMD. - */ -struct AuditorState -{ - - /** - * Process for the "auditor" command. - */ - struct GNUNET_OS_Process *auditor_proc; - - /** - * Configuration file used by the command. - */ - const char *config_filename; -}; - - -/** - * Run the command; calls the `taler-auditor' program. - * - * @param cls closure. - * @param cmd the commaind being run. - * @param is interpreter state. - */ -static void -auditor_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct AuditorState *ks = cls; - - ks->auditor_proc - = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-auditor", - "taler-auditor", - "-c", ks->config_filename, - NULL); - if (NULL == ks->auditor_proc) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - TALER_TESTING_wait_for_sigchld (is); -} - - -/** - * Free the state of a "auditor" CMD, and possibly kills its - * process if it did not terminate correctly. - * - * @param cls closure. - * @param cmd the command being freed. - */ -static void -auditor_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct AuditorState *ks = cls; - - if (NULL != ks->auditor_proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (ks->auditor_proc, - SIGKILL)); - GNUNET_OS_process_wait (ks->auditor_proc); - GNUNET_OS_process_destroy (ks->auditor_proc); - ks->auditor_proc = NULL; - } - GNUNET_free (ks); -} - - -/** - * Offer "auditor" CMD internal data to other commands. - * - * @param cls closure. - * @param ret[out] result - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -auditor_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct AuditorState *ks = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &ks->auditor_proc), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Make the "exec-auditor" CMD. - * - * @param label command label. - * @param config_filename configuration filename. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exec_auditor (const char *label, - const char *config_filename) -{ - struct TALER_TESTING_Command cmd; - struct AuditorState *ks; - - ks = GNUNET_new (struct AuditorState); - ks->config_filename = config_filename; - cmd.cls = ks; - cmd.label = label; - cmd.run = &auditor_run; - cmd.cleanup = &auditor_cleanup; - cmd.traits = &auditor_traits; - return cmd; -} - - -/* end of testing_auditor_api_cmd_exec_auditor.c */ diff --git a/src/lib/testing_auditor_api_cmd_exec_auditor_dbinit.c b/src/lib/testing_auditor_api_cmd_exec_auditor_dbinit.c deleted file mode 100644 index 3b7e3aa0b..000000000 --- a/src/lib/testing_auditor_api_cmd_exec_auditor_dbinit.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 -*/ - -/** - * @file auditor-lib/testing_auditor_api_cmd_exec_auditor_dbinit.c - * @brief run the taler-auditor-dbinit "-r" command - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "auditor_api_handle.h" -#include "taler_signatures.h" -#include "taler_testing_lib.h" - - -/** - * State for a "auditor-dbinit" CMD. - */ -struct AuditorDbinitState -{ - - /** - * Process for the "auditor-dbinit" command. - */ - struct GNUNET_OS_Process *auditor_dbinit_proc; - - /** - * Configuration file used by the command. - */ - const char *config_filename; -}; - - -/** - * Run the command; calls the `taler-auditor-dbinit' program. - * - * @param cls closure. - * @param cmd the commaind being run. - * @param is interpreter state. - */ -static void -auditor_dbinit_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct AuditorDbinitState *ks = cls; - - ks->auditor_dbinit_proc - = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-auditor-dbinit", - "taler-auditor-dbinit", - "-c", ks->config_filename, - "-r", - NULL); - if (NULL == ks->auditor_dbinit_proc) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - TALER_TESTING_wait_for_sigchld (is); -} - - -/** - * Free the state of a "auditor-dbinit" CMD, and possibly kills its - * process if it did not terminate correctly. - * - * @param cls closure. - * @param cmd the command being freed. - */ -static void -auditor_dbinit_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct AuditorDbinitState *ks = cls; - - if (NULL != ks->auditor_dbinit_proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (ks->auditor_dbinit_proc, - SIGKILL)); - GNUNET_OS_process_wait (ks->auditor_dbinit_proc); - GNUNET_OS_process_destroy (ks->auditor_dbinit_proc); - ks->auditor_dbinit_proc = NULL; - } - GNUNET_free (ks); -} - - -/** - * Offer "auditor-dbinit" CMD internal data to other commands. - * - * @param cls closure. - * @param ret[out] result - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -auditor_dbinit_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct AuditorDbinitState *ks = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &ks->auditor_dbinit_proc), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Make the "exec-auditor-dbinit" CMD. - * - * @param label command label. - * @param config_filename configuration filename. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exec_auditor_dbinit (const char *label, - const char *config_filename) -{ - struct TALER_TESTING_Command cmd; - struct AuditorDbinitState *ks; - - ks = GNUNET_new (struct AuditorDbinitState); - ks->config_filename = config_filename; - cmd.cls = ks; - cmd.label = label; - cmd.run = &auditor_dbinit_run; - cmd.cleanup = &auditor_dbinit_cleanup; - cmd.traits = &auditor_dbinit_traits; - return cmd; -} - - -/* end of testing_auditor_api_cmd_exec_auditor_dbinit.c */ diff --git a/src/lib/testing_auditor_api_cmd_exec_wire_auditor.c b/src/lib/testing_auditor_api_cmd_exec_wire_auditor.c deleted file mode 100644 index 744420d3f..000000000 --- a/src/lib/testing_auditor_api_cmd_exec_wire_auditor.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 -*/ - -/** - * @file auditor-lib/testing_auditor_api_cmd_exec_wire-auditor.c - * @brief run the taler-wire-auditor command - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "auditor_api_handle.h" -#include "taler_signatures.h" -#include "taler_testing_lib.h" - - -/** - * State for a "wire-auditor" CMD. - */ -struct WireAuditorState -{ - - /** - * Process for the "wire-auditor" command. - */ - struct GNUNET_OS_Process *wire_auditor_proc; - - /** - * Configuration file used by the command. - */ - const char *config_filename; -}; - - -/** - * Run the command; calls the `taler-wire-auditor' program. - * - * @param cls closure. - * @param cmd the commaind being run. - * @param is interpreter state. - */ -static void -wire_auditor_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct WireAuditorState *ks = cls; - - ks->wire_auditor_proc - = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-wire-auditor", - "taler-wire-auditor", - "-c", ks->config_filename, - NULL); - if (NULL == ks->wire_auditor_proc) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - TALER_TESTING_wait_for_sigchld (is); -} - - -/** - * Free the state of a "wire-auditor" CMD, and possibly kills its - * process if it did not terminate correctly. - * - * @param cls closure. - * @param cmd the command being freed. - */ -static void -wire_auditor_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct WireAuditorState *ks = cls; - - if (NULL != ks->wire_auditor_proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (ks->wire_auditor_proc, - SIGKILL)); - GNUNET_OS_process_wait (ks->wire_auditor_proc); - GNUNET_OS_process_destroy (ks->wire_auditor_proc); - ks->wire_auditor_proc = NULL; - } - GNUNET_free (ks); -} - - -/** - * Offer "wire-auditor" CMD internal data to other commands. - * - * @param cls closure. - * @param ret[out] result - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -wire_auditor_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct WireAuditorState *ks = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &ks->wire_auditor_proc), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Make the "exec wire-auditor" CMD. - * - * @param label command label. - * @param config_filename configuration filename. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_exec_wire_auditor (const char *label, - const char *config_filename) -{ - struct TALER_TESTING_Command cmd; - struct WireAuditorState *ks; - - ks = GNUNET_new (struct WireAuditorState); - ks->config_filename = config_filename; - cmd.cls = ks; - cmd.label = label; - cmd.run = &wire_auditor_run; - cmd.cleanup = &wire_auditor_cleanup; - cmd.traits = &wire_auditor_traits; - return cmd; -} - - -/* end of testing_auditor_api_cmd_exec_wire_auditor.c */ diff --git a/src/lib/testing_auditor_api_helpers.c b/src/lib/testing_auditor_api_helpers.c deleted file mode 100644 index cf15131da..000000000 --- a/src/lib/testing_auditor_api_helpers.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 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 - -*/ - -/** - * @file auditor-lib/testing_auditor_api_helpers.c - * @brief helper functions - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include -#include "taler_testing_lib.h" -#include "taler_testing_auditor_lib.h" -#include "taler_auditor_service.h" - - -/** - * Closure for #cleanup_auditor. - */ -struct CleanupContext -{ - /** - * Where we find the state to clean up. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Next cleanup routine to call, NULL for none. - */ - GNUNET_SCHEDULER_TaskCallback fcb; - - /** - * Closure for @e fcb - */ - void *fcb_cls; -}; - - -/** - * Function to clean up the auditor connection. - * - * @param cls a `struct CleanupContext` - */ -static void -cleanup_auditor (void *cls) -{ - struct CleanupContext *cc = cls; - struct TALER_TESTING_Interpreter *is = cc->is; - - TALER_AUDITOR_disconnect (is->auditor); - is->auditor = NULL; - if (NULL != cc->fcb) - cc->fcb (cc->fcb_cls); - GNUNET_free (cc); -} - - -/** - * Closure for #auditor_main_wrapper() - */ -struct MainWrapperContext -{ - /** - * Main function to launch. - */ - TALER_TESTING_Main main_cb; - - /** - * Closure for @e main_cb. - */ - void *main_cb_cls; - - /** - * Configuration we use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Name of the configuration file. - */ - const char *config_filename; - -}; - - -/** - * Function called with information about the auditor. - * - * @param cls closure - * @param vi basic information about the auditor - * @param compat protocol compatibility information - */ -static void -auditor_version_cb - (void *cls, - const struct TALER_AUDITOR_VersionInformation *vi, - enum TALER_AUDITOR_VersionCompatibility compat) -{ - struct TALER_TESTING_Interpreter *is = cls; - - if (TALER_AUDITOR_VC_MATCH != compat) - { - TALER_TESTING_interpreter_fail (is); - return; - } - - is->auditor_working = GNUNET_YES; -} - - -/** - * Setup the @a is 'auditor' member before running the main test loop. - * - * @param cls must be a `struct MainWrapperContext *` - * @param is[in,out] interpreter state to setup - */ -static void -auditor_main_wrapper (void *cls, - struct TALER_TESTING_Interpreter *is) -{ - struct MainWrapperContext *mwc = cls; - struct CleanupContext *cc; - char *auditor_base_url; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (mwc->cfg, - "auditor", - "BASE_URL", - &auditor_base_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "BASE_URL"); - return; - } - - is->auditor = TALER_AUDITOR_connect (is->ctx, - auditor_base_url, - &auditor_version_cb, - is); - GNUNET_free (auditor_base_url); - - if (NULL == is->auditor) - { - GNUNET_break (0); - return; - } - - cc = GNUNET_new (struct CleanupContext); - cc->is = is; - cc->fcb = is->final_cleanup_cb; - cc->fcb_cls = is->final_cleanup_cb; - is->final_cleanup_cb = cleanup_auditor; - is->final_cleanup_cb_cls = cc; - mwc->main_cb (mwc->main_cb_cls, - is); -} - - -/** - * Install signal handlers plus schedules the main wrapper - * around the "run" method. - * - * @param cls our `struct MainWrapperContext` - * @param cfg configuration we use - * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. - * non-GNUNET_OK codes are #GNUNET_SYSERR most of the - * times. - */ -static int -setup_with_cfg (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct MainWrapperContext *mwc = cls; - struct TALER_TESTING_SetupContext setup_ctx = { - .config_filename = mwc->config_filename, - .main_cb = &auditor_main_wrapper, - .main_cb_cls = mwc - }; - - mwc->cfg = cfg; - return TALER_TESTING_setup_with_auditor_and_exchange_cfg (&setup_ctx, - cfg); -} - - -/** - * Install signal handlers plus schedules the main wrapper - * around the "run" method. - * - * @param main_cb the "run" method which contains all the - * commands. - * @param main_cb_cls a closure for "run", typically NULL. - * @param config_filename configuration filename. - * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. - * non-GNUNET_OK codes are #GNUNET_SYSERR most of the - * times. - */ -int -TALER_TESTING_AUDITOR_setup (TALER_TESTING_Main main_cb, - void *main_cb_cls, - const char *config_filename) -{ - struct MainWrapperContext mwc = { - .main_cb = main_cb, - .main_cb_cls = main_cb_cls, - .config_filename = config_filename - }; - - return GNUNET_CONFIGURATION_parse_and_run (config_filename, - &setup_with_cfg, - &mwc); -} - - -/* end of testing_auditor_api_helpers.c */ -- cgit v1.2.3