diff options
Diffstat (limited to 'src/testing')
174 files changed, 29046 insertions, 11440 deletions
diff --git a/src/testing/.gitignore b/src/testing/.gitignore index 6f3923501..e1075ab16 100644 --- a/src/testing/.gitignore +++ b/src/testing/.gitignore @@ -1,13 +1,20 @@ -test_auditor_api_version +test_auditor_api_version_cs +test_auditor_api_version_rsa test_bank_api_with_fakebank test_bank_api_with_fakebank_twisted test_bank_api_with_pybank test_bank_api_with_pybank_twisted test_taler_exchange_aggregator-postgres test_taler_exchange_wirewatch-postgres -test_exchange_api_revocation +test_exchange_api_revocation_cs +test_exchange_api_revocation_rsa +test_exchange_api_age_restriction_cs +test_exchange_api_age_restriction_rsa +test_exchange_api_conflicts_cs +test_exchange_api_conflicts_rsa report* -test_exchange_management_api +test_exchange_management_api_cs +test_exchange_management_api_rsa test_exchange_api_home/.local/share/taler/crypto-eddsa/ test_exchange_api_home/.local/share/taler/crypto-rsa/ test_exchange_api_home/.local/share/taler/exchange/offline-keys/secm_tofus.priv @@ -24,11 +31,32 @@ test_taler_exchange_httpd_home/.local/share/taler/taler-exchange-secmod-eddsa/ test_taler_exchange_httpd_home/.local/share/taler/taler-exchange-secmod-rsa/ test_exchange_api_keys_cherry_picking_home/.local/share/taler/crypto-rsa/ test_exchange_api_home/.local/share/taler/exchange-offline/secm_tofus.pub +test_exchange_api_home/.local/share/taler/exchange-secmod-cs/ test_exchange_api_home/.local/share/taler/exchange-secmod-eddsa/ test_exchange_api_home/.local/share/taler/exchange-secmod-rsa/ test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-offline/secm_tofus.pub +test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-secmod-cs/ test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-secmod-eddsa/ test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-secmod-rsa/ test_taler_exchange_httpd_home/.local/share/taler/exchange-offline/secm_tofus.pub +test_taler_exchange_httpd_home/.local/share/taler/exchange-secmod-cs/ test_taler_exchange_httpd_home/.local/share/taler/exchange-secmod-eddsa/ test_taler_exchange_httpd_home/.local/share/taler/exchange-secmod-rsa/ +test_kyc_api +test_helper_cs_home/ +test_helper_rsa_home/test_exchange_api_twisted-cs +test_exchange_api_twisted-rsa +test_exchange_api_twisted_cs +test_exchange_api_twisted_rsa +test_exchange_p2p_cs +test_exchange_p2p_rsa +*.edited +tmp-last-response.* +test_exchange_api_home/taler/auditor/ +test_exchange_api_home/taler/exchange-offline/secm_tofus.pub +test_exchange_api_home/taler/exchange-secmod-cs/ +test_exchange_api_home/taler/exchange-secmod-eddsa/ +test_exchange_api_home/taler/exchange-secmod-rsa/ +test_exchange_api_keys_cherry_picking_home/taler/ +test_taler_exchange_httpd_home/taler/ +libeufin-bank.pid diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 7fa7ef3d2..195ab4c55 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -13,6 +13,9 @@ endif clean-local: rm -rf report* +bin_SCRIPTS = \ + taler-unified-setup.sh + # Libraries lib_LTLIBRARIES = \ @@ -37,11 +40,11 @@ libtalertesting_la_LDFLAGS = \ -version-info 0:0:0 \ -no-undefined libtalertesting_la_SOURCES = \ + testing_api_cmd_age_withdraw.c \ testing_api_cmd_auditor_add_denom_sig.c \ testing_api_cmd_auditor_add.c \ testing_api_cmd_auditor_del.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_bank_admin_add_incoming.c \ @@ -52,73 +55,86 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_bank_history_debit.c \ testing_api_cmd_bank_transfer.c \ testing_api_cmd_batch.c \ - testing_api_cmd_change_auth.c \ - testing_api_cmd_check_keys.c \ + testing_api_cmd_batch_deposit.c \ + testing_api_cmd_batch_withdraw.c \ + testing_api_cmd_check_aml_decision.c \ + testing_api_cmd_check_aml_decisions.c \ + testing_api_cmd_coin_history.c \ + testing_api_cmd_common.c \ + testing_api_cmd_contract_get.c \ testing_api_cmd_deposit.c \ testing_api_cmd_deposits_get.c \ testing_api_cmd_exec_aggregator.c \ + testing_api_cmd_exec_auditor-offline.c \ testing_api_cmd_exec_closer.c \ + testing_api_cmd_exec_expire.c \ + testing_api_cmd_exec_router.c \ testing_api_cmd_exec_transfer.c \ + testing_api_cmd_exec_wget.c \ testing_api_cmd_exec_wirewatch.c \ + testing_api_cmd_get_auditor.c \ + testing_api_cmd_get_exchange.c \ testing_api_cmd_insert_deposit.c \ - testing_api_cmd_offline_sign_fees.c \ + testing_api_cmd_kyc_check_get.c \ + testing_api_cmd_kyc_proof.c \ + testing_api_cmd_kyc_wallet_get.c \ + testing_api_cmd_oauth.c \ + testing_api_cmd_offline_sign_global_fees.c \ + testing_api_cmd_offline_sign_wire_fees.c \ testing_api_cmd_offline_sign_keys.c \ - testing_api_cmd_set_wire_fee.c \ + testing_api_cmd_offline_sign_extensions.c \ + testing_api_cmd_purse_create_deposit.c \ + testing_api_cmd_purse_delete.c \ + testing_api_cmd_purse_deposit.c \ + testing_api_cmd_purse_get.c \ + testing_api_cmd_purse_merge.c \ testing_api_cmd_recoup.c \ + testing_api_cmd_recoup_refresh.c \ testing_api_cmd_refund.c \ testing_api_cmd_refresh.c \ + testing_api_cmd_reserve_attest.c \ + testing_api_cmd_reserve_close.c \ + testing_api_cmd_reserve_get.c \ + testing_api_cmd_reserve_get_attestable.c \ + testing_api_cmd_reserve_history.c \ + testing_api_cmd_reserve_open.c \ + testing_api_cmd_reserve_purse.c \ testing_api_cmd_revoke.c \ testing_api_cmd_revoke_denom_key.c \ testing_api_cmd_revoke_sign_key.c \ - testing_api_cmd_rewind.c \ - testing_api_cmd_serialize_keys.c \ + testing_api_cmd_run_fakebank.c \ + testing_api_cmd_set_officer.c \ + testing_api_cmd_set_wire_fee.c \ testing_api_cmd_signal.c \ testing_api_cmd_sleep.c \ testing_api_cmd_stat.c \ - testing_api_cmd_status.c \ + testing_api_cmd_system_start.c \ + testing_api_cmd_take_aml_decision.c \ testing_api_cmd_transfer_get.c \ testing_api_cmd_wait.c \ - testing_api_cmd_wire.c \ testing_api_cmd_wire_add.c \ testing_api_cmd_wire_del.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_contract.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_merchant_key.c \ - testing_api_trait_number.c \ - testing_api_trait_process.c \ - testing_api_trait_reserve_history.c \ - testing_api_trait_reserve_pub.c \ - testing_api_trait_reserve_priv.c \ - testing_api_trait_string.c \ - testing_api_trait_time.c \ - testing_api_trait_uuid.c \ - testing_api_trait_wtid.c + testing_api_misc.c \ + testing_api_traits.c + + libtalertesting_la_LIBADD = \ $(top_builddir)/src/lib/libtalerauditor.la \ $(top_builddir)/src/lib/libtalerexchange.la \ + $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/mhd/libtalermhd.la \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/bank-lib/libtalerfakebank.la \ -lgnunetcurl \ -lgnunetjson \ -lgnunetutil \ -ljansson \ + -lmicrohttpd \ $(XLIB) @@ -126,35 +142,64 @@ libtalertesting_la_LIBADD = \ AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; -.NOTPARALLEL: + check_PROGRAMS = \ - test_auditor_api \ test_auditor_api_version \ test_bank_api_with_fakebank \ - test_bank_api_with_pybank \ test_bank_api_with_nexus \ - test_exchange_api \ - test_exchange_api_keys_cherry_picking \ - test_exchange_api_revocation \ - test_exchange_api_overlapping_keys_bug \ - test_exchange_management_api \ + test_exchange_api_cs \ + test_exchange_api_rsa \ + test_exchange_api_age_restriction_cs \ + test_exchange_api_age_restriction_rsa \ + test_exchange_api_conflicts_cs \ + test_exchange_api_conflicts_rsa \ + test_exchange_api_keys_cherry_picking_cs \ + test_exchange_api_keys_cherry_picking_rsa \ + test_exchange_api_revocation_cs \ + test_exchange_api_revocation_rsa \ + test_exchange_api_overlapping_keys_bug_cs \ + test_exchange_api_overlapping_keys_bug_rsa \ + test_exchange_management_api_cs \ + test_exchange_management_api_rsa \ + test_kyc_api \ test_taler_exchange_aggregator-postgres \ - test_taler_exchange_wirewatch-postgres + test_taler_exchange_wirewatch-postgres \ + test_exchange_p2p_cs \ + test_exchange_p2p_rsa if HAVE_TWISTER check_PROGRAMS += \ - test_exchange_api_twisted \ - test_bank_api_with_fakebank_twisted \ - test_bank_api_with_pybank_twisted + test_exchange_api_twisted_cs \ + test_exchange_api_twisted_rsa \ + test_bank_api_with_fakebank_twisted endif +# Removed for now... +# test_auditor_api_cs +# test_auditor_api_rsa TESTS = \ $(check_PROGRAMS) -test_auditor_api_SOURCES = \ +test_auditor_api_cs_SOURCES = \ + test_auditor_api.c +test_auditor_api_cs_LDADD = \ + $(top_builddir)/src/lib/libtalerauditor.la \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_auditor_api_rsa_SOURCES = \ test_auditor_api.c -test_auditor_api_LDADD = \ +test_auditor_api_rsa_LDADD = \ $(top_builddir)/src/lib/libtalerauditor.la \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ @@ -168,6 +213,7 @@ test_auditor_api_LDADD = \ -ljansson \ $(XLIB) + test_auditor_api_version_SOURCES = \ test_auditor_api_version.c test_auditor_api_version_LDADD = \ @@ -184,7 +230,7 @@ test_bank_api_with_nexus_SOURCES = \ test_bank_api.c test_bank_api_with_nexus_LDADD = \ libtalertesting.la \ - -ltalerexchange \ + $(top_builddir)/src/lib/libtalerexchange.la \ -lgnunetutil \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(XLIB) @@ -193,23 +239,30 @@ test_bank_api_with_fakebank_SOURCES = \ test_bank_api.c test_bank_api_with_fakebank_LDADD = \ libtalertesting.la \ - -ltalerexchange \ + $(top_builddir)/src/lib/libtalerexchange.la \ -lgnunetutil \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(XLIB) -test_bank_api_with_pybank_SOURCES = \ - test_bank_api.c -test_bank_api_with_pybank_LDADD = \ +test_exchange_api_cs_SOURCES = \ + test_exchange_api.c +test_exchange_api_cs_LDADD = \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ - -lgnunetutil \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ $(XLIB) -test_exchange_api_SOURCES = \ +test_exchange_api_rsa_SOURCES = \ test_exchange_api.c -test_exchange_api_LDADD = \ +test_exchange_api_rsa_LDADD = \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ $(LIBGCRYPT_LIBS) \ @@ -217,23 +270,31 @@ test_exchange_api_LDADD = \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ -lgnunetcurl \ -lgnunetutil \ -ljansson \ $(XLIB) -test_exchange_management_api_SOURCES = \ - test_exchange_management_api.c -test_exchange_management_api_LDADD = \ +test_exchange_api_age_restriction_cs_SOURCES = \ + test_exchange_api_age_restriction.c +test_exchange_api_age_restriction_cs_LDADD = \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ + -lgnunetcurl \ -lgnunetutil \ + -ljansson \ $(XLIB) -test_exchange_api_revocation_SOURCES = \ - test_exchange_api_revocation.c -test_exchange_api_revocation_LDADD = \ +test_exchange_api_age_restriction_rsa_SOURCES = \ + test_exchange_api_age_restriction.c +test_exchange_api_age_restriction_rsa_LDADD = \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ $(LIBGCRYPT_LIBS) \ @@ -241,14 +302,80 @@ test_exchange_api_revocation_LDADD = \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ -lgnunetcurl \ -lgnunetutil \ -ljansson \ $(XLIB) -test_exchange_api_keys_cherry_picking_SOURCES = \ +test_exchange_api_conflicts_cs_SOURCES = \ + test_exchange_api_conflicts.c +test_exchange_api_conflicts_cs_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_exchange_api_conflicts_rsa_SOURCES = \ + test_exchange_api_conflicts.c +test_exchange_api_conflicts_rsa_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_exchange_p2p_cs_SOURCES = \ + test_exchange_p2p.c +test_exchange_p2p_cs_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_exchange_p2p_rsa_SOURCES = \ + test_exchange_p2p.c +test_exchange_p2p_rsa_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/extensions/libtalerextensions.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + + +test_exchange_api_keys_cherry_picking_cs_SOURCES = \ test_exchange_api_keys_cherry_picking.c -test_exchange_api_keys_cherry_picking_LDADD = \ +test_exchange_api_keys_cherry_picking_cs_LDADD = \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ $(LIBGCRYPT_LIBS) \ @@ -260,9 +387,68 @@ test_exchange_api_keys_cherry_picking_LDADD = \ -ljansson \ $(XLIB) -test_exchange_api_overlapping_keys_bug_SOURCES = \ +test_exchange_api_keys_cherry_picking_rsa_SOURCES = \ + test_exchange_api_keys_cherry_picking.c +test_exchange_api_keys_cherry_picking_rsa_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_exchange_api_revocation_cs_SOURCES = \ + test_exchange_api_revocation.c +test_exchange_api_revocation_cs_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_exchange_api_revocation_rsa_SOURCES = \ + test_exchange_api_revocation.c +test_exchange_api_revocation_rsa_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + + +test_exchange_api_overlapping_keys_bug_cs_SOURCES = \ + test_exchange_api_overlapping_keys_bug.c +test_exchange_api_overlapping_keys_bug_cs_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +test_exchange_api_overlapping_keys_bug_rsa_SOURCES = \ test_exchange_api_overlapping_keys_bug.c -test_exchange_api_overlapping_keys_bug_LDADD = \ +test_exchange_api_overlapping_keys_bug_rsa_LDADD = \ libtalertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ $(LIBGCRYPT_LIBS) \ @@ -274,6 +460,25 @@ test_exchange_api_overlapping_keys_bug_LDADD = \ -ljansson \ $(XLIB) +test_exchange_management_api_cs_SOURCES = \ + test_exchange_management_api.c +test_exchange_management_api_cs_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil \ + $(XLIB) + +test_exchange_management_api_rsa_SOURCES = \ + test_exchange_management_api.c +test_exchange_management_api_rsa_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil \ + $(XLIB) + + test_taler_exchange_aggregator_postgres_SOURCES = \ test_taler_exchange_aggregator.c test_taler_exchange_aggregator_postgres_LDADD = \ @@ -307,9 +512,9 @@ test_taler_exchange_wirewatch_postgres_LDADD = \ -lpthread \ $(XLIB) -test_exchange_api_twisted_SOURCES = \ +test_exchange_api_twisted_cs_SOURCES = \ test_exchange_api_twisted.c -test_exchange_api_twisted_LDADD = \ +test_exchange_api_twisted_cs_LDADD = \ $(LIBGCRYPT_LIBS) \ libtalertesting.la \ libtalertwistertesting.la \ @@ -324,24 +529,26 @@ test_exchange_api_twisted_LDADD = \ -ljansson \ $(XLIB) -test_bank_api_with_fakebank_twisted_SOURCES = \ - test_bank_api_twisted.c -test_bank_api_with_fakebank_twisted_LDADD = \ +test_exchange_api_twisted_rsa_SOURCES = \ + test_exchange_api_twisted.c +test_exchange_api_twisted_rsa_LDADD = \ + $(LIBGCRYPT_LIBS) \ libtalertesting.la \ - $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + libtalertwistertesting.la \ $(top_builddir)/src/lib/libtalerexchange.la \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/json/libtalerjson.la \ - libtalertwistertesting.la \ + $(top_builddir)/src/util/libtalerutil.la \ -lgnunetjson \ -lgnunetcurl \ -lgnunetutil \ -ljansson \ $(XLIB) -test_bank_api_with_pybank_twisted_SOURCES = \ +test_bank_api_with_fakebank_twisted_SOURCES = \ test_bank_api_twisted.c -test_bank_api_with_pybank_twisted_LDADD = \ +test_bank_api_with_fakebank_twisted_LDADD = \ libtalertesting.la \ $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/bank-lib/libtalerfakebank.la \ @@ -351,34 +558,60 @@ test_bank_api_with_pybank_twisted_LDADD = \ -lgnunetjson \ -lgnunetcurl \ -lgnunetutil \ - -ljansson \ + -ljansson \ $(XLIB) +test_kyc_api_SOURCES = \ + test_kyc_api.c +test_kyc_api_LDADD = \ + libtalertesting.la \ + $(top_builddir)/src/lib/libtalerauditor.la \ + $(top_builddir)/src/lib/libtalerexchange.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerfakebank.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetcurl \ + -lgnunetutil \ + -ljansson \ + $(XLIB) # Distribution EXTRA_DIST = \ - test_auditor_api.conf \ - test_auditor_api_expire_reserve_now.conf \ + $(bin_SCRIPTS) \ + valgrind.h \ + coins-cs.conf \ + coins-rsa.conf \ + test_auditor_api-cs.conf \ + test_auditor_api-rsa.conf \ + test_auditor_api_expire_reserve_now-cs.conf \ + test_auditor_api_expire_reserve_now-rsa.conf \ + test_bank_api.conf \ test_bank_api_fakebank.conf \ test_bank_api_fakebank_twisted.conf \ - test_bank_api_pybank.conf \ - test_bank_api_pybank_twisted.conf \ - test_exchange_api_home/.config/taler/account-2.json \ + test_bank_api_nexus.conf \ + test_exchange_api_home/taler/auditor/offline-keys/auditor.priv \ test_exchange_api_home/.local/share/taler/exchange-offline/master.priv \ - test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv \ - test_exchange_api_home/.local/share/taler/exchange/wirefees/x-taler-bank.fee \ - test_exchange_api_keys_cherry_picking_home/.config/taler/x-taler-bank.json \ - test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-offline/master.priv \ - test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange/offline-keys/master.priv \ - test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange/wirefees/x-taler-bank.fee \ + test_exchange_api_home/.local/share/taler/auditor/offline-keys/auditor.priv \ test_exchange_api.conf \ - test_exchange_api_twisted.conf \ + test_exchange_api-cs.conf \ + test_exchange_api-rsa.conf \ + test_exchange_api_age_restriction.conf \ + test_exchange_api_age_restriction-cs.conf \ + test_exchange_api_age_restriction-rsa.conf \ + test_exchange_api_conflicts.conf \ + test_exchange_api_conflicts-cs.conf \ + test_exchange_api_conflicts-rsa.conf \ + test_exchange_api-twisted.conf \ + test_exchange_api_twisted-cs.conf \ + test_exchange_api_twisted-rsa.conf \ test_exchange_api_keys_cherry_picking.conf \ - test_exchange_api_expire_reserve_now.conf \ - test_taler_exchange_httpd_home/.config/taler/account-1.json \ - test_taler_exchange_httpd_home/.local/share/taler/exchange-offline/master.priv \ - test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv \ - test_taler_exchange_httpd_home/.local/share/taler/exchange/wirefees/x-taler-bank.fee \ + test_exchange_api_keys_cherry_picking-cs.conf \ + test_exchange_api_keys_cherry_picking-rsa.conf \ + test_exchange_api_expire_reserve_now-cs.conf \ + test_exchange_api_expire_reserve_now-rsa.conf \ test-taler-exchange-aggregator-postgres.conf \ - test-taler-exchange-wirewatch-postgres.conf + test-taler-exchange-wirewatch-postgres.conf \ + test_kyc_api.conf diff --git a/src/testing/coins-cs.conf b/src/testing/coins-cs.conf new file mode 100644 index 000000000..92163baac --- /dev/null +++ b/src/testing/coins-cs.conf @@ -0,0 +1,118 @@ + +# Sections starting with "coin_" specify which denominations +# the exchange should support (and their respective fee structure) +[coin_eur_ct_1] +value = EUR:0.01 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.00 +fee_deposit = EUR:0.00 +fee_refresh = EUR:0.01 +fee_refund = EUR:0.01 +CIPHER = CS + +[coin_eur_ct_10] +value = EUR:0.10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = CS + +[coin_eur_1] +value = EUR:1 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = CS + +[coin_eur_5] +value = EUR:5 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = CS + +[coin_eur_10] +value = EUR:10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = CS + + +[coin_eur_ct_1_age_restricted] +value = EUR:0.01 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.00 +fee_deposit = EUR:0.00 +fee_refresh = EUR:0.01 +fee_refund = EUR:0.01 +age_restricted = YES +CIPHER = CS + +[coin_eur_ct_10_age_restricted] +value = EUR:0.10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +age_restricted = YES +CIPHER = CS + +[coin_eur_1_age_restricted] +value = EUR:1 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +age_restricted = YES +CIPHER = CS + +[coin_eur_5_age_restricted] +value = EUR:5 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +age_restricted = YES +CIPHER = CS + +[coin_eur_10_age_restricted] +value = EUR:10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +age_restricted = YES +CIPHER = CS diff --git a/src/testing/coins-rsa.conf b/src/testing/coins-rsa.conf new file mode 100644 index 000000000..7a21a343b --- /dev/null +++ b/src/testing/coins-rsa.conf @@ -0,0 +1,128 @@ +# This file is in the public domain. + +# Sections starting with "coin_" specify which denominations +# the exchange should support (and their respective fee structure) +[coin_eur_ct_1] +value = EUR:0.01 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.00 +fee_deposit = EUR:0.00 +fee_refresh = EUR:0.01 +fee_refund = EUR:0.01 +CIPHER = RSA +rsa_keysize = 1024 + +[coin_eur_ct_10] +value = EUR:0.10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = RSA +rsa_keysize = 1024 + +[coin_eur_1] +value = EUR:1 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = RSA +rsa_keysize = 1024 + +[coin_eur_5] +value = EUR:5 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = RSA +rsa_keysize = 1024 + +[coin_eur_10] +value = EUR:10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = RSA +rsa_keysize = 1024 + +[coin_eur_ct_1_age_restricted] +value = EUR:0.01 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.00 +fee_deposit = EUR:0.00 +fee_refresh = EUR:0.01 +fee_refund = EUR:0.01 +rsa_keysize = 1024 +age_restricted = YES +CIPHER = RSA + +[coin_eur_ct_10_age_restricted] +value = EUR:0.10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +rsa_keysize = 1024 +age_restricted = YES +CIPHER = RSA + +[coin_eur_1_age_restricted] +value = EUR:1 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +rsa_keysize = 1024 +age_restricted = YES +CIPHER = RSA + +[coin_eur_5_age_restricted] +value = EUR:5 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +rsa_keysize = 1024 +age_restricted = YES +CIPHER = RSA + +[coin_eur_10_age_restricted] +value = EUR:10 +duration_withdraw = 7 days +duration_spend = 2 years +duration_legal = 3 years +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +rsa_keysize = 1024 +age_restricted = YES +CIPHER = RSA
\ No newline at end of file diff --git a/src/testing/taler-unified-setup.sh b/src/testing/taler-unified-setup.sh new file mode 100755 index 000000000..d933cc819 --- /dev/null +++ b/src/testing/taler-unified-setup.sh @@ -0,0 +1,934 @@ +#!/bin/bash +# +# This file is part of TALER +# Copyright (C) 2023, 2024 Taler Systems SA +# +# TALER is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3, or +# (at your option) any later version. +# +# TALER is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with TALER; see the file COPYING. If not, see +# <http://www.gnu.org/licenses/> +# +# Author: Christian Grothoff +# +# This script configures and launches various GNU Taler services. +# Which ones depend on command-line options. Use "-h" to find out. +# Prints "<<READY>>" on a separate line once all requested services +# are running. Close STDIN (or input 'NEWLINE') to stop all started +# services again. +# +# shellcheck disable=SC2317 + +set -eu + +# These break TALER_HOME control via TALER_TEST_HOME... +unset XDG_DATA_HOME +unset XDG_CONFIG_HOME +unset XDG_CACHE_HOME + +EXIT_STATUS=2 + +# Exit, with status code "skip" (no 'real' failure) +function exit_skip() { + echo " SKIP: " "$@" >&2 + EXIT_STATUS=77 + exit "$EXIT_STATUS" +} + +# Exit, with error message (hard failure) +function exit_fail() { + echo " FAIL: " "$@" >&2 + EXIT_STATUS=1 + exit "$EXIT_STATUS" +} + +# Cleanup to run whenever we exit +function cleanup() +{ + echo "Taler unified setup terminating at $STAGE!" >&2 + + for n in $(jobs -p) + do + kill "$n" 2> /dev/null || true + done + wait + rm -f libeufin-nexus.pid libeufin-sandbox.pid + exit "$EXIT_STATUS" +} + +STAGE="boot" + +# Install cleanup handler (except for kill -9) +trap cleanup EXIT + +WAIT_FOR_SIGNAL=0 +START_AUDITOR=0 +START_BACKUP=0 +START_EXCHANGE=0 +START_FAKEBANK=0 +START_DONAU=0 +START_CHALLENGER=0 +START_AGGREGATOR=0 +START_MERCHANT=0 +START_NEXUS=0 +START_BANK=0 +START_TRANSFER=0 +START_WIREWATCH=0 +START_DEPOSITCHECK=0 +START_MERCHANT_EXCHANGE=0 +START_MERCHANT_WIREWATCH=0 +USE_ACCOUNT="exchange-account-1" +USE_VALGRIND="" +WIRE_DOMAIN="x-taler-bank" +CONF_ORIG="$HOME/.config/taler.conf" +LOGLEVEL="DEBUG" +DEFAULT_SLEEP="0.2" + +# Parse command-line options +while getopts ':abc:d:DeEfghkL:mMnr:stu:vwWz' OPTION; do + case "$OPTION" in + a) + START_AUDITOR="1" + ;; + b) + START_BANK="1" + ;; + c) + CONF_ORIG="$OPTARG" + ;; + d) + WIRE_DOMAIN="$OPTARG" + ;; + D) + START_DONAU="1" + ;; + e) + START_EXCHANGE="1" + ;; + E) + START_MERCHANT_EXCHANGE="1" + ;; + f) + START_FAKEBANK="1" + ;; + h) + echo 'Supported options:' + echo ' -a -- start auditor' + echo ' -b -- start bank' + # shellcheck disable=SC2016 + echo ' -c $CONF -- set configuration' + # shellcheck disable=SC2016 + echo ' -d $METHOD -- use wire method (default: x-taler-bank)' + echo ' -D -- start donau' + echo ' -e -- start exchange' + echo ' -E -- start taler-merchant-exchange' + echo ' -f -- start fakebank' + echo ' -g -- start taler-exchange-aggregator' + echo ' -h -- print this help' + echo ' -k -- start challenger (KYC service)' + # shellcheck disable=SC2016 + echo ' -L $LOGLEVEL -- set log level' + echo ' -m -- start taler-merchant' + echo ' -M -- start taler-merchant-depositcheck' + echo ' -n -- start nexus' + # shellcheck disable=SC2016 + echo ' -r $MEX -- which exchange to use at the merchant (optional)' + echo ' -s -- start backup/sync' + echo ' -t -- start taler-exchange-transfer' + # shellcheck disable=SC2016 + echo ' -u $SECTION -- exchange account to use' + echo ' -v -- use valgrind' + echo ' -w -- start taler-exchange-wirewatch' + echo ' -W -- wait for signal' + echo ' -z -- start taler-merchant-wirewatch' + exit 0 + ;; + g) + START_AGGREGATOR="1" + ;; + k) + START_CHALLENGER="1" + ;; + L) + LOGLEVEL="$OPTARG" + ;; + m) + START_MERCHANT="1" + ;; + M) + START_DEPOSITCHECK="1" + ;; + n) + START_NEXUS="1" + ;; + r) + USE_MERCHANT_EXCHANGE="$OPTARG" + ;; + s) + START_BACKUP="1" + ;; + t) + START_TRANSFER="1" + ;; + u) + USE_ACCOUNT="$OPTARG" + ;; + v) + USE_VALGRIND="valgrind --leak-check=yes" + DEFAULT_SLEEP="2" + ;; + w) + START_WIREWATCH="1" + ;; + W) + WAIT_FOR_SIGNAL="1" + ;; + z) + START_MERCHANT_WIREWATCH="1" + ;; + ?) + exit_fail "Unrecognized command line option" + ;; + esac +done + +STAGE="init" + +echo "Starting with configuration file at: $CONF_ORIG" +CONF="$CONF_ORIG.edited" +cp "${CONF_ORIG}" "${CONF}" + +STAGE="checks" + +echo -n "Testing for jq" +jq -h > /dev/null || exit_skip " jq required" +echo " FOUND" + +echo -n "Testing for wget" +wget --help > /dev/null || exit_skip " wget required" +echo " FOUND" + +if [ "1" = "$START_EXCHANGE" ] +then + echo -n "Testing for Taler exchange" + taler-exchange-httpd -h > /dev/null || exit_skip " taler-exchange-httpd required" + echo " FOUND" +fi + +if [ "1" = "$START_DONAU" ] +then + echo -n "Testing for Donau" + donau-httpd -h > /dev/null || exit_skip " donau-httpd required" + echo " FOUND" +fi + +if [ "1" = "$START_MERCHANT" ] +then + echo -n "Testing for Taler merchant" + taler-merchant-httpd -h > /dev/null || exit_skip " taler-merchant-httpd required" + echo " FOUND" +fi + +if [ "1" = "$START_CHALLENGER" ] +then + echo -n "Testing for Taler challenger" + challenger-httpd -h > /dev/null || exit_skip " challenger-httpd required" + echo " FOUND" +fi + +if [ "1" = "$START_BACKUP" ] +then + echo -n "Testing for sync-httpd" + sync-httpd -h > /dev/null || exit_skip " sync-httpd required" + echo " FOUND" +fi + +if [ "1" = "$START_NEXUS" ] +then + echo -n "Testing for libeufin-cli" + libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING" + echo " FOUND" +fi + +if [ "1" = "$START_BANK" ] +then + echo -n "Testing for libeufin-bank" + libeufin-bank --help >/dev/null </dev/null || exit_skip " MISSING" + echo " FOUND" +fi + +STAGE="config" + + +EXCHANGE_URL=$(taler-config -c "$CONF" -s "EXCHANGE" -o "BASE_URL") +CURRENCY=$(taler-config -c "$CONF" -s "TALER" -o "CURRENCY") + +echo "Setting up for $CURRENCY at $EXCHANGE_URL" + +register_bank_account() { + wget \ + --http-user="$AUSER" \ + --http-password="$APASS" \ + --method=DELETE \ + -o /dev/null \ + -O /dev/null \ + -a wget-delete-account.log \ + "http://localhost:${BANK_PORT}/accounts/$1" \ + || true # deletion may fail, that's OK! + if [ "$1" = "exchange" ] || [ "$1" = "Exchange" ] + then + IS_EXCHANGE="true" + else + IS_EXCHANGE="false" + fi + MAYBE_IBAN="${4:-}" + if [ -n "$MAYBE_IBAN" ] + then + # shellcheck disable=SC2001 + ENAME=$(echo "$3" | sed -e "s/ /+/g") + # Note: this assumes that $3 has no spaces. Should probably escape in the future.. + PAYTO="payto://iban/SANDBOXX/${MAYBE_IBAN}?receiver-name=$ENAME" + BODY='{"username":"'"$1"'","password":"'"$2"'","is_taler_exchange":'"$IS_EXCHANGE"',"name":"'"$3"'","payto_uri":"'"$PAYTO"'"}' + else + BODY='{"username":"'"$1"'","password":"'"$2"'","is_taler_exchange":'"$IS_EXCHANGE"',"name":"'"$3"'"}' + fi + wget \ + --http-user="$AUSER" \ + --http-password="$APASS" \ + --method=POST \ + --header='Content-type: application/json' \ + --body-data="${BODY}" \ + -o /dev/null \ + -O /dev/null \ + -a wget-register-account.log \ + "http://localhost:${BANK_PORT}/accounts" +} + +register_fakebank_account() { + if [ "$1" = "exchange" ] || [ "$1" = "Exchange" ] + then + IS_EXCHANGE="true" + else + IS_EXCHANGE="false" + fi + BODY='{"username":"'"$1"'","password":"'"$2"'","name":"'"$1"'","is_taler_exchange":'"$IS_EXCHANGE"'}' + wget \ + --post-data="$BODY" \ + --header='Content-type: application/json' \ + --tries=3 \ + --waitretry=1 \ + --timeout=30 \ + "http://localhost:$BANK_PORT/accounts" \ + -a wget-register-account.log \ + -o /dev/null \ + -O /dev/null \ + >/dev/null +} + + +if [[ "1" = "$START_BANK" ]] +then + BANK_PORT=$(taler-config -c "$CONF" -s "libeufin-bank" -o "PORT") + BANK_URL="http://localhost:${BANK_PORT}/" +fi + +if [[ "1" = "$START_FAKEBANK" ]] +then + BANK_PORT=$(taler-config -c "$CONF" -s "BANK" -o "HTTP_PORT") + BANK_URL="http://localhost:${BANK_PORT}/" +fi + +STAGE="bank" + +if [ "1" = "$START_BANK" ] +then + echo -n "Setting up bank database ... " + libeufin-bank dbinit \ + -r \ + -c "$CONF" \ + &> libeufin-bank-reset.log + echo "DONE" + echo -n "Launching bank ... " + libeufin-bank serve \ + -c "$CONF" \ + > libeufin-bank-stdout.log \ + 2> libeufin-bank-stderr.log & + echo $! > libeufin-bank.pid + echo "DONE" + echo -n "Waiting for Bank ..." + OK="0" + for n in $(seq 1 100); do + echo -n "." + sleep "$DEFAULT_SLEEP" + wget --timeout=1 \ + --tries=3 \ + --waitretry=0 \ + -a wget-bank-check.log \ + -o /dev/null \ + -O /dev/null \ + "${BANK_URL}config" || continue + OK="1" + break + done + if [ "1" != "$OK" ] + then + exit_skip "Failed to launch services (bank)" + fi + echo "OK" + echo -n "Set admin password..." + AUSER="admin" + APASS="secret" + libeufin-bank \ + passwd \ + -c "$CONF" \ + "$AUSER" "$APASS" \ + &> libeufin-bank-passwd.log + libeufin-bank \ + edit-account \ + -c "$CONF" \ + --debit_threshold="$CURRENCY:1000000" \ + "$AUSER" \ + &> libeufin-bank-debit-threshold.log + echo " OK" +fi + +if [ "1" = "$START_NEXUS" ] +then + echo "Nexus currently not supported ..." +fi + +if [ "1" = "$START_FAKEBANK" ] +then + echo -n "Setting up fakebank ..." + $USE_VALGRIND taler-fakebank-run \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + -n 4 \ + 2> taler-fakebank-run.log & + echo " OK" +fi + +if [[ "1" = "$START_NEXUS" || "1" = "$START_FAKEBANK" ]] +then + echo -n "Waiting for the bank" + # Wait for bank to be available (usually the slowest) + OK="0" + for n in $(seq 1 300) + do + echo -n "." + sleep "$DEFAULT_SLEEP" + # bank + wget --tries=1 \ + --waitretry=0 \ + --timeout=1 \ + --user admin \ + --password secret \ + -a wget-bank-check.log \ + -o /dev/null \ + -O /dev/null \ + "http://localhost:${BANK_PORT}/" || continue + OK="1" + break + done + if [ "1" != "$OK" ] + then + exit_skip "Failed to launch services (bank)" + fi + echo " OK" +fi + +STAGE="accounts" + +if [ "1" = "$START_FAKEBANK" ] +then + echo -n "Register Fakebank users ..." + register_fakebank_account fortytwo x + register_fakebank_account fortythree x + register_fakebank_account exchange x + register_fakebank_account tor x + register_fakebank_account gnunet x + register_fakebank_account tutorial x + register_fakebank_account survey x + echo " DONE" +fi + +if [ "1" = "$START_BANK" ] +then + echo -n "Register bank users ..." + # The specified IBAN and name must match the ones hard-coded into + # the C helper for the add-incoming call. Without this value, + # libeufin-bank won't find the target account to debit along a /add-incoming + # call. + register_bank_account fortytwo x "User42" FR7630006000011234567890189 + register_bank_account fortythree x "Forty Three" + register_bank_account exchange x "Exchange Company" DE989651 + register_bank_account tor x "Tor Project" + register_bank_account gnunet x "GNUnet" + register_bank_account tutorial x "Tutorial" + register_bank_account survey x "Survey" + echo " DONE" +fi + +STAGE="exchange" + +if [ "1" = "$START_EXCHANGE" ] +then + echo -n "Starting exchange ..." + EXCHANGE_PORT=$(taler-config -c "$CONF" -s EXCHANGE -o PORT) + SERVE=$(taler-config -c "$CONF" -s EXCHANGE -o SERVE) + if [ "${SERVE}" = "unix" ] + then + EXCHANGE_URL=$(taler-config -c "$CONF" -s EXCHANGE -o BASE_URL) + else + EXCHANGE_URL="http://localhost:${EXCHANGE_PORT}/" + fi + MASTER_PRIV_FILE=$(taler-config -f -c "${CONF}" -s "EXCHANGE-OFFLINE" -o "MASTER_PRIV_FILE") + MASTER_PRIV_DIR=$(dirname "$MASTER_PRIV_FILE") + mkdir -p "${MASTER_PRIV_DIR}" + if [ ! -e "$MASTER_PRIV_FILE" ] + then + gnunet-ecc -g1 "$MASTER_PRIV_FILE" > /dev/null 2> /dev/null + echo -n "." + fi + MASTER_PUB=$(gnunet-ecc -p "${MASTER_PRIV_FILE}") + MPUB=$(taler-config -c "$CONF" -s exchange -o MASTER_PUBLIC_KEY) + if [ "$MPUB" != "$MASTER_PUB" ] + then + echo -n " patching master_pub ($MASTER_PUB)..." + taler-config -c "$CONF" -s exchange -o MASTER_PUBLIC_KEY -V "$MASTER_PUB" + fi + taler-exchange-dbinit -c "$CONF" --reset + $USE_VALGRIND taler-exchange-secmod-eddsa \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> taler-exchange-secmod-eddsa.log & + $USE_VALGRIND taler-exchange-secmod-rsa \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> taler-exchange-secmod-rsa.log & + $USE_VALGRIND taler-exchange-secmod-cs \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> taler-exchange-secmod-cs.log & + $USE_VALGRIND taler-exchange-httpd \ + -c "$CONF" \ + -L "$LOGLEVEL" 2> taler-exchange-httpd.log & + echo " DONE" +fi + +STAGE="donau" + +if [ "1" = "$START_DONAU" ] +then + echo -n "Starting Donau ..." + DONAU_PORT=$(donau-config -c "$CONF" -s DONAU -o PORT) + SERVE=$(donau-config -c "$CONF" -s DONAU -o SERVE) + if [ "${SERVE}" = "unix" ] + then + DONAU_URL=$(donau-config -c "$CONF" -s DONAU -o BASE_URL) + else + DONAU_URL="http://localhost:${DONAU_PORT}/" + fi + donau-dbinit -c "$CONF" --reset + $USE_VALGRIND taler-exchange-secmod-eddsa -c "$CONF" -L "$LOGLEVEL" -s donau 2> donau-secmod-eddsa.log & + $USE_VALGRIND taler-exchange-secmod-rsa -c "$CONF" -L "$LOGLEVEL" -s donau 2> donau-secmod-rsa.log & + $USE_VALGRIND taler-exchange-secmod-cs -c "$CONF" -L "$LOGLEVEL" -s donau 2> donau-secmod-cs.log & + $USE_VALGRIND donau-httpd -c "$CONF" -L "$LOGLEVEL" 2> donau-httpd.log & + echo " DONE" +fi + +STAGE="wirewatch" + +if [ "1" = "$START_WIREWATCH" ] +then + echo -n "Starting wirewatch ..." + $USE_VALGRIND taler-exchange-wirewatch \ + --account="$USE_ACCOUNT" \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + --longpoll-timeout="60 s" \ + 2> taler-exchange-wirewatch.log & + echo " DONE" +fi + +STAGE="aggregator" + +if [ "1" = "$START_AGGREGATOR" ] +then + echo -n "Starting aggregator ..." + $USE_VALGRIND taler-exchange-aggregator \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> taler-exchange-aggregator.log & + echo " DONE" +fi + +STAGE="transfer" + +if [ "1" = "$START_TRANSFER" ] +then + echo -n "Starting transfer ..." + $USE_VALGRIND taler-exchange-transfer \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> taler-exchange-transfer.log & + echo " DONE" +fi + +STAGE="merchant" + +if [ "1" = "$START_MERCHANT" ] +then + echo -n "Starting merchant ..." + if [ -n "${USE_MERCHANT_EXCHANGE+x}" ] + then + MEPUB=$(taler-config -c "$CONF" -s "${USE_MERCHANT_EXCHANGE}" -o MASTER_KEY) + MXPUB=${MASTER_PUB:-$(taler-config -c "$CONF" -s exchange -o MASTER_PUBLIC_KEY)} + if [ "$MEPUB" != "$MXPUB" ] + then + echo -n " patching master_pub ($MXPUB)..." + taler-config -c "$CONF" -s "${USE_MERCHANT_EXCHANGE}" -o MASTER_KEY -V "$MXPUB" + fi + fi + MERCHANT_TYPE=$(taler-config -c "$CONF" -s MERCHANT -o SERVE) + if [ "unix" = "$MERCHANT_TYPE" ] + then + MERCHANT_URL="$(taler-config -c "$CONF" -s MERCHANT -o BASE_URL)" + else + MERCHANT_PORT="$(taler-config -c "$CONF" -s MERCHANT -o PORT)" + MERCHANT_URL="http://localhost:${MERCHANT_PORT}/" + fi + taler-merchant-dbinit \ + -c "$CONF" \ + --reset &> taler-merchant-dbinit.log + $USE_VALGRIND taler-merchant-httpd \ + -c "$CONF" \ + -L "$LOGLEVEL" 2> taler-merchant-httpd.log & + $USE_VALGRIND taler-merchant-webhook \ + -c "$CONF" \ + -L "$LOGLEVEL" 2> taler-merchant-webhook.log & + echo " DONE" + if [ "1" = "$START_MERCHANT_WIREWATCH" ] + then + echo -n "Starting taler-merchant-wirewatch ..." + $USE_VALGRIND taler-merchant-wirewatch \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + --persist \ + 2> taler-merchant-wirewatch.log & + echo " DONE" + fi + if [ "1" = "$START_MERCHANT_EXCHANGE" ] + then + echo -n "Starting taler-merchant-exchange ..." + $USE_VALGRIND taler-merchant-exchange \ + -c "$CONF" \ + -L "$LOGLEVEL" 2> taler-merchant-exchange.log & + echo " DONE" + fi + if [ "1" = "$START_DEPOSITCHECK" ] + then + echo -n "Starting taler-merchant-depositcheck ..." + $USE_VALGRIND taler-merchant-depositcheck \ + -c "$CONF" \ + -L "$LOGLEVEL" 2> taler-merchant-depositcheck.log & + echo " DONE" + fi +fi + +STAGE="sync" + +if [ "1" = "$START_BACKUP" ] +then + echo -n "Starting sync ..." + SYNC_PORT=$(taler-config -c "$CONF" -s SYNC -o PORT) + SERVE=$(taler-config -c "$CONF" -s SYNC -o SERVE) + if [ "${SERVE}" = "unix" ] + then + SYNC_URL=$(taler-config -c "$CONF" -s SYNC -o BASE_URL) + else + SYNC_URL="http://localhost:${SYNC_PORT}/" + fi + sync-dbinit -c "$CONF" --reset + $USE_VALGRIND sync-httpd \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> sync-httpd.log & + echo " DONE" +fi + +STAGE="challenger" + +if [ "1" = "$START_CHALLENGER" ] +then + echo -n "Starting challenger ..." + CHALLENGER_PORT=$(challenger-config -c "$CONF" -s CHALLENGER -o PORT) + SERVE=$(taler-config -c "$CONF" -s CHALLENGER -o SERVE) + if [ "${SERVE}" = "unix" ] + then + CHALLENGER_URL=$(taler-config -c "$CONF" -s CHALLENGER -o BASE_URL) + else + CHALLENGER_URL="http://localhost:${CHALLENGER_PORT}/" + fi + challenger-dbinit \ + -c "$CONF" \ + --reset + $USE_VALGRIND challenger-httpd \ + -c "$CONF" \ + -L "$LOGLEVEL" \ + 2> challenger-httpd.log & + echo " DONE" + for SECTION in $(taler-config -c "$CONF" -S | grep kyc-provider) + do + LOGIC=$(taler-config -c "$CONF" -s "$SECTION" -o "LOGIC") + if [ "${LOGIC}" = "oauth2" ] + then + INFO=$(taler-config -c "$CONF" -s "$SECTION" -o "KYC_OAUTH2_INFO_URL") + if [ "${CHALLENGER_URL}info" = "$INFO" ] + then + echo -n "Enabling Challenger client for $SECTION" + CLIENT_SECRET=$(taler-config -c "$CONF" -s "$SECTION" -o "KYC_OAUTH2_CLIENT_SECRET") + RFC_8959_PREFIX="secret-token:" + if ! echo "${CLIENT_SECRET}" | grep ^${RFC_8959_PREFIX} > /dev/null + then + exit_fail "Client secret does not begin with '${RFC_8959_PREFIX}'" + fi + REDIRECT_URI="${EXCHANGE_URL}kyc-proof/kyc-provider-example-challeger" + CLIENT_ID=$(challenger-admin --add="${CLIENT_SECRET}" --quiet "${REDIRECT_URI}") + taler-config -c "$CONF" -s "$SECTION" -o KYC_OAUTH2_CLIENT_ID -V "$CLIENT_ID" + echo " DONE" + fi + fi + done +fi + +STAGE="auditor" + +if [ "1" = "$START_AUDITOR" ] +then + echo -n "Starting auditor ..." + AUDITOR_URL=$(taler-config -c "$CONF" -s AUDITOR -o BASE_URL) + AUDITOR_PRIV_FILE=$(taler-config -f -c "$CONF" -s AUDITOR -o AUDITOR_PRIV_FILE) + AUDITOR_PRIV_DIR=$(dirname "$AUDITOR_PRIV_FILE") + mkdir -p "$AUDITOR_PRIV_DIR" + if [ ! -e "$AUDITOR_PRIV_FILE" ] + then + gnunet-ecc -g1 "$AUDITOR_PRIV_FILE" > /dev/null 2> /dev/null + echo -n "." + fi + AUDITOR_PUB=$(gnunet-ecc -p "${AUDITOR_PRIV_FILE}") + MAPUB=${MASTER_PUB:-$(taler-config -c "$CONF" -s exchange -o MASTER_PUBLIC_KEY)} + taler-auditor-dbinit \ + -c "$CONF" \ + --reset + $USE_VALGRIND taler-auditor-httpd \ + -L "$LOGLEVEL" \ + -c "$CONF" 2> taler-auditor-httpd.log & +# $USE_VALGRIND taler-helper-auditor-deposits \ +# -L "$LOGLEVEL" \ +# -c "$CONF" 2> taler-helper-auditor.log & + echo " DONE" +fi + +STAGE="wait" + +echo -n "Waiting for Taler services ..." +# Wait for all other taler services to be available +E_DONE=0 +M_DONE=0 +S_DONE=0 +K_DONE=0 +A_DONE=0 +for n in $(seq 1 20) +do + sleep "$DEFAULT_SLEEP" + OK="0" + if [ "0" = "$E_DONE" ] && [ "1" = "$START_EXCHANGE" ] + then + echo -n "E" + wget \ + --tries=1 \ + --timeout=1 \ + "${EXCHANGE_URL}config" \ + -o /dev/null \ + -O /dev/null >/dev/null || continue + E_DONE=1 + fi + if [ "0" = "$M_DONE" ] && [ "1" = "$START_MERCHANT" ] + then + echo -n "M" + wget \ + --tries=1 \ + --timeout=1 \ + "${MERCHANT_URL}config" \ + -o /dev/null \ + -O /dev/null >/dev/null || continue + M_DONE=1 + fi + if [ "0" = "$S_DONE" ] && [ "1" = "$START_BACKUP" ] + then + echo -n "S" + wget \ + --tries=1 \ + --timeout=1 \ + "${SYNC_URL}config" \ + -o /dev/null \ + -O /dev/null >/dev/null || continue + S_DONE=1 + fi + if [ "0" = "$K_DONE" ] && [ "1" = "$START_CHALLENGER" ] + then + echo -n "K" + wget \ + --tries=1 \ + --timeout=1 \ + "${CHALLENGER_URL}config" \ + -o /dev/null \ + -O /dev/null >/dev/null || continue + K_DONE=1 + fi + if [ "0" = "$A_DONE" ] && [ "1" = "$START_AUDITOR" ] + then + echo -n "A" + wget \ + --tries=1 \ + --timeout=1 \ + "${AUDITOR_URL}config" \ + -o /dev/null \ + -O /dev/null >/dev/null || continue + A_DONE=1 + fi + OK="1" + break +done +if [ 1 != "$OK" ] +then + exit_skip "Failed to launch (some) Taler services" +fi +echo " OK" + +if [ "1" = "$START_EXCHANGE" ] +then + echo -n "Wait for exchange /management/keys to be ready " + OK="0" + LAST_RESPONSE=$(mktemp tmp-last-response.XXXXXXXX) + for n in $(seq 1 10) + do + echo -n "." + sleep "$DEFAULT_SLEEP" + # exchange + wget \ + --tries=3 \ + --waitretry=0 \ + --timeout=30 \ + "${EXCHANGE_URL}management/keys"\ + -o /dev/null \ + -O "$LAST_RESPONSE" \ + >/dev/null || continue + OK="1" + break; + done + if [ "1" != "$OK" ] + then + cat "$LAST_RESPONSE" + exit_fail "Failed to setup exchange keys, check secmod logs" + fi + rm "$LAST_RESPONSE" + echo " OK" + + echo -n "Setting up exchange keys ..." + taler-exchange-offline -c "$CONF" \ + download \ + sign \ + wire-fee now "$WIRE_DOMAIN" "$CURRENCY:0.01" "$CURRENCY:0.01" \ + global-fee now "$CURRENCY:0.01" "$CURRENCY:0.01" "$CURRENCY:0.0" 1h 1year 5 \ + upload &> taler-exchange-offline.log + echo "OK" + ENABLED=$(taler-config -c "$CONF" -s "$USE_ACCOUNT" -o "ENABLE_CREDIT") + if [ "YES" = "$ENABLED" ] + then + echo -n "Configuring bank account $USE_ACCOUNT ..." + EXCHANGE_PAYTO_URI=$(taler-config -c "$CONF" -s "$USE_ACCOUNT" -o "PAYTO_URI") + taler-exchange-offline -c "$CONF" \ + enable-account "$EXCHANGE_PAYTO_URI" \ + upload &> "taler-exchange-offline-account.log" + echo " OK" + else + echo "WARNING: Account ${USE_ACCOUNT} not enabled (set to: '$ENABLED')" + fi + if [ "1" = "$START_AUDITOR" ] + then + echo -n "Enabling auditor ..." + taler-exchange-offline -c "$CONF" \ + enable-auditor "$AUDITOR_PUB" "$AUDITOR_URL" "$CURRENCY Auditor" \ + upload &> taler-exchange-offline-auditor.log + echo "OK" + fi + + echo -n "Checking /keys " + OK="0" + LAST_RESPONSE=$(mktemp tmp-last-response.XXXXXXXX) + for n in $(seq 1 10) + do + echo -n "." + sleep "$DEFAULT_SLEEP" + wget \ + --tries=1 \ + --timeout=5 \ + "${EXCHANGE_URL}keys" \ + -a wget-keys-check.log \ + -o /dev/null \ + -O "$LAST_RESPONSE" \ + >/dev/null || continue + OK="1" + break + done + if [ "1" != "$OK" ] + then + cat "$LAST_RESPONSE" + exit_fail " Failed to fetch ${EXCHANGE_URL}keys" + fi + rm "$LAST_RESPONSE" + echo " OK" +fi + +if [ "1" = "$START_AUDITOR" ] +then + echo -n "Setting up auditor signatures ..." + timeout 15 taler-auditor-offline -c "$CONF" \ + download \ + sign \ + upload &> taler-auditor-offline.log + echo " OK" +fi + +STAGE="ready" + +# Signal caller that we are ready. +echo "<<READY>>" + +if [ "1" = "$WAIT_FOR_SIGNAL" ] +then + while true + do + sleep 0.1 + done +else + # Wait until caller stops us. + # shellcheck disable=SC2162 + read +fi + +STAGE="exiting" + +echo "Taler unified setup terminating!" >&2 +EXIT_STATUS=0 +exit "$EXIT_STATUS" diff --git a/src/testing/test-taler-exchange-aggregator-postgres.conf b/src/testing/test-taler-exchange-aggregator-postgres.conf index 7f277629d..8c3ee4ba5 100644 --- a/src/testing/test-taler-exchange-aggregator-postgres.conf +++ b/src/testing/test-taler-exchange-aggregator-postgres.conf @@ -1,84 +1,59 @@ +# This file is in the public domain. + [PATHS] # Persistent data storage for the testcase TALER_TEST_HOME = test_taler_exchange_httpd_home/ -TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ [taler-exchange-secmod-rsa] # Reduce from 1 year to speed up test LOOKAHEAD_SIGN = 24 days [taler-exchange-secmod-eddsa] -# Reduce from 1 year to speed up test LOOKAHEAD_SIGN = 24 days -# Reduce from 12 weeks to ensure we have multiple DURATION = 14 days [taler] -# Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 [exchange] -# The DB plugin to use +AML_THRESHOLD = EUR:1000000 DB = postgres - -# HTTP port the exchange listens to PORT = 8081 - -# Master public key used to sign the exchange's various keys MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG - -# Expected base URL of the exchange. Used in wire transfers for -# the tracking API. BASE_URL = "http://localhost:8081/" [auditor] BASE_URL = "http://auditor.example.com/" +PORT = 8083 [auditordb-postgres] CONFIG = "postgres:///talercheck" [exchangedb] -# After how long do we close idle reserves? The exchange -# and the auditor must agree on this value. We currently -# expect it to be globally defined for the whole system, -# as there is no way for wallets to query this value. Thus, -# it is only configurable for testing, and should be treated -# as constant in production. IDLE_RESERVE_EXPIRATION_TIME = 4 weeks +LEGAL_RESERVE_EXPIRATION_TIME = 7 years [exchangedb-postgres] - -#The connection string the plugin has to use for connecting to the database CONFIG = postgres:///talercheck -[exchangedb] - -# After how long do we close idle reserves? The exchange -# and the auditor must agree on this value. We currently -# expect it to be globally defined for the whole system, -# as there is no way for wallets to query this value. Thus, -# it is only configurable for testing, and should be treated -# as constant in production. -IDLE_RESERVE_EXPIRATION_TIME = 4 weeks - -# After how long do we forget about reserves? Should be above -# the legal expiration timeframe of withdrawn coins. -LEGAL_RESERVE_EXPIRATION_TIME = 7 years - [exchange-account-1] - # What is the account URL? -PAYTO_URI = "payto://x-taler-bank/localhost/2" +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" ENABLE_DEBIT = YES ENABLE_CREDIT = YES [exchange-accountcredentials-1] -WIRE_GATEWAY_URL = "http://localhost:8082/2/" +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" WIRE_GATEWAY_AUTH_METHOD = basic USERNAME = Exchange PASSWORD = x +[admin-accountcredentials-1] +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x [bank] HTTP_PORT = 8082 @@ -92,4 +67,5 @@ fee_withdraw = EUR:0.00 fee_deposit = EUR:0.00 fee_refresh = EUR:0.01 fee_refund = EUR:0.01 +CIPHER = RSA rsa_keysize = 1024 diff --git a/src/testing/test-taler-exchange-wirewatch-postgres.conf b/src/testing/test-taler-exchange-wirewatch-postgres.conf index d42f9d445..4f13077ac 100644 --- a/src/testing/test-taler-exchange-wirewatch-postgres.conf +++ b/src/testing/test-taler-exchange-wirewatch-postgres.conf @@ -1,68 +1,55 @@ +# This file is in the public domain. + [PATHS] # Persistent data storage for the testcase TALER_TEST_HOME = test_taler_exchange_httpd_home/ -TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ [taler-exchange-secmod-rsa] -# Reduce from 1 year to speed up test LOOKAHEAD_SIGN = 24 days [taler-exchange-secmod-eddsa] -# Reduce from 1 year to speed up test LOOKAHEAD_SIGN = 24 days -# Reduce from 12 weeks to ensure we have multiple DURATION = 14 days [taler] -# Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 [exchange] -# The DB plugin to use +AML_THRESHOLD = EUR:1000000 DB = postgres - -# HTTP port the exchange listens to PORT = 8081 - -# Master public key used to sign the exchange's various keys MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG - -# Expected base URL of the exchange. BASE_URL = "http://localhost:8081/" [exchangedb] -# After how long do we close idle reserves? The exchange -# and the auditor must agree on this value. We currently -# expect it to be globally defined for the whole system, -# as there is no way for wallets to query this value. Thus, -# it is only configurable for testing, and should be treated -# as constant in production. -# # This is THE test that requires a short reserve expiration time! IDLE_RESERVE_EXPIRATION_TIME = 4 s [exchangedb-postgres] -#The connection string the plugin has to use for connecting to the database -CONFIG = "postgres:///talercheck" - -[auditordb-postgres] CONFIG = "postgres:///talercheck" [auditor] BASE_URL = "http://localhost:8083/" - -# HTTP port the auditor listens to PORT = 8083 +[auditordb-postgres] +CONFIG = "postgres:///talercheck" + [exchange-account-1] # What is the account URL? -PAYTO_URI = "payto://x-taler-bank/localhost/2" +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" ENABLE_DEBIT = YES ENABLE_CREDIT = YES [exchange-accountcredentials-1] -WIRE_GATEWAY_URL = "http://localhost:8082/2/" +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x + +[admin-accountcredentials-1] +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" WIRE_GATEWAY_AUTH_METHOD = basic USERNAME = Exchange PASSWORD = x @@ -70,6 +57,16 @@ PASSWORD = x [bank] HTTP_PORT = 8082 +[libeufin-bank] +CURRENCY = EUR +DEFAULT_CUSTOMER_DEBT_LIMIT = EUR:200 +DEFAULT_ADMIN_DEBT_LIMIT = EUR:2000 +REGISTRATION_BONUS_ENABLED = yes +REGISTRATION_BONUS = EUR:100 +SUGGESTED_WITHDRAWAL_EXCHANGE = http://localhost:8081/ +SERVE = tcp +PORT = 8082 + # Need at least one coin, otherwise Exchange # refuses to start. [coin_eur_ct_1] @@ -81,4 +78,5 @@ fee_withdraw = EUR:0.00 fee_deposit = EUR:0.00 fee_refresh = EUR:0.01 fee_refund = EUR:0.01 +CIPHER = RSA rsa_keysize = 1024 diff --git a/src/testing/test_auditor_api-cs.conf b/src/testing/test_auditor_api-cs.conf new file mode 100644 index 000000000..b80696fb2 --- /dev/null +++ b/src/testing/test_auditor_api-cs.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ coins-cs.conf +@INLINE@ test_exchange_api.conf diff --git a/src/testing/test_auditor_api-rsa.conf b/src/testing/test_auditor_api-rsa.conf new file mode 100644 index 000000000..671e81108 --- /dev/null +++ b/src/testing/test_auditor_api-rsa.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ coins-rsa.conf +@INLINE@ test_exchange_api.conf diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c index 2ea173569..3810c601e 100644 --- a/src/testing/test_auditor_api.c +++ b/src/testing/test_auditor_api.c @@ -29,6 +29,7 @@ #include "taler_auditor_service.h" #include "taler_json_lib.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_bank_service.h" #include "taler_fakebank_lib.h" @@ -39,20 +40,14 @@ * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_auditor_api.conf" +static char *config_file; -#define CONFIG_FILE_EXPIRE_RESERVE_NOW \ - "test_auditor_api_expire_reserve_now.conf" +static char *config_file_expire_reserve_now; /** - * Exchange configuration data. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; - -/** - * Bank configuration data. - */ -static struct TALER_TESTING_BankConfiguration bc; +static struct TALER_TESTING_Credentials cred; /** * Execute the taler-exchange-wirewatch command with @@ -61,7 +56,7 @@ static struct TALER_TESTING_BankConfiguration bc; * @param label label to use for the command. */ #define CMD_EXEC_WIREWATCH(label) \ - TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE) + TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, "exchange-account-2") /** * Execute the taler-exchange-aggregator, closer and transfer commands with @@ -70,8 +65,9 @@ static struct TALER_TESTING_BankConfiguration bc; * @param label label to use for the command. */ #define CMD_EXEC_AGGREGATOR(label) \ - TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE), \ - TALER_TESTING_cmd_exec_transfer (label, CONFIG_FILE) + TALER_TESTING_cmd_sleep (label "-sleep", 1), \ + TALER_TESTING_cmd_exec_aggregator (label, config_file), \ + TALER_TESTING_cmd_exec_transfer (label, config_file) /** * Run wire transfer of funds from some user's account to the @@ -82,8 +78,8 @@ static struct TALER_TESTING_BankConfiguration bc; */ #define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ TALER_TESTING_cmd_admin_add_incoming (label, amount, \ - &bc.exchange_auth, \ - bc.user42_payto) + &cred.ba, \ + cred.user42_payto) /** * Run the taler-auditor. @@ -91,7 +87,7 @@ static struct TALER_TESTING_BankConfiguration bc; * @param label label to use for the command. */ #define CMD_RUN_AUDITOR(label) \ - TALER_TESTING_cmd_exec_auditor (label, CONFIG_FILE) + TALER_TESTING_cmd_exec_auditor (label, config_file) /** @@ -115,7 +111,7 @@ run (void *cls, "EUR:5.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1", - "EUR:5.01", bc.user42_payto, bc.exchange_payto, + "EUR:5.01", cred.user42_payto, cred.exchange_payto, "create-reserve-1"), /** * Make a reserve exist, according to the previous transfer. @@ -127,6 +123,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", "create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_end () }; @@ -138,7 +135,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-simple", "withdraw-coin-1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", @@ -155,7 +152,7 @@ run (void *cls, "EUR:5.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("check-refresh-create-reserve-1", - "EUR:5.01", bc.user42_payto, bc.exchange_payto, + "EUR:5.01", cred.user42_payto, cred.exchange_payto, "refresh-create-reserve-1"), /** * Make previous command effective. @@ -167,6 +164,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-1", "refresh-create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /** * Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in @@ -175,14 +173,15 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-partial", "refresh-withdraw-coin-1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice\",\"value\":\"EUR:1\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK), /** * Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x - * EUR:0.13) */ + * EUR:0.13) + */ TALER_TESTING_cmd_melt_double ("refresh-melt-1", "refresh-withdraw-coin-1", MHD_HTTP_OK, @@ -199,7 +198,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-1b", "refresh-reveal-1", 3, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.1", @@ -213,74 +212,83 @@ run (void *cls, * happen here, as each deposit operation is run with a * fresh merchant public key! NOTE: this comment comes * "verbatim" from the old test-suite, and IMO does not explain - * a lot!*/// + * a lot! */ CMD_EXEC_AGGREGATOR ("run-aggregator"), /** * Check all the transfers took place. */ - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-499c", ec.exchange_url, - "EUR:4.98", bc.exchange_payto, bc.user42_payto), - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-99c1", ec.exchange_url, - "EUR:0.98", bc.exchange_payto, bc.user42_payto), - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-99c", ec.exchange_url, - "EUR:0.08", bc.exchange_payto, bc.user43_payto), + TALER_TESTING_cmd_check_bank_transfer ( + "check_bank_transfer-499c", + cred.exchange_url, + "EUR:4.98", + cred.exchange_payto, + cred.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ( + "check_bank_transfer-99c1", + cred.exchange_url, + "EUR:0.98", + cred.exchange_payto, + cred.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ( + "check_bank_transfer-99c", + cred.exchange_url, + "EUR:0.08", + cred.exchange_payto, + cred.user43_payto), /* The following transactions got originated within * the "massive deposit confirms" batch. */ - TALER_TESTING_cmd_check_bank_transfer - ("check-massive-transfer-1", - ec.exchange_url, + TALER_TESTING_cmd_check_bank_transfer ( + "check-massive-transfer-1", + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-2", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-3", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-4", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-5", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-6", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-7", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-8", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-9", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_transfer ("check-massive-transfer-10", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, bc.user43_payto), + cred.exchange_payto, cred.user43_payto), TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"), TALER_TESTING_cmd_end () }; @@ -298,17 +306,18 @@ run (void *cls, TALER_TESTING_cmd_check_bank_admin_transfer ( "check_bank_transfer-unaggregated", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-unaggregated"), TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unaggregated", "create-reserve-unaggregated", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_deposit ("deposit-unaggregated", "withdraw-coin-unaggregated", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, @@ -336,6 +345,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1", "create-reserve-r1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /** * Spend 5 EUR of the 5 EUR coin (in full). Merchant would @@ -344,7 +354,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-refund-1", "withdraw-coin-r1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice\",\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_MINUTES, "EUR:5", @@ -361,7 +371,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-refund-2", "withdraw-coin-r1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"more\",\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:4.99", @@ -391,15 +401,15 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1", "recoup-create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_revoke ("revoke-1", MHD_HTTP_OK, "recoup-withdraw-coin-1", - CONFIG_FILE), + config_file), TALER_TESTING_cmd_recoup ("recoup-1", MHD_HTTP_OK, "recoup-withdraw-coin-1", - NULL, "EUR:5"), /** * Re-withdraw from this reserve @@ -407,17 +417,20 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2", "recoup-create-reserve-1", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), /** * These commands should close the reserve because the aggregator * is given a config file that overrides the reserve expiration * time (making it now-ish) - */CMD_TRANSFER_TO_EXCHANGE ("short-lived-reserve", + */ + CMD_TRANSFER_TO_EXCHANGE ("short-lived-reserve", "EUR:5.01"), - TALER_TESTING_cmd_exec_wirewatch ("short-lived-aggregation", - CONFIG_FILE_EXPIRE_RESERVE_NOW), + TALER_TESTING_cmd_exec_wirewatch2 ("short-lived-aggregation", + config_file_expire_reserve_now, + "exchange-account-2"), TALER_TESTING_cmd_exec_aggregator ("close-reserves", - CONFIG_FILE_EXPIRE_RESERVE_NOW), + config_file_expire_reserve_now), /** * Fill reserve with EUR:2.02, as withdraw fee is 1 ct per * config, then withdraw two coin, partially spend one, and @@ -436,6 +449,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2a", "recoup-create-reserve-2", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), /** * Withdraw a 1 EUR coin, at fee of 1 ct @@ -443,11 +457,12 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2b", "recoup-create-reserve-2", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_deposit ("recoup-deposit-partial", "recoup-withdraw-coin-2a", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.5", @@ -455,11 +470,10 @@ run (void *cls, TALER_TESTING_cmd_revoke ("revoke-2", MHD_HTTP_OK, "recoup-withdraw-coin-2a", - CONFIG_FILE), + config_file), TALER_TESTING_cmd_recoup ("recoup-2", MHD_HTTP_OK, "recoup-withdraw-coin-2a", - NULL, "EUR:0.5"), TALER_TESTING_cmd_end () }; @@ -472,57 +486,68 @@ run (void *cls, */ CMD_TRANSFER_TO_EXCHANGE ("massive-reserve", "EUR:10.10"), - TALER_TESTING_cmd_check_bank_admin_transfer - ("check-massive-transfer", + TALER_TESTING_cmd_check_bank_admin_transfer ( + "check-massive-transfer", "EUR:10.10", - bc.user42_payto, bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "massive-reserve"), CMD_EXEC_WIREWATCH ("massive-wirewatch"), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-1", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-2", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-3", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-4", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-5", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-6", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-7", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-8", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-9", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ("massive-withdraw-10", "massive-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), - TALER_TESTING_cmd_deposit - ("massive-deposit-1", + TALER_TESTING_cmd_deposit ( + "massive-deposit-1", "massive-withdraw-1", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -531,7 +556,7 @@ run (void *cls, ("massive-deposit-2", "massive-withdraw-2", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -540,7 +565,7 @@ run (void *cls, ("massive-deposit-3", "massive-withdraw-3", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -549,7 +574,7 @@ run (void *cls, ("massive-deposit-4", "massive-withdraw-4", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -558,7 +583,7 @@ run (void *cls, ("massive-deposit-5", "massive-withdraw-5", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -567,7 +592,7 @@ run (void *cls, ("massive-deposit-6", "massive-withdraw-6", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -576,7 +601,7 @@ run (void *cls, ("massive-deposit-7", "massive-withdraw-7", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -585,7 +610,7 @@ run (void *cls, ("massive-deposit-8", "massive-withdraw-8", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -594,24 +619,23 @@ run (void *cls, ("massive-deposit-9", "massive-withdraw-9", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK), - TALER_TESTING_cmd_deposit - ("massive-deposit-10", + TALER_TESTING_cmd_deposit ( + "massive-deposit-10", "massive-withdraw-10", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK), TALER_TESTING_cmd_deposit_confirmation ("deposit-confirmation", - is->auditor, "massive-deposit-10", - 0, + 1, "EUR:0.99", MHD_HTTP_OK), CMD_RUN_AUDITOR ("massive-auditor"), @@ -620,25 +644,25 @@ run (void *cls, }; struct TALER_TESTING_Command commands[] = { - TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", - CONFIG_FILE, - "EUR:0.01", - "EUR:0.01"), - TALER_TESTING_cmd_auditor_add ("add-auditor-OK", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 2), + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-u", "exchange-account-2", + "-ae", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_get_auditor ("get-auditor", + cred.cfg, + true), + TALER_TESTING_cmd_exec_auditor_offline ("auditor-offline", + config_file), CMD_RUN_AUDITOR ("virgin-auditor"), - TALER_TESTING_cmd_exchanges_with_url ("check-exchange", - MHD_HTTP_OK, - "http://localhost:8081/"), TALER_TESTING_cmd_batch ("massive-deposit-confirms", massive_deposit_confirms), TALER_TESTING_cmd_batch ("withdraw", @@ -659,9 +683,9 @@ run (void *cls, TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - commands, - bc.exchange_auth.wire_gateway_url); + (void) cls; + TALER_TESTING_run (is, + commands); } @@ -669,47 +693,28 @@ int main (int argc, char *const *argv) { - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-auditor-api", - "INFO", - NULL); - /* Check fakebank port is available and get configuration data. */ - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (CONFIG_FILE, - "exchange-account-2", - &bc)) - return 77; - TALER_TESTING_cleanup_files (CONFIG_FILE); - /* @helpers. Run keyup, create tables, ... Note: it - * fetches the port number from config in order to see - * if it's available. */ - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (GNUNET_OK != - /* Set up event loop and reschedule context, plus - * start/stop the exchange. It calls TALER_TESTING_setup - * which creates the 'is' object. - */ - TALER_TESTING_auditor_setup (&run, - NULL, - CONFIG_FILE)) - return 1; - break; - default: - GNUNET_break (0); - return 1; + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + GNUNET_asprintf (&config_file, + "test_auditor_api-%s.conf", + cipher); + GNUNET_asprintf (&config_file_expire_reserve_now, + "test_auditor_api_expire_reserve_now-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_auditor_api.conf b/src/testing/test_auditor_api.conf deleted file mode 100644 index 03a5e2453..000000000 --- a/src/testing/test_auditor_api.conf +++ /dev/null @@ -1,140 +0,0 @@ - -# This file is in the public domain. -# -[PATHS] -# Persistent data storage for the testcase -TALER_TEST_HOME = test_exchange_api_home/ -TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ - -[taler-exchange-secmod-rsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days - -[taler-exchange-secmod-eddsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days -# Reduce from 12 weeks to ensure we have multiple -DURATION = 14 days - - -[taler] -# Currency supported by the exchange (can only be one) -CURRENCY = EUR -CURRENCY_ROUND_UNIT = EUR:0.01 - -[auditor] -BASE_URL = "http://localhost:8083/" - -# HTTP port the auditor listens to -PORT = 8083 - -TINY_AMOUNT = EUR:0.01 - -[exchange] - -# HTTP port the exchange listens to -PORT = 8081 - -# Master public key used to sign the exchange's various keys -MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG - -# How to access our database -DB = postgres - -# Base URL of the exchange. Must be set to a URL where the -# exchange (or the twister) is actually listening. -BASE_URL = "http://localhost:8081/" - -[exchangedb-postgres] -CONFIG = "postgres:///talercheck" - -[auditordb-postgres] -CONFIG = "postgres:///talercheck" - -# Sections starting with "exchange-account-" configure the bank accounts -# of the exchange. The "URL" specifies the account in -# payto://-format. -[exchange-account-1] -# What is the URL of our account? -PAYTO_URI = "payto://x-taler-bank/localhost/42" - -[exchange-accountcredentials-1] -WIRE_GATEWAY_URL = "http://localhost:8082/42/" - -[bank] -HTTP_PORT = 8082 - -# ENABLE_CREDIT = YES - -[exchange-account-2] -# What is the bank account (with the "Taler Bank" demo system)? -PAYTO_URI = "payto://x-taler-bank/localhost/2" -ENABLE_DEBIT = YES -ENABLE_CREDIT = YES - -# Authentication information for basic authentication -[exchange-accountcredentials-2] -WIRE_GATEWAY_URL = "http://localhost:8082/2/" -WIRE_GATEWAY_AUTH_METHOD = "basic" -USERNAME = user -PASSWORD = pass - - - - -# Sections starting with "coin_" specify which denominations -# the exchange should support (and their respective fee structure) -[coin_eur_ct_1] -value = EUR:0.01 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.00 -fee_deposit = EUR:0.00 -fee_refresh = EUR:0.01 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_ct_10] -value = EUR:0.10 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_1] -value = EUR:1 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_5] -value = EUR:5 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_10] -value = EUR:10 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 diff --git a/src/testing/test_exchange_api_expire_reserve_now.conf b/src/testing/test_auditor_api_expire_reserve_now-cs.conf index 05bca956b..7277a0dff 100644 --- a/src/testing/test_exchange_api_expire_reserve_now.conf +++ b/src/testing/test_auditor_api_expire_reserve_now-cs.conf @@ -1,4 +1,4 @@ -@INLINE@ test_exchange_api.conf +@INLINE@ test_auditor_api-cs.conf [exchangedb] IDLE_RESERVE_EXPIRATION_TIME = 0 s diff --git a/src/testing/test_auditor_api_expire_reserve_now.conf b/src/testing/test_auditor_api_expire_reserve_now-rsa.conf index c2bf8f479..788cc36f8 100644 --- a/src/testing/test_auditor_api_expire_reserve_now.conf +++ b/src/testing/test_auditor_api_expire_reserve_now-rsa.conf @@ -1,4 +1,4 @@ -@INLINE@ test_auditor_api.conf +@INLINE@ test_auditor_api-rsa.conf [exchangedb] IDLE_RESERVE_EXPIRATION_TIME = 0 s diff --git a/src/testing/test_auditor_api_version.c b/src/testing/test_auditor_api_version.c index 7b96525ac..dcd542ad8 100644 --- a/src/testing/test_auditor_api_version.c +++ b/src/testing/test_auditor_api_version.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -39,9 +39,9 @@ * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_auditor_api.conf" +#define CONFIG_FILE "test_auditor_api-rsa.conf" -static struct TALER_AUDITOR_Handle *ah; +static struct TALER_AUDITOR_GetConfigHandle *ah; static struct GNUNET_CURL_Context *ctx; @@ -51,6 +51,7 @@ static int global_ret; static struct GNUNET_SCHEDULER_Task *tt; + static void do_shutdown (void *cls) { @@ -61,7 +62,11 @@ do_shutdown (void *cls) GNUNET_SCHEDULER_cancel (tt); tt = NULL; } - TALER_AUDITOR_disconnect (ah); + if (NULL != ah) + { + TALER_AUDITOR_get_config_cancel (ah); + ah = NULL; + } GNUNET_CURL_fini (ctx); GNUNET_CURL_gnunet_rc_destroy (rc); } @@ -81,19 +86,16 @@ do_timeout (void *cls) * Function called with information about the auditor. * * @param cls closure - * @param hr http response details - * @param vi basic information about the auditor - * @param compat protocol compatibility information + * @param vr response details */ static void version_cb (void *cls, - const struct TALER_AUDITOR_HttpResponse *hr, - const struct TALER_AUDITOR_VersionInformation *vi, - enum TALER_AUDITOR_VersionCompatibility compat) + const struct TALER_AUDITOR_ConfigResponse *vr) { - (void) hr; - if ( (NULL != vi) && - (TALER_AUDITOR_VC_MATCH == compat) ) + (void) cls; + ah = NULL; + if ( (MHD_HTTP_OK == vr->hr.http_status) && + (TALER_AUDITOR_VC_MATCH == vr->details.ok.compat) ) global_ret = 0; else global_ret = 2; @@ -116,10 +118,10 @@ run (void *cls) ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, &rc); rc = GNUNET_CURL_gnunet_rc_create (ctx); - ah = TALER_AUDITOR_connect (ctx, - auditor_url, - &version_cb, - NULL); + ah = TALER_AUDITOR_get_config (ctx, + auditor_url, + &version_cb, + NULL); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, @@ -134,6 +136,8 @@ main (int argc, { struct GNUNET_OS_Process *proc; + (void) argc; + (void) argv; /* These environment variables get in the way... */ unsetenv ("XDG_DATA_HOME"); unsetenv ("XDG_CONFIG_HOME"); @@ -145,15 +149,16 @@ main (int argc, "taler-auditor-httpd", "taler-auditor-httpd", "-c", CONFIG_FILE, + "-L", "INFO", NULL); if (NULL == proc) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-auditor-httpd`," - " is your PATH correct?\n"); + "Failed to run `taler-auditor-httpd`, is your PATH correct?\n"); return 77; } - if (0 != TALER_TESTING_wait_auditor_ready ("http://localhost:8083/")) + global_ret = TALER_TESTING_wait_httpd_ready ("http://localhost:8083/"); + if (0 != global_ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to launch `taler-auditor-httpd`\n"); @@ -163,7 +168,8 @@ main (int argc, GNUNET_SCHEDULER_run (&run, NULL); } - GNUNET_OS_process_kill (proc, SIGTERM); + GNUNET_OS_process_kill (proc, + SIGTERM); GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); return global_ret; diff --git a/src/testing/test_bank_api.c b/src/testing/test_bank_api.c index e5a125894..8cbc86bbd 100644 --- a/src/testing/test_bank_api.c +++ b/src/testing/test_bank_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016-2020 Taler Systems SA + Copyright (C) 2016-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -34,34 +34,27 @@ #include "taler_testing_lib.h" #define CONFIG_FILE_FAKEBANK "test_bank_api_fakebank.conf" -#define CONFIG_FILE_PYBANK "test_bank_api_pybank.conf" + #define CONFIG_FILE_NEXUS "test_bank_api_nexus.conf" -/** - * Bank configuration data. - */ -static struct TALER_TESTING_BankConfiguration bc; /** - * Handle to the Py-bank daemon. + * Configuration file. It changes based on + * whether Nexus or Fakebank are used. */ -static struct GNUNET_OS_Process *bankd; +static const char *cfgfile; /** - * Flag indicating whether the test is running against the - * Fakebank. Set up at runtime. + * Our credentials. */ -static int with_fakebank; +static struct TALER_TESTING_Credentials cred; /** - * Handles to the libeufin services. + * Which bank is the test running against? + * Set up at runtime. */ -static struct TALER_TESTING_LibeufinServices libeufin_services; +static enum TALER_TESTING_BankSystem bs; -/** - * Needed to shutdown differently. - */ -static int with_libeufin; /** * Main function that will tell the interpreter what commands to @@ -74,51 +67,76 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_WireTransferIdentifierRawP wtid; + const char *ssoptions; - memset (&wtid, 42, sizeof (wtid)); + (void) cls; + switch (bs) + { + case TALER_TESTING_BS_FAKEBANK: + ssoptions = "-f"; + break; + case TALER_TESTING_BS_IBAN: + ssoptions = "-b"; + break; + default: + ssoptions = NULL; + break; + } + memset (&wtid, + 42, + sizeof (wtid)); { struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_system_start ("start-taler", + cfgfile, + ssoptions, + NULL), TALER_TESTING_cmd_bank_credits ("history-0", - &bc.exchange_auth, + &cred.ba, NULL, 1), TALER_TESTING_cmd_admin_add_incoming ("credit-1", - "KUDOS:5.01", - &bc.exchange_auth, - bc.user42_payto), + "EUR:5.01", + &cred.ba_admin, + cred.user42_payto), + /** + * This CMD doesn't care about the HTTP response code; that's + * because Fakebank and euFin behaves differently when a reserve + * pub is duplicate. Fakebank responds with 409, whereas euFin + * with 200 but it bounces the payment back to the customer. + */ TALER_TESTING_cmd_admin_add_incoming_with_ref ("credit-1-fail", - "KUDOS:2.01", - &bc.exchange_auth, - bc.user42_payto, + "EUR:2.01", + &cred.ba_admin, + cred.user42_payto, "credit-1", - MHD_HTTP_CONFLICT), - TALER_TESTING_cmd_sleep ("Waiting 4s for 'credit-1' to settle", - 4), + -1), + /** + * Check that the incoming payment with a duplicate + * reserve public key didn't make it to the exchange. + */ TALER_TESTING_cmd_bank_credits ("history-1c", - &bc.exchange_auth, + &cred.ba, NULL, 5), TALER_TESTING_cmd_bank_debits ("history-1d", - &bc.exchange_auth, + &cred.ba, NULL, 5), TALER_TESTING_cmd_admin_add_incoming ("credit-2", - "KUDOS:3.21", - &bc.exchange_auth, - bc.user42_payto), + "EUR:3.21", + &cred.ba_admin, + cred.user42_payto), TALER_TESTING_cmd_transfer ("debit-1", - "KUDOS:3.22", - &bc.exchange_auth, - bc.exchange_payto, - bc.user42_payto, + "EUR:3.22", + &cred.ba, + cred.exchange_payto, + cred.user42_payto, &wtid, "http://exchange.example.com/"), - - TALER_TESTING_cmd_sleep ("Waiting 5s for 'debit-1' to settle", - 5), TALER_TESTING_cmd_bank_debits ("history-2b", - &bc.exchange_auth, + &cred.ba, NULL, 5), TALER_TESTING_cmd_end () @@ -126,150 +144,53 @@ run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bank serves at `%s'\n", - bc.exchange_auth.wire_gateway_url); - if (GNUNET_YES == with_fakebank) - TALER_TESTING_run_with_fakebank (is, - commands, - bc.exchange_auth.wire_gateway_url); - else - TALER_TESTING_run (is, - commands); + cred.ba.wire_gateway_url); + TALER_TESTING_run (is, + commands); } } -/** - * Runs #TALER_TESTING_setup() using the configuration. - * - * @param cls unused - * @param cfg configuration to use - * @return status code - */ -static int -setup_with_cfg (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - (void) cls; - return TALER_TESTING_setup (&run, - NULL, - cfg, - NULL, - GNUNET_NO); -} - - int main (int argc, char *const *argv) { - int rv; - const char *cfgfile; - - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-bank-api", - "DEBUG", - NULL); - - with_fakebank = TALER_TESTING_has_in_name (argv[0], - "_with_fakebank"); - if (GNUNET_YES == with_fakebank) + (void) argc; + if (TALER_TESTING_has_in_name (argv[0], + "_with_fakebank")) { - TALER_LOG_DEBUG ("Running against the Fakebank.\n"); + bs = TALER_TESTING_BS_FAKEBANK; cfgfile = CONFIG_FILE_FAKEBANK; - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (CONFIG_FILE_FAKEBANK, - "exchange-account-2", - &bc)) - { - GNUNET_break (0); - return 77; - } } - else if (GNUNET_YES == TALER_TESTING_has_in_name (argv[0], - "_with_pybank")) + else if (TALER_TESTING_has_in_name (argv[0], + "_with_nexus")) { - TALER_LOG_DEBUG ("Running against the Pybank.\n"); - cfgfile = CONFIG_FILE_PYBANK; - if (GNUNET_OK != - TALER_TESTING_prepare_bank (CONFIG_FILE_PYBANK, - GNUNET_YES, - "exchange-account-2", - &bc)) - { - GNUNET_break (0); - return 77; - } - - if (NULL == (bankd = TALER_TESTING_run_bank ( - CONFIG_FILE_PYBANK, - bc.exchange_auth.wire_gateway_url))) - { - GNUNET_break (0); - return 77; - } - } - else if (GNUNET_YES == TALER_TESTING_has_in_name (argv[0], - "_with_nexus")) - { - TALER_LOG_DEBUG ("Running with Nexus.\n"); - with_libeufin = GNUNET_YES; + bs = TALER_TESTING_BS_IBAN; cfgfile = CONFIG_FILE_NEXUS; - if (GNUNET_OK != - TALER_TESTING_prepare_nexus (CONFIG_FILE_NEXUS, - GNUNET_YES, - "exchange-account-2", - &bc)) + if (GNUNET_SYSERR == + GNUNET_OS_check_helper_binary ("libeufin-bank", + false, + NULL)) { - GNUNET_break (0); + fprintf (stderr, + "libeufin-bank not found. Skipping test.\n"); return 77; } - libeufin_services = TALER_TESTING_run_libeufin (&bc); - if ( (NULL == libeufin_services.nexus) || - (NULL == libeufin_services.sandbox) ) - return 77; } else { - /* no bank service was ever invoked. */ + /* no bank service was specified. */ + GNUNET_break (0); return 77; } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_parse_and_run (cfgfile, - &setup_with_cfg, - NULL)) - rv = 1; - else - rv = 0; - - if (GNUNET_NO == with_fakebank) - { - // -> pybank - if (GNUNET_NO == with_libeufin) - { - - GNUNET_OS_process_kill (bankd, - SIGKILL); - GNUNET_OS_process_wait (bankd); - GNUNET_OS_process_destroy (bankd); - } - else // -> libeufin - { - GNUNET_OS_process_kill (libeufin_services.nexus, - SIGKILL); - GNUNET_OS_process_wait (libeufin_services.nexus); - GNUNET_OS_process_destroy (libeufin_services.nexus); - - GNUNET_OS_process_kill (libeufin_services.sandbox, - SIGKILL); - GNUNET_OS_process_wait (libeufin_services.sandbox); - GNUNET_OS_process_destroy (libeufin_services.sandbox); - } - } - - return rv; + return TALER_TESTING_main (argv, + "INFO", + cfgfile, + "exchange-account-2", + bs, + &cred, + &run, + NULL); } diff --git a/src/testing/test_bank_api.conf b/src/testing/test_bank_api.conf new file mode 100644 index 000000000..c262ae197 --- /dev/null +++ b/src/testing/test_bank_api.conf @@ -0,0 +1,23 @@ +# This file is in the public domain + +[PATHS] +TALER_TEST_HOME = test_exchange_api_home/ + +[taler] +currency = EUR + +[bank] +SERVE = http +HTTP_PORT = 8082 + +[libeufin-bank] +CURRENCY = EUR +WIRE_TYPE = iban +IBAN_PAYTO_BIC = SANDBOXX= +DEFAULT_CUSTOMER_DEBT_LIMIT = EUR:200 +DEFAULT_ADMIN_DEBT_LIMIT = EUR:2000 +REGISTRATION_BONUS_ENABLED = yes +REGISTRATION_BONUS = EUR:100 +SUGGESTED_WITHDRAWAL_EXCHANGE = http://localhost:8081/ +SERVE = tcp +PORT = 8082 diff --git a/src/testing/test_bank_api_fakebank.conf b/src/testing/test_bank_api_fakebank.conf index b97423987..62fa4cd4c 100644 --- a/src/testing/test_bank_api_fakebank.conf +++ b/src/testing/test_bank_api_fakebank.conf @@ -1,21 +1,21 @@ # This file is in the public domain. +@INLINE@ test_bank_api.conf -[taler] -currency = KUDOS +[exchange-account-1] +PAYTO_URI = "payto://x-taler-bank/localhost:8082/1?receiver-name=1" [exchange-account-2] -PAYTO_URI = payto://x-taler-bank/localhost/2 +PAYTO_URI = "payto://x-taler-bank/localhost:8082/2?receiver-name=2" [exchange-accountcredentials-2] -WIRE_GATEWAY_URL = "http://localhost:8081/2/" +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" WIRE_GATEWAY_AUTH_METHOD = basic USERNAME = Exchange PASSWORD = x -[bank] -SERVE = http -HTTP_PORT = 8081 -DATABASE = postgres:///talercheck - -[auditor] -BASE_URL = "http://localhost:8083/" +[admin-accountcredentials-2] +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" +WIRE_GATEWAY_AUTH_METHOD = basic +# For now, fakebank still checks against the Exchange account... +USERNAME = Exchange +PASSWORD = x diff --git a/src/testing/test_bank_api_fakebank_twisted.conf b/src/testing/test_bank_api_fakebank_twisted.conf index 4dab123a7..bc66c0d88 100644 --- a/src/testing/test_bank_api_fakebank_twisted.conf +++ b/src/testing/test_bank_api_fakebank_twisted.conf @@ -1,12 +1,15 @@ +# This file is in the public domain. + +@INLINE@ test_bank_api_fakebank.conf [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" +DESTINATION_BASE_URL = "http://localhost:8082" # Control port for TCP # PORT = 8889 @@ -18,20 +21,3 @@ ACCEPT_FROM6 = ::1; UNIXPATH = /tmp/taler-service-twister.sock UNIX_MATCH_UID = NO UNIX_MATCH_GID = YES - -[taler] -currency = KUDOS - -[bank] -serve = http -http_port = 8081 -database = postgres:///talercheck - -[exchange-account-1] -PAYTO_URI = payto://x-taler-bank/localhost:8081/1 - -[exchange-account-2] -PAYTO_URI = payto://x-taler-bank/localhost:8081/2 - -[auditor] -BASE_URL = "http://localhost:8083/" diff --git a/src/testing/test_bank_api_nexus.conf b/src/testing/test_bank_api_nexus.conf index c514170e1..605c7b00e 100644 --- a/src/testing/test_bank_api_nexus.conf +++ b/src/testing/test_bank_api_nexus.conf @@ -1,23 +1,35 @@ # This file is in the public domain. - -[taler] -currency = KUDOS +@INLINE@ test_bank_api.conf [exchange-account-2] PAYTO_URI = payto://iban/BIC/ES9121000418450200051332?receiver-name=Exchange [exchange-accountcredentials-2] -WIRE_GATEWAY_URL = http://localhost:5001/facades/my-facade/taler/ +WIRE_GATEWAY_URL = http://localhost:8082/accounts/exchange/taler-wire-gateway/ WIRE_GATEWAY_AUTH_METHOD = basic -# the exchange authenticates as the 'admin' user, -# since that makes the test preparation just easier. -USERNAME = Exchange +USERNAME = exchange PASSWORD = x -[bank] -# not (!) used by the nexus, only by the helper -# check to make sure the port is free for the nexus. -HTTP_PORT = 5001 +[admin-accountcredentials-2] +WIRE_GATEWAY_URL = http://localhost:8082/accounts/exchange/taler-wire-gateway/ +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = admin +# 'secret' is from taler-unified-setup.sh +PASSWORD = secret + +[libeufin-bankdb-postgres] +CONFIG="postgresql:///talercheck" -[auditor] -BASE_URL = "http://localhost:8083/" +# libeufin doesn't search our config.d/currencies.conf +# as it has a different resource path. Thus replicated +# here. +[currency-euro] +ENABLED = YES +name = "Euro" +code = "EUR" +decimal_separator = "," +fractional_input_digits = 2 +fractional_normal_digits = 2 +fractional_trailing_zero_digits = 2 +is_currency_name_leading = NO +alt_unit_names = {"0":"€"} diff --git a/src/testing/test_bank_api_pybank.conf b/src/testing/test_bank_api_pybank.conf deleted file mode 100644 index 6603ba8a5..000000000 --- a/src/testing/test_bank_api_pybank.conf +++ /dev/null @@ -1,21 +0,0 @@ -# This file is in the public domain. - -[taler] -currency = KUDOS - -[exchange-account-2] -PAYTO_URI = payto://x-taler-bank/localhost/Exchange - -[exchange-accountcredentials-2] -WIRE_GATEWAY_URL = "http://localhost:8081/taler-wire-gateway/Exchange/" -WIRE_GATEWAY_AUTH_METHOD = basic -USERNAME = Exchange -PASSWORD = x - -[bank] -SERVE = http -HTTP_PORT = 8081 -DATABASE = postgres:///talercheck - -[auditor] -BASE_URL = "http://localhost:8083/" diff --git a/src/testing/test_bank_api_pybank_twisted.conf b/src/testing/test_bank_api_pybank_twisted.conf deleted file mode 100644 index d89cf0469..000000000 --- a/src/testing/test_bank_api_pybank_twisted.conf +++ /dev/null @@ -1,43 +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 - -[auditor] -BASE_URL = "http://localhost:8083/" - -[taler] -currency = KUDOS - -[bank] -serve = http -http_port = 8081 -database = postgres:///talercheck - -[exchange-account-1] -PAYTO_URI = payto://x-taler-bank/localhost/1 - - -[exchange-account-2] -PAYTO_URI = payto://x-taler-bank/localhost/Exchange - -[exchange-accountcredentials-2] -WIRE_GATEWAY_URL = "http://localhost:8888/taler-wire-gateway/Exchange/" -WIRE_GATEWAY_AUTH_METHOD = basic -USERNAME = Exchange -PASSWORD = x diff --git a/src/testing/test_bank_api_twisted.c b/src/testing/test_bank_api_twisted.c index 8e505cdf3..038ec8a1f 100644 --- a/src/testing/test_bank_api_twisted.c +++ b/src/testing/test_bank_api_twisted.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,7 +17,7 @@ <http://www.gnu.org/licenses/> */ /** - * @file testing/test_bank_api_with_fakebank_twisted.c + * @file testing/test_bank_api_twisted.c * @author Marcello Stanisci * @author Sree Harsha Totakura <sreeharsha@totakura.in> * @author Christian Grothoff @@ -42,19 +42,20 @@ #define CONFIG_FILE_FAKEBANK "test_bank_api_fakebank_twisted.conf" /** - * Separate config file for running with the pybank. + * Configuration file we use. */ -#define CONFIG_FILE_PYBANK "test_bank_api_pybank_twisted.conf" +static const char *cfgfile; /** - * True when the test runs against Fakebank. + * Our credentials. */ -static int with_fakebank; +static struct TALER_TESTING_Credentials cred; /** - * Bank configuration data. + * Which bank is the test running against? + * Set up at runtime. */ -static struct TALER_TESTING_BankConfiguration bc; +static enum TALER_TESTING_BankSystem bs; /** * (real) Twister URL. Used at startup time to check if it runs. @@ -66,11 +67,6 @@ static char *twister_url; */ static struct GNUNET_OS_Process *twisterd; -/** - * Python bank process handle. - */ -static struct GNUNET_OS_Process *bankd; - /** * Main function that will tell @@ -83,174 +79,118 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_WireTransferIdentifierRawP wtid; - /* Route our commands through twister. */ + /* Authentication data to route our commands through twister. */ struct TALER_BANK_AuthenticationData exchange_auth_twisted; + const char *systype = NULL; + (void) cls; memset (&wtid, 0x5a, sizeof (wtid)); - memcpy (&exchange_auth_twisted, - &bc.exchange_auth, - sizeof (struct TALER_BANK_AuthenticationData)); - if (with_fakebank) - exchange_auth_twisted.wire_gateway_url = - "http://localhost:8888/2/"; - else - exchange_auth_twisted.wire_gateway_url = - "http://localhost:8888/taler-wire-gateway/Exchange/"; + GNUNET_memcpy (&exchange_auth_twisted, + &cred.ba, + sizeof (struct TALER_BANK_AuthenticationData)); + switch (bs) + { + case TALER_TESTING_BS_FAKEBANK: + exchange_auth_twisted.wire_gateway_url + = "http://localhost:8888/accounts/2/taler-wire-gateway/"; + systype = "-f"; + break; + case TALER_TESTING_BS_IBAN: + exchange_auth_twisted.wire_gateway_url + = "http://localhost:8888/accounts/Exchange/taler-wire-gateway/"; + systype = "-b"; + break; + } + GNUNET_assert (NULL != systype); - struct TALER_TESTING_Command commands[] = { - /* Test retrying transfer after failure. */ - TALER_TESTING_cmd_malform_response ("malform-transfer", - CONFIG_FILE_FAKEBANK), - TALER_TESTING_cmd_transfer_retry ( - TALER_TESTING_cmd_transfer ("debit-1", - "KUDOS:3.22", - &exchange_auth_twisted, - bc.exchange_payto, - bc.user42_payto, - &wtid, - "http://exchange.example.com/")), - TALER_TESTING_cmd_end () - }; + { + struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_system_start ("start-taler", + cfgfile, + systype, + NULL), + /* Test retrying transfer after failure. */ + TALER_TESTING_cmd_malform_response ("malform-transfer", + cfgfile), + TALER_TESTING_cmd_transfer_retry ( + TALER_TESTING_cmd_transfer ("debit-1", + "EUR:3.22", + &exchange_auth_twisted, + cred.exchange_payto, + cred.user42_payto, + &wtid, + "http://exchange.example.com/")), + TALER_TESTING_cmd_end () + }; - if (GNUNET_YES == with_fakebank) - TALER_TESTING_run_with_fakebank (is, - commands, - bc.exchange_auth.wire_gateway_url); - else TALER_TESTING_run (is, commands); + } } /** * Kill, wait, and destroy convenience function. * - * @param process process to purge. + * @param[in] process process to purge. */ static void purge_process (struct GNUNET_OS_Process *process) { - GNUNET_OS_process_kill (process, SIGINT); + GNUNET_OS_process_kill (process, + SIGINT); GNUNET_OS_process_wait (process); GNUNET_OS_process_destroy (process); } -/** - * Runs #TALER_TESTING_setup() using the configuration. - * - * @param cls unused - * @param cfg configuration to use - * @return status code - */ -static int -setup_with_cfg (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - (void) cls; - return TALER_TESTING_setup (&run, - NULL, - cfg, - NULL, - GNUNET_NO); -} - - int main (int argc, char *const *argv) { int ret; - const char *cfgfilename; - - /* 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); - with_fakebank = TALER_TESTING_has_in_name (argv[0], - "_with_fakebank"); - - if (with_fakebank) - cfgfilename = CONFIG_FILE_FAKEBANK; - else - cfgfilename = CONFIG_FILE_PYBANK; - - if (NULL == (twister_url = TALER_TWISTER_prepare_twister ( - cfgfilename))) - { - GNUNET_break (0); - return 77; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "twister_url is %s\n", - twister_url); - if (NULL == (twisterd = TALER_TWISTER_run_twister (cfgfilename))) + (void) argc; + if (TALER_TESTING_has_in_name (argv[0], + "_with_fakebank")) { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; + bs = TALER_TESTING_BS_FAKEBANK; + cfgfile = CONFIG_FILE_FAKEBANK; } - if (GNUNET_YES == with_fakebank) + else if (TALER_TESTING_has_in_name (argv[0], + "_with_nexus")) { - TALER_LOG_DEBUG ("Running against the Fakebank.\n"); - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (cfgfilename, - "exchange-account-2", - &bc)) - { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; - } + GNUNET_assert (0); /* FIXME: test with nexus not yet implemented */ + bs = TALER_TESTING_BS_IBAN; + /* cfgfile = CONFIG_FILE_NEXUS; */ } else { - TALER_LOG_DEBUG ("Running against the Pybank.\n"); - if (GNUNET_OK != - TALER_TESTING_prepare_bank (cfgfilename, - GNUNET_YES, - "exchange-account-2", - &bc)) - { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; - } - - if (NULL == (bankd = TALER_TESTING_run_bank ( - cfgfilename, - bc.exchange_auth.wire_gateway_url))) - { - GNUNET_break (0); - GNUNET_free (twister_url); - return 77; - } + /* no bank service was specified. */ + GNUNET_break (0); + return 77; } - sleep (5); - ret = GNUNET_CONFIGURATION_parse_and_run (cfgfilename, - &setup_with_cfg, - NULL); + /* FIXME: introduce commands for twister! */ + twister_url = TALER_TWISTER_prepare_twister (cfgfile); + if (NULL == twister_url) + return 77; + twisterd = TALER_TWISTER_run_twister (cfgfile); + if (NULL == twisterd) + return 77; + ret = TALER_TESTING_main (argv, + "INFO", + cfgfile, + "exchange-account-2", + bs, + &cred, + &run, + NULL); 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); - if (GNUNET_OK == ret) - return 0; - - return 1; + return ret; } diff --git a/src/testing/test_exchange_api-cs.conf b/src/testing/test_exchange_api-cs.conf new file mode 100644 index 000000000..b80696fb2 --- /dev/null +++ b/src/testing/test_exchange_api-cs.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ coins-cs.conf +@INLINE@ test_exchange_api.conf diff --git a/src/testing/test_exchange_api-rsa.conf b/src/testing/test_exchange_api-rsa.conf new file mode 100644 index 000000000..351c876d9 --- /dev/null +++ b/src/testing/test_exchange_api-rsa.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ coins-rsa.conf +@INLINE@ test_exchange_api.conf
\ No newline at end of file diff --git a/src/testing/test_exchange_api-twisted.conf b/src/testing/test_exchange_api-twisted.conf new file mode 100644 index 000000000..536d36ee4 --- /dev/null +++ b/src/testing/test_exchange_api-twisted.conf @@ -0,0 +1,29 @@ +# This file is in the public domain. + +[exchange] +# Base URL of the exchange ('S PROXY). This URL is where the +# twister listens at, so that it will be able to get all the +# connection addressed to the exchange. In fact, the presence +# of the twister is 100% transparent to the test case, as it +# only seeks the exchange/BASE_URL URL to connect to the exchange. +BASE_URL = "http://localhost:8888/" + +[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 diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c index 008eefb20..caeec1e76 100644 --- a/src/testing/test_exchange_api.c +++ b/src/testing/test_exchange_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014--2020 Taler Systems SA + Copyright (C) 2014--2022 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 @@ -29,31 +29,38 @@ #include "taler_exchange_service.h" #include "taler_json_lib.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_bank_service.h" #include "taler_fakebank_lib.h" #include "taler_testing_lib.h" +#include "taler_extensions.h" /** * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_exchange_api.conf" - -#define CONFIG_FILE_EXPIRE_RESERVE_NOW \ - "test_exchange_api_expire_reserve_now.conf" - +static char *config_file; /** - * Exchange configuration data. + * Special configuration file to use when we want reserves + * to expire 'immediately'. */ -static struct TALER_TESTING_ExchangeConfiguration ec; +static char *config_file_expire_reserve_now; /** - * Bank configuration data. + * Our credentials. */ -static struct TALER_TESTING_BankConfiguration bc; +static struct TALER_TESTING_Credentials cred; +/** + * Some tests behave differently when using CS as we cannot + * reuse the coin private key for different denominations + * due to the derivation of it with the /csr values. Hence + * some tests behave differently in CS mode, hence this + * flag. + */ +static bool uses_cs; /** * Execute the taler-exchange-wirewatch command with @@ -62,7 +69,7 @@ static struct TALER_TESTING_BankConfiguration bc; * @param label label to use for the command. */ #define CMD_EXEC_WIREWATCH(label) \ - TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE) + TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, "exchange-account-2") /** * Execute the taler-exchange-aggregator, closer and transfer commands with @@ -71,8 +78,9 @@ static struct TALER_TESTING_BankConfiguration bc; * @param label label to use for the command. */ #define CMD_EXEC_AGGREGATOR(label) \ - TALER_TESTING_cmd_exec_aggregator (label "-aggregator", CONFIG_FILE), \ - TALER_TESTING_cmd_exec_transfer (label "-transfer", CONFIG_FILE) + TALER_TESTING_cmd_sleep ("sleep-before-aggregator", 2), \ + TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \ + TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file) /** @@ -84,8 +92,8 @@ static struct TALER_TESTING_BankConfiguration bc; */ #define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ TALER_TESTING_cmd_admin_add_incoming (label, amount, \ - &bc.exchange_auth, \ - bc.user42_payto) + &cred.ba, \ + cred.user42_payto) /** * Main function that will tell the interpreter what commands to @@ -99,21 +107,6 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { /** - * Checks made against /wire response. - */ - struct TALER_TESTING_Command wire[] = { - /** - * Check if 'x-taler-bank' wire method is offered - * by the exchange. - */ - TALER_TESTING_cmd_wire ("wire-taler-bank-1", - "x-taler-bank", - NULL, - MHD_HTTP_OK), - TALER_TESTING_cmd_end () - }; - - /** * Test withdrawal plus spending. */ struct TALER_TESTING_Command withdraw[] = { @@ -122,30 +115,45 @@ run (void *cls, */ CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1", "EUR:6.02"), + TALER_TESTING_cmd_reserve_poll ("poll-reserve-1", + "create-reserve-1", + "EUR:6.02", + GNUNET_TIME_UNIT_MINUTES, + MHD_HTTP_OK), TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1", "EUR:6.02", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-1"), /** * Make a reserve exist, according to the previous * transfer. */ CMD_EXEC_WIREWATCH ("wirewatch-1"), + TALER_TESTING_cmd_reserve_poll_finish ("finish-poll-reserve-1", + GNUNET_TIME_UNIT_SECONDS, + "poll-reserve-1"), /** * Withdraw EUR:5. */ TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", "create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /** * Withdraw EUR:1 using the SAME private coin key as for the previous coin * (in violation of the specification, to be detected on spending!). + * However, note that this does NOT work with 'CS', as for a different + * denomination we get different R0/R1 values from the exchange, and + * thus will generate a different coin private key as R0/R1 are hashed + * into the coin priv. So here, we fail to 'reuse' the key due to the + * cryptographic construction! */ TALER_TESTING_cmd_withdraw_amount_reuse_key ("withdraw-coin-1x", "create-reserve-1", "EUR:1", + 0, /* age restriction off */ "withdraw-coin-1", MHD_HTTP_OK), /** @@ -161,6 +169,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2", "create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_CONFLICT), TALER_TESTING_cmd_end () }; @@ -172,29 +181,43 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-simple", "withdraw-coin-1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK), - TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay", + TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-1", + "deposit-simple", + MHD_HTTP_OK), + TALER_TESTING_cmd_sleep ("sleep-before-deposit-replay", + 1), + TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-2", "deposit-simple", MHD_HTTP_OK), + /* This creates a conflict, as we have the same coin public key (reuse!), + but different denomination public keys (which is not allowed). + However, note that this does NOT work with 'CS', as for a different + denomination we get different R0/R1 values from the exchange, and + thus will generate a different coin private key as R0/R1 are hashed + into the coin priv. So here, we fail to 'reuse' the key due to the + cryptographic construction! */ TALER_TESTING_cmd_deposit ("deposit-reused-coin-key-failure", "withdraw-coin-1x", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", - MHD_HTTP_CONFLICT), + uses_cs + ? MHD_HTTP_OK + : MHD_HTTP_CONFLICT), /** * Try to double spend using different wire details. */ TALER_TESTING_cmd_deposit ("deposit-double-1", "withdraw-coin-1", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", @@ -208,7 +231,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-double-1", "withdraw-coin-1", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", @@ -219,7 +242,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-double-2", "withdraw-coin-1", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", @@ -230,7 +253,10 @@ run (void *cls, struct TALER_TESTING_Command refresh[] = { /** * Try to melt the coin that shared the private key with another - * coin (should fail). */ + * coin (should fail). Note that in the CS-case, we fail also + * with MHD_HTTP_CONFLICT, but for a different reason: here it + * is not a denomination conflict, but a double-spending conflict. + */ TALER_TESTING_cmd_melt ("refresh-melt-reused-coin-key-failure", "withdraw-coin-1x", MHD_HTTP_CONFLICT, @@ -241,8 +267,8 @@ run (void *cls, "EUR:5.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("ck-refresh-create-reserve-1", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "refresh-create-reserve-1"), /** * Make previous command effective. @@ -254,14 +280,16 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-1", "refresh-create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin * (in full) (merchant would receive EUR:0.99 due to 1 ct - * deposit fee) */// + * deposit fee) + */ TALER_TESTING_cmd_deposit ("refresh-deposit-partial", "refresh-withdraw-coin-1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -298,7 +326,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-1a", "refresh-reveal-1-idempotency", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -309,7 +337,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-1b", "refresh-reveal-1", 3, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.1", @@ -330,11 +358,74 @@ run (void *cls, TALER_TESTING_cmd_end () }; + /** + * Test withdrawal with age restriction. Success is expected, so it MUST be + * called _after_ TALER_TESTING_cmd_exec_offline_sign_extensions is called, + * i. e. age restriction is activated in the exchange! + * + * TODO: create a test that tries to withdraw coins with age restriction but + * (expectedly) fails because the exchange doesn't support age restriction + * yet. + */ + struct TALER_TESTING_Command withdraw_age[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-age", + "EUR:6.01"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-age", + "EUR:6.01", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-age"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-age"), + /** + * Withdraw EUR:5. + */ + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-age-1", + "create-reserve-age", + "EUR:5", + 13, + MHD_HTTP_OK), + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command spend_age[] = { + /** + * Spend the coin. + */ + TALER_TESTING_cmd_deposit ("deposit-simple-age", + "withdraw-coin-age-1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:4.99", + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-age", + "deposit-simple-age", + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-age-1", + "deposit-simple-age", + MHD_HTTP_OK), + TALER_TESTING_cmd_sleep ("sleep-before-age-deposit-replay", + 1), + TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-age-2", + "deposit-simple-age", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command track[] = { /* Try resolving a deposit's WTID, as we never triggered * execution of transactions, the answer should be that * the exchange knows about the deposit, but has no WTID yet. - */// + */ TALER_TESTING_cmd_track_transaction ("deposit-wtid-found", "deposit-simple", 0, @@ -343,7 +434,7 @@ run (void *cls, /* Try resolving a deposit's WTID for a failed deposit. * As the deposit failed, the answer should be that the * exchange does NOT know about the deposit. - */// + */ TALER_TESTING_cmd_track_transaction ("deposit-wtid-failing", "deposit-double-2", 0, @@ -352,42 +443,69 @@ run (void *cls, /* Try resolving an undefined (all zeros) WTID; this * should fail as obviously the exchange didn't use that * WTID value for any transaction. - */// + */ TALER_TESTING_cmd_track_transfer_empty ("wire-deposit-failing", NULL, - 0, MHD_HTTP_NOT_FOUND), - TALER_TESTING_cmd_sleep ("sleep-before-aggregator", - 1), /* Run transfers. Note that _actual_ aggregation will NOT * happen here, as each deposit operation is run with a * fresh merchant public key, so the aggregator will treat * them as "different" merchants and do the wire transfers - * individually. */// + * individually. */ CMD_EXEC_AGGREGATOR ("run-aggregator"), /** * Check all the transfers took place. */ TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-499c", - ec.exchange_url, + cred.exchange_url, "EUR:4.98", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-499c2", + cred.exchange_url, + "EUR:4.97", + cred.exchange_payto, + cred.user42_payto), TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c1", - ec.exchange_url, + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c2", - ec.exchange_url, + cred.exchange_url, + "EUR:0.98", + cred.exchange_payto, + cred.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c3", + cred.exchange_url, "EUR:0.98", - bc.exchange_payto, - bc.user42_payto), - TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c", - ec.exchange_url, + cred.exchange_payto, + cred.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-99c4", + cred.exchange_url, + "EUR:0.98", + cred.exchange_payto, + cred.user42_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-08c", + cred.exchange_url, + "EUR:0.08", + cred.exchange_payto, + cred.user43_payto), + TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-08c2", + cred.exchange_url, "EUR:0.08", - bc.exchange_payto, - bc.user43_payto), + cred.exchange_payto, + cred.user43_payto), + /* In case of CS, one transaction above succeeded that + failed for RSA, hence we need to check for an extra transfer here */ + uses_cs + ? TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-98c", + cred.exchange_url, + "EUR:0.98", + cred.exchange_payto, + cred.user42_payto) + : TALER_TESTING_cmd_sleep ("dummy", + 0), TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"), TALER_TESTING_cmd_track_transaction ("deposit-wtid-ok", "deposit-simple", @@ -396,20 +514,17 @@ run (void *cls, "check_bank_transfer-499c"), TALER_TESTING_cmd_track_transfer ("wire-deposit-success-bank", "check_bank_transfer-99c1", - 0, MHD_HTTP_OK, "EUR:0.98", "EUR:0.01"), TALER_TESTING_cmd_track_transfer ("wire-deposits-success-wtid", "deposit-wtid-ok", - 0, MHD_HTTP_OK, "EUR:4.98", "EUR:0.01"), TALER_TESTING_cmd_end () }; - /** * This block checks whether a wire deadline * very far in the future does NOT get aggregated now. @@ -422,18 +537,19 @@ run (void *cls, TALER_TESTING_cmd_check_bank_admin_transfer ( "check-create-reserve-unaggregated", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-unaggregated"), CMD_EXEC_WIREWATCH ("wirewatch-unaggregated"), TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-unaggregated", "create-reserve-unaggregated", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_deposit ("deposit-unaggregated", "withdraw-coin-unaggregated", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_relative_multiply ( GNUNET_TIME_UNIT_YEARS, @@ -442,12 +558,108 @@ run (void *cls, MHD_HTTP_OK), CMD_EXEC_AGGREGATOR ("aggregation-attempt"), - TALER_TESTING_cmd_check_bank_empty - ("far-future-aggregation-b"), + TALER_TESTING_cmd_check_bank_empty ( + "far-future-aggregation-b"), TALER_TESTING_cmd_end () }; + struct TALER_TESTING_Command refresh_age[] = { + /* Fill reserve with EUR:5, 1ct is for fees. */ + CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-age-1", + "EUR:6.01"), + TALER_TESTING_cmd_check_bank_admin_transfer ( + "ck-refresh-create-reserve-age-1", + "EUR:6.01", + cred.user42_payto, + cred.exchange_payto, + "refresh-create-reserve-age-1"), + /** + * Make previous command effective. + */ + CMD_EXEC_WIREWATCH ("wirewatch-age-2"), + /** + * Withdraw EUR:7 with age restriction for age 13. + */ + TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-age-1", + "refresh-create-reserve-age-1", + "EUR:5", + 13, + MHD_HTTP_OK), + /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin + * (in full) (merchant would receive EUR:0.99 due to 1 ct + * deposit fee) + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-partial-age", + "refresh-withdraw-coin-age-1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), + /** + * Melt the rest of the coin's value + * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ + TALER_TESTING_cmd_melt_double ("refresh-melt-age-1", + "refresh-withdraw-coin-age-1", + MHD_HTTP_OK, + NULL), + /** + * Complete (successful) melt operation, and + * withdraw the coins + */ + TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-age-1", + "refresh-melt-age-1", + MHD_HTTP_OK), + /** + * Do it again to check idempotency + */ + TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-age-1-idempotency", + "refresh-melt-age-1", + MHD_HTTP_OK), + /** + * Test that /refresh/link works + */ + TALER_TESTING_cmd_refresh_link ("refresh-link-age-1", + "refresh-reveal-age-1", + MHD_HTTP_OK), + /** + * Try to spend a refreshed EUR:1 coin + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-age-1a", + "refresh-reveal-age-1-idempotency", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), + /** + * Try to spend a refreshed EUR:0.1 coin + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-age-1b", + "refresh-reveal-age-1", + 3, + cred.user43_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.1", + MHD_HTTP_OK), + /* Test running a failing melt operation (same operation + * again must fail) */ + TALER_TESTING_cmd_melt ("refresh-melt-failing-age", + "refresh-withdraw-coin-age-1", + MHD_HTTP_CONFLICT, + NULL), + /* Test running a failing melt operation (on a coin that + was itself revealed and subsequently deposited) */ + TALER_TESTING_cmd_melt ("refresh-melt-failing-age-2", + "refresh-reveal-age-1", + MHD_HTTP_CONFLICT, + NULL), + TALER_TESTING_cmd_end () + }; /** * This block exercises the aggretation logic by making two payments @@ -460,18 +672,19 @@ run (void *cls, TALER_TESTING_cmd_check_bank_admin_transfer ( "check-create-reserve-aggtest", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-aggtest"), CMD_EXEC_WIREWATCH ("wirewatch-aggtest"), TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-aggtest", "create-reserve-aggtest", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_deposit ("deposit-aggtest-1", "withdraw-coin-aggtest", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:2", @@ -479,7 +692,7 @@ run (void *cls, TALER_TESTING_cmd_deposit_with_ref ("deposit-aggtest-2", "withdraw-coin-aggtest", 0, - bc.user43_payto, + cred.user43_payto, "{\"items\":[{\"name\":\"foo bar\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:2", @@ -487,10 +700,10 @@ run (void *cls, "deposit-aggtest-1"), CMD_EXEC_AGGREGATOR ("aggregation-aggtest"), TALER_TESTING_cmd_check_bank_transfer ("check-bank-transfer-aggtest", - ec.exchange_url, + cred.exchange_url, "EUR:3.97", - bc.exchange_payto, - bc.user43_payto), + cred.exchange_payto, + cred.user43_payto), TALER_TESTING_cmd_check_bank_empty ("check-bank-empty-aggtest"), TALER_TESTING_cmd_end () }; @@ -504,8 +717,8 @@ run (void *cls, "EUR:5.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-r1", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-r1"), /** * Run wire-watch to trigger the reserve creation. @@ -515,6 +728,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1", "create-reserve-r1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /** * Spend 5 EUR of the 5 EUR coin (in full) (merchant would @@ -523,7 +737,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-refund-1", "withdraw-coin-r1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_MINUTES, "EUR:5", @@ -536,7 +750,7 @@ run (void *cls, * Note, this operation takes two commands: one to "flush" * the preliminary transfer (used to withdraw) from the * fakebank and the second to actually check there are not - * other transfers around. */// + * other transfers around. */ TALER_TESTING_cmd_check_bank_empty ("check_bank_transfer-pre-refund"), TALER_TESTING_cmd_refund_with_id ("refund-ok", MHD_HTTP_OK, @@ -557,7 +771,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-refund-insufficient-refund", "withdraw-coin-r1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:4\"}]}", GNUNET_TIME_UNIT_MINUTES, "EUR:4", @@ -574,7 +788,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-refund-2", "withdraw-coin-r1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"more ice cream\",\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:4.99", @@ -588,10 +802,10 @@ run (void *cls, * Check that deposit did run. */ TALER_TESTING_cmd_check_bank_transfer ("check_bank_transfer-pre-refund", - ec.exchange_url, + cred.exchange_url, "EUR:4.97", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /** * Run failing refund, as past deadline & aggregation. */ @@ -608,18 +822,19 @@ run (void *cls, "EUR:5.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-rb", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-rb"), CMD_EXEC_WIREWATCH ("wirewatch-rb"), TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb", "create-reserve-rb", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_deposit ("deposit-refund-1b", "withdraw-coin-rb", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", @@ -636,7 +851,7 @@ run (void *cls, * Run transfers. This will do the transfer as refund deadline * was 0, except of course because the refund succeeded, the * transfer should no longer be done. - */// + */ CMD_EXEC_AGGREGATOR ("run-aggregator-3b"), /* check that aggregator didn't do anything, as expected */ TALER_TESTING_cmd_check_bank_empty ("check-refund-fast-not-run"), @@ -653,8 +868,8 @@ run (void *cls, TALER_TESTING_cmd_check_bank_admin_transfer ( "recoup-create-reserve-1-check", "EUR:15.02", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "recoup-create-reserve-1"), /** * Run wire-watch to trigger the reserve creation. @@ -664,11 +879,13 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1", "recoup-create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /* Withdraw a 10 EUR coin, at fee of 1 ct */ TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-1b", "recoup-create-reserve-1", "EUR:10", + 0, /* age restriction off */ MHD_HTTP_OK), /* melt 10 EUR coin to get 5 EUR refreshed coin */ TALER_TESTING_cmd_melt ("recoup-melt-coin-1b", @@ -683,12 +900,11 @@ run (void *cls, TALER_TESTING_cmd_revoke ("revoke-0-EUR:5", MHD_HTTP_OK, "recoup-withdraw-coin-1", - CONFIG_FILE), + config_file), /* Recoup coin to reserve */ TALER_TESTING_cmd_recoup ("recoup-1", MHD_HTTP_OK, "recoup-withdraw-coin-1", - NULL, "EUR:5"), /* Check the money is back with the reserve */ TALER_TESTING_cmd_status ("recoup-reserve-status-1", @@ -696,11 +912,11 @@ run (void *cls, "EUR:5.0", MHD_HTTP_OK), /* Recoup-refresh coin to 10 EUR coin */ - TALER_TESTING_cmd_recoup ("recoup-1b", - MHD_HTTP_OK, - "recoup-reveal-coin-1b", - "recoup-melt-coin-1b", - "EUR:5"), + TALER_TESTING_cmd_recoup_refresh ("recoup-1b", + MHD_HTTP_OK, + "recoup-reveal-coin-1b", + "recoup-melt-coin-1b", + "EUR:5"), /* melt 10 EUR coin *again* to get 1 EUR refreshed coin */ TALER_TESTING_cmd_melt ("recoup-remelt-coin-1a", "recoup-withdraw-coin-1b", @@ -760,6 +976,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2", "recoup-create-reserve-1", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), /** * This withdrawal will test the logic to create a "recoup" @@ -768,6 +985,7 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2-over", "recoup-create-reserve-1", "EUR:10", + 0, /* age restriction off */ MHD_HTTP_CONFLICT), TALER_TESTING_cmd_status ("recoup-reserve-status-2", "recoup-create-reserve-1", @@ -780,18 +998,19 @@ run (void *cls, "EUR:5.01"), TALER_TESTING_cmd_check_bank_admin_transfer ("check-short-lived-reserve", "EUR:5.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "short-lived-reserve"), - TALER_TESTING_cmd_exec_wirewatch ("short-lived-aggregation", - CONFIG_FILE_EXPIRE_RESERVE_NOW), + TALER_TESTING_cmd_exec_wirewatch2 ("short-lived-aggregation", + config_file_expire_reserve_now, + "exchange-account-2"), TALER_TESTING_cmd_exec_closer ("close-reserves", - CONFIG_FILE_EXPIRE_RESERVE_NOW, + config_file_expire_reserve_now, "EUR:5", "EUR:0.01", "short-lived-reserve"), TALER_TESTING_cmd_exec_transfer ("close-reserves-transfer", - CONFIG_FILE_EXPIRE_RESERVE_NOW), + config_file_expire_reserve_now), TALER_TESTING_cmd_status ("short-lived-status", "short-lived-reserve", @@ -800,23 +1019,24 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("expired-withdraw", "short-lived-reserve", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_CONFLICT), TALER_TESTING_cmd_check_bank_transfer ("check_bank_short-lived_reimburse", - ec.exchange_url, + cred.exchange_url, "EUR:5", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /* Fill reserve with EUR:2.02, as withdraw fee is 1 ct per * config, then withdraw two coin, partially spend one, and * then have the rest paid back. Check deposit of other coin * fails. Do not use EUR:5 here as the EUR:5 coin was - * revoked and we did not bother to create a new one... */// + * revoked and we did not bother to create a new one... */ CMD_TRANSFER_TO_EXCHANGE ("recoup-create-reserve-2", "EUR:2.02"), TALER_TESTING_cmd_check_bank_admin_transfer ("ck-recoup-create-reserve-2", "EUR:2.02", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "recoup-create-reserve-2"), /* Make previous command effective. */ CMD_EXEC_WIREWATCH ("wirewatch-5"), @@ -824,16 +1044,18 @@ run (void *cls, TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2a", "recoup-create-reserve-2", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), /* Withdraw a 1 EUR coin, at fee of 1 ct */ TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-2b", "recoup-create-reserve-2", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_OK), TALER_TESTING_cmd_deposit ("recoup-deposit-partial", "recoup-withdraw-coin-2a", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.5", @@ -841,28 +1063,27 @@ run (void *cls, TALER_TESTING_cmd_revoke ("revoke-1-EUR:1", MHD_HTTP_OK, "recoup-withdraw-coin-2a", - CONFIG_FILE), - /* Check recoup is failing for the coin with the reused coin key */ + config_file), + /* Check recoup is failing for the coin with the reused coin key + (fails either because of denomination conflict (RSA) or + double-spending (CS))*/ TALER_TESTING_cmd_recoup ("recoup-2x", MHD_HTTP_CONFLICT, "withdraw-coin-1x", - NULL, "EUR:1"), TALER_TESTING_cmd_recoup ("recoup-2", MHD_HTTP_OK, "recoup-withdraw-coin-2a", - NULL, "EUR:0.5"), /* Idempotency of recoup (withdrawal variant) */ TALER_TESTING_cmd_recoup ("recoup-2b", MHD_HTTP_OK, "recoup-withdraw-coin-2a", - NULL, - NULL), + "EUR:0.5"), TALER_TESTING_cmd_deposit ("recoup-deposit-revoked", "recoup-withdraw-coin-2b", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -874,7 +1095,7 @@ run (void *cls, TALER_TESTING_cmd_deposit ("recoup-deposit-partial-after-recoup", "recoup-withdraw-coin-2a", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.5", @@ -885,13 +1106,14 @@ run (void *cls, TALER_TESTING_cmd_check_bank_admin_transfer ( "check-recoup-create-reserve-3", "EUR:1.01", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "recoup-create-reserve-3"), CMD_EXEC_WIREWATCH ("wirewatch-6"), TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-3-revoked", "recoup-create-reserve-3", "EUR:1", + 0, /* age restriction off */ MHD_HTTP_GONE), /* check that we are empty before the rejection test */ TALER_TESTING_cmd_check_bank_empty ("check-empty-again"), @@ -899,12 +1121,83 @@ run (void *cls, TALER_TESTING_cmd_end () }; + /** + * Test batch withdrawal plus spending. + */ + struct TALER_TESTING_Command batch_withdraw[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-batch-reserve-1", + "EUR:6.03"), + TALER_TESTING_cmd_reserve_poll ("poll-batch-reserve-1", + "create-batch-reserve-1", + "EUR:6.03", + GNUNET_TIME_UNIT_MINUTES, + MHD_HTTP_OK), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-batch-reserve-1", + "EUR:6.03", + cred.user42_payto, + cred.exchange_payto, + "create-batch-reserve-1"), + /* + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-batch-1"), + TALER_TESTING_cmd_reserve_poll_finish ("finish-poll-batch-reserve-1", + GNUNET_TIME_UNIT_SECONDS, + "poll-batch-reserve-1"), + /** + * Withdraw EUR:5 AND EUR:1. + */ + TALER_TESTING_cmd_batch_withdraw ("batch-withdraw-coin-1", + "create-batch-reserve-1", + 0, /* age restriction off */ + MHD_HTTP_OK, + "EUR:5", + "EUR:1", + NULL), + /** + * Check the reserve is (almost) depleted. + */ + TALER_TESTING_cmd_status ("status-batch-1", + "create-batch-reserve-1", + "EUR:0.01", + MHD_HTTP_OK), + TALER_TESTING_cmd_reserve_history ("history-batch-1", + "create-batch-reserve-1", + "EUR:0.01", + MHD_HTTP_OK), + /** + * Spend the coins. + */ + TALER_TESTING_cmd_batch_deposit ("batch-deposit-1", + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":5}]}", + GNUNET_TIME_UNIT_ZERO, + MHD_HTTP_OK, + "batch-withdraw-coin-1#0", + "EUR:5", + "batch-withdraw-coin-1#1", + "EUR:1", + NULL), + TALER_TESTING_cmd_coin_history ("coin-history-batch-1", + "batch-withdraw-coin-1#0", + "EUR:0.0", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + + #define RESERVE_OPEN_CLOSE_CHUNK 4 #define RESERVE_OPEN_CLOSE_ITERATIONS 3 struct TALER_TESTING_Command reserve_open_close[(RESERVE_OPEN_CLOSE_ITERATIONS * RESERVE_OPEN_CLOSE_CHUNK) + 1]; + + (void) cls; for (unsigned int i = 0; i < RESERVE_OPEN_CLOSE_ITERATIONS; i++) @@ -913,11 +1206,12 @@ run (void *cls, = CMD_TRANSFER_TO_EXCHANGE ("reserve-open-close-key", "EUR:20"); reserve_open_close[(i * RESERVE_OPEN_CLOSE_CHUNK) + 1] - = TALER_TESTING_cmd_exec_wirewatch ("reserve-open-close-wirewatch", - CONFIG_FILE_EXPIRE_RESERVE_NOW); + = TALER_TESTING_cmd_exec_wirewatch2 ("reserve-open-close-wirewatch", + config_file_expire_reserve_now, + "exchange-account-2"); reserve_open_close[(i * RESERVE_OPEN_CLOSE_CHUNK) + 2] = TALER_TESTING_cmd_exec_closer ("reserve-open-close-aggregation", - CONFIG_FILE_EXPIRE_RESERVE_NOW, + config_file_expire_reserve_now, "EUR:19.99", "EUR:0.01", "reserve-open-close-key"); @@ -932,30 +1226,30 @@ run (void *cls, { struct TALER_TESTING_Command commands[] = { - /* setup exchange */ - TALER_TESTING_cmd_auditor_add ("add-auditor-OK", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", - CONFIG_FILE, - "EUR:0.01", - "EUR:0.01"), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1), - TALER_TESTING_cmd_batch ("wire", - wire), + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), TALER_TESTING_cmd_batch ("withdraw", withdraw), TALER_TESTING_cmd_batch ("spend", spend), TALER_TESTING_cmd_batch ("refresh", refresh), + TALER_TESTING_cmd_batch ("withdraw-age", + withdraw_age), + TALER_TESTING_cmd_batch ("spend-age", + spend_age), + TALER_TESTING_cmd_batch ("refresh-age", + refresh_age), TALER_TESTING_cmd_batch ("track", track), TALER_TESTING_cmd_batch ("unaggregation", @@ -964,6 +1258,8 @@ run (void *cls, aggregation), TALER_TESTING_cmd_batch ("refund", refund), + TALER_TESTING_cmd_batch ("batch-withdraw", + batch_withdraw), TALER_TESTING_cmd_batch ("recoup", recoup), TALER_TESTING_cmd_batch ("reserve-open-close", @@ -972,9 +1268,8 @@ run (void *cls, TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - commands, - bc.exchange_auth.wire_gateway_url); + TALER_TESTING_run (is, + commands); } } @@ -983,47 +1278,30 @@ int main (int argc, char *const *argv) { - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api", - "INFO", - NULL); - /* Check fakebank port is available and get config */ - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (CONFIG_FILE, - "exchange-account-2", - &bc)) - return 77; - TALER_TESTING_cleanup_files (CONFIG_FILE); - /* @helpers. Run keyup, create tables, ... Note: it - * fetches the port number from config in order to see - * if it's available. */ - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (GNUNET_OK != - /* Set up event loop and reschedule context, plus - * start/stop the exchange. It calls TALER_TESTING_setup - * which creates the 'is' object. - */ - TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE)) - return 1; - break; - default: - GNUNET_break (0); - return 1; + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + uses_cs = (0 == strcmp (cipher, + "cs")); + GNUNET_asprintf (&config_file, + "test_exchange_api-%s.conf", + cipher); + GNUNET_asprintf (&config_file_expire_reserve_now, + "test_exchange_api_expire_reserve_now-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_exchange_api.conf b/src/testing/test_exchange_api.conf index a1b743658..c25f5091a 100644 --- a/src/testing/test_exchange_api.conf +++ b/src/testing/test_exchange_api.conf @@ -2,69 +2,80 @@ # [PATHS] -# Persistent data storage for the testcase TALER_TEST_HOME = test_exchange_api_home/ -TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ -[taler-exchange-secmod-rsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days +[libeufin-bank] +CURRENCY = EUR +DEFAULT_CUSTOMER_DEBT_LIMIT = EUR:200 +DEFAULT_ADMIN_DEBT_LIMIT = EUR:2000 +REGISTRATION_BONUS_ENABLED = yes +REGISTRATION_BONUS = EUR:100 +SUGGESTED_WITHDRAWAL_EXCHANGE = http://localhost:8081/ +WIRE_TYPE = iban +IBAN_PAYTO_BIC = SANDBOXX +SERVE = tcp +PORT = 8082 + +[libeufin-bankdb-postgres] +CONFIG = postgresql:///talercheck -[taler-exchange-secmod-eddsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days -# Reduce from 12 weeks to ensure we have multiple -DURATION = 14 days [taler] -# Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 [auditor] BASE_URL = "http://localhost:8083/" - -# HTTP port the auditor listens to PORT = 8083 +PUBLIC_KEY = 9QZ7CCC5QFMWE9FVF50MGYWV7JR92SFHY5KHT8A1A2VNHM37VCRG +TINY_AMOUNT = EUR:0.01 -[exchange] +[auditordb-postgres] +CONFIG = "postgres:///talercheck" -TERMS_ETAG = 0 -PRIVACY_ETAG = 0 +[bank] +HTTP_PORT = 8082 -# HTTP port the exchange listens to +[exchange] +TERMS_ETAG = tos +PRIVACY_ETAG = 0 +AML_THRESHOLD = EUR:1000000 PORT = 8081 - -# Master public key used to sign the exchange's various keys -MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG - -# How to access our database +MASTER_PUBLIC_KEY = QD6H521CBJBW0Z7PRN0JTAGH5JCQ97RDZRPPV5TQZSE78NQRT3KG DB = postgres - -# Base URL of the exchange. Must be set to a URL where the -# exchange (or the twister) is actually listening. BASE_URL = "http://localhost:8081/" +EXPIRE_SHARD_SIZE ="300 ms" +EXPIRE_IDLE_SLEEP_INTERVAL ="1 s" [exchangedb-postgres] CONFIG = "postgres:///talercheck" -[auditordb-postgres] -CONFIG = "postgres:///talercheck" +[taler-exchange-secmod-cs] +LOOKAHEAD_SIGN = "24 days" + +[taler-exchange-secmod-rsa] +LOOKAHEAD_SIGN = "24 days" + +[taler-exchange-secmod-eddsa] +LOOKAHEAD_SIGN = "24 days" +DURATION = "14 days" + -# Sections starting with "exchange-account-" configure the bank accounts -# of the exchange. The "URL" specifies the account in -# payto://-format. [exchange-account-1] -# What is the URL of our account? -PAYTO_URI = "payto://x-taler-bank/localhost/42" -# ENABLE_CREDIT = YES +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES [exchange-accountcredentials-1] -WIRE_GATEWAY_URL = "http://localhost:9081/42/" +WIRE_GATEWAY_AUTH_METHOD = none +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + +[admin-accountcredentials-1] +WIRE_GATEWAY_AUTH_METHOD = none +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" [exchange-account-2] -# What is the bank account (with the "Taler Bank" demo system)? -PAYTO_URI = "payto://x-taler-bank/localhost/2" +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" ENABLE_DEBIT = YES ENABLE_CREDIT = YES @@ -72,64 +83,36 @@ ENABLE_CREDIT = YES WIRE_GATEWAY_AUTH_METHOD = basic USERNAME = Exchange PASSWORD = x -WIRE_GATEWAY_URL = "http://localhost:9081/2/" +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" -[bank] -HTTP_PORT = 9081 - -# Sections starting with "coin_" specify which denominations -# the exchange should support (and their respective fee structure) -[coin_eur_ct_1] -value = EUR:0.01 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.00 -fee_deposit = EUR:0.00 -fee_refresh = EUR:0.01 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_ct_10] -value = EUR:0.10 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_1] -value = EUR:1 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_5] -value = EUR:5 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_10] -value = EUR:10 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 +[admin-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +# For now, fakebank still checks against the Exchange account... +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + + +[kyc-provider-test-oauth2] +COST = 0 +LOGIC = oauth2 +USER_TYPE = INDIVIDUAL +PROVIDED_CHECKS = DUMMY +KYC_OAUTH2_VALIDITY = forever +KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token +KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login +KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me +KYC_OAUTH2_CLIENT_ID = taler-exchange +KYC_OAUTH2_CLIENT_SECRET = exchange-secret +KYC_OAUTH2_POST_URL = http://example.com/ +KYC_OAUTH2_CONVERTER_HELPER = taler-exchange-kyc-oauth2-test-converter.sh + +[kyc-legitimization-close] +OPERATION_TYPE = CLOSE +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:0 +TIMEFRAME = 1d + +[exchange-extension-age_restriction] +ENABLED = YES +#AGE_GROUPS = "8:10:12:14:16:18:21" diff --git a/src/testing/test_exchange_api_age_restriction-cs.conf b/src/testing/test_exchange_api_age_restriction-cs.conf new file mode 100644 index 000000000..12195f9ba --- /dev/null +++ b/src/testing/test_exchange_api_age_restriction-cs.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ test_exchange_api_age_restriction.conf +@INLINE@ coins-cs.conf diff --git a/src/testing/test_exchange_api_age_restriction-rsa.conf b/src/testing/test_exchange_api_age_restriction-rsa.conf new file mode 100644 index 000000000..30d75090f --- /dev/null +++ b/src/testing/test_exchange_api_age_restriction-rsa.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ test_exchange_api_age_restriction.conf +@INLINE@ coins-rsa.conf diff --git a/src/testing/test_exchange_api_age_restriction.c b/src/testing/test_exchange_api_age_restriction.c new file mode 100644 index 000000000..5ba24a00c --- /dev/null +++ b/src/testing/test_exchange_api_age_restriction.c @@ -0,0 +1,370 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/test_exchange_api_age_restriction.c + * @brief testcase to test exchange's age-restrictrition related HTTP API interfaces + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> +#include <microhttpd.h> +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_testing_lib.h" +#include "taler_extensions.h" + +/** + * Configuration file we use. One (big) configuration is used + * for the various components for this test. + */ +static char *config_file; + +/** + * Our credentials. + */ +static struct TALER_TESTING_Credentials cred; + +/** + * Some tests behave differently when using CS as we cannot + * reuse the coin private key for different denominations + * due to the derivation of it with the /csr values. Hence + * some tests behave differently in CS mode, hence this + * flag. + */ +static bool uses_cs; + +/** + * Execute the taler-exchange-wirewatch command with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_WIREWATCH(label) \ + TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, "exchange-account-2") + +/** + * Execute the taler-exchange-aggregator, closer and transfer commands with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_AGGREGATOR(label) \ + TALER_TESTING_cmd_sleep ("sleep-before-aggregator", 2), \ + TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \ + TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file) + + +/** + * Run wire transfer of funds from some user's account to the + * exchange. + * + * @param label label to use for the command. + * @param amount amount to transfer, i.e. "EUR:1" + */ +#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ + TALER_TESTING_cmd_admin_add_incoming (label, amount, \ + &cred.ba, \ + cred.user42_payto) + +/** + * Main function that will tell the interpreter what commands to + * run. + * + * @param cls closure + * @param is interpreter we use to run commands + */ +static void +run (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + (void) cls; + /** + * Test withdrawal with age restriction. Success is expected (because the + * amount is below the kyc threshold ), so it MUST be + * called _after_ TALER_TESTING_cmd_exec_offline_sign_extensions is called, + * i. e. age restriction is activated in the exchange! + * + * TODO: create a test that tries to withdraw coins with age restriction but + * (expectedly) fails because the exchange doesn't support age restriction + * yet. + */ + struct TALER_TESTING_Command withdraw_age[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-age", + "EUR:6.01"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-age", + "EUR:6.01", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-age"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-age"), + /** + * Withdraw EUR:5. + */ + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-age-1", + "create-reserve-age", + "EUR:5", + 13, + MHD_HTTP_OK), + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command spend_age[] = { + /** + * Spend the coin. + */ + TALER_TESTING_cmd_deposit ("deposit-simple-age", + "withdraw-coin-age-1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:4.99", + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay-age", + "deposit-simple-age", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + + + struct TALER_TESTING_Command refresh_age[] = { + /* Fill reserve with EUR:5, 1ct is for fees. */ + CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-age-1", + "EUR:6.01"), + TALER_TESTING_cmd_check_bank_admin_transfer ( + "ck-refresh-create-reserve-age-1", + "EUR:6.01", + cred.user42_payto, + cred.exchange_payto, + "refresh-create-reserve-age-1"), + /** + * Make previous command effective. + */ + CMD_EXEC_WIREWATCH ("wirewatch-age-2"), + /** + * Withdraw EUR:7 with age restriction for age 13. + */ + TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin-age-1", + "refresh-create-reserve-age-1", + "EUR:5", + 13, + MHD_HTTP_OK), + /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin + * (in full) (merchant would receive EUR:0.99 due to 1 ct + * deposit fee) + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-partial-age", + "refresh-withdraw-coin-age-1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), + /** + * Melt the rest of the coin's value + * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ + TALER_TESTING_cmd_melt_double ("refresh-melt-age-1", + "refresh-withdraw-coin-age-1", + MHD_HTTP_OK, + NULL), + /** + * Complete (successful) melt operation, and + * withdraw the coins + */ + TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-age-1", + "refresh-melt-age-1", + MHD_HTTP_OK), + /** + * Do it again to check idempotency + */ + TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-age-1-idempotency", + "refresh-melt-age-1", + MHD_HTTP_OK), + /** + * Test that /refresh/link works + */ + TALER_TESTING_cmd_refresh_link ("refresh-link-age-1", + "refresh-reveal-age-1", + MHD_HTTP_OK), + /** + * Try to spend a refreshed EUR:1 coin + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-age-1a", + "refresh-reveal-age-1-idempotency", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), + /** + * Try to spend a refreshed EUR:0.1 coin + */ + TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-age-1b", + "refresh-reveal-age-1", + 3, + cred.user43_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":3}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.1", + MHD_HTTP_OK), + /* Test running a failing melt operation (same operation + * again must fail) */ + TALER_TESTING_cmd_melt ("refresh-melt-failing-age", + "refresh-withdraw-coin-age-1", + MHD_HTTP_CONFLICT, + NULL), + /* Test running a failing melt operation (on a coin that + was itself revealed and subsequently deposited) */ + TALER_TESTING_cmd_melt ("refresh-melt-failing-age-2", + "refresh-reveal-age-1", + MHD_HTTP_CONFLICT, + NULL), + TALER_TESTING_cmd_end () + }; + + /** + * Test with age-withdraw, after kyc process has set a birthdate + */ + struct TALER_TESTING_Command age_withdraw[] = { + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-kyc-1", + "EUR:30.02"), + TALER_TESTING_cmd_check_bank_admin_transfer ( + "check-create-reserve-kyc-1", + "EUR:30.02", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-kyc-1"), + CMD_EXEC_WIREWATCH ("wirewatch-age-withdraw-1"), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-lacking-kyc", + "create-reserve-kyc-1", + "EUR:10", + 0, /* age restriction off */ + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-withdraw", + "withdraw-coin-1-lacking-kyc", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc", + "withdraw-coin-1-lacking-kyc", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc", + "create-reserve-kyc-1", + "EUR:10", + 0, /* age restriction off */ + MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_age_withdraw ("age-withdraw-coin-1-too-low", + "create-reserve-kyc-1", + 18, /* Too high */ + MHD_HTTP_CONFLICT, + "EUR:10", + NULL), + TALER_TESTING_cmd_age_withdraw ("age-withdraw-coins-1", + "create-reserve-kyc-1", + 8, + MHD_HTTP_OK, + "EUR:10", + "EUR:10", + "EUR:5", + NULL), + TALER_TESTING_cmd_age_withdraw_reveal ("age-withdraw-coins-reveal-1", + "age-withdraw-coins-1", + MHD_HTTP_OK), + TALER_TESTING_cmd_end (), + }; + + { + struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_oauth_with_birthdate ("oauth-service-with-birthdate", + "2015-00-00", /* enough for a while */ + 6666), + TALER_TESTING_cmd_batch ("withdraw-age", + withdraw_age), + TALER_TESTING_cmd_batch ("spend-age", + spend_age), + TALER_TESTING_cmd_batch ("refresh-age", + refresh_age), + TALER_TESTING_cmd_batch ("age-withdraw", + age_withdraw), + /* End the suite. */ + TALER_TESTING_cmd_end () + }; + + TALER_TESTING_run (is, + commands); + } +} + + +int +main (int argc, + char *const *argv) +{ + (void) argc; + { + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + uses_cs = (0 == strcmp (cipher, + "cs")); + GNUNET_asprintf (&config_file, + "test_exchange_api_age_restriction-%s.conf", + cipher); + GNUNET_free (cipher); + } + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); +} + + +/* end of test_exchange_api_age_restriction.c */ diff --git a/src/testing/test_exchange_api_age_restriction.conf b/src/testing/test_exchange_api_age_restriction.conf new file mode 100644 index 000000000..1345fcb1a --- /dev/null +++ b/src/testing/test_exchange_api_age_restriction.conf @@ -0,0 +1,119 @@ +# This file is in the public domain. +# + +[PATHS] +TALER_TEST_HOME = test_exchange_api_home/ + +[taler] +CURRENCY = EUR +CURRENCY_ROUND_UNIT = EUR:0.01 + +[auditor] +BASE_URL = "http://localhost:8083/" +PORT = 8083 +PUBLIC_KEY = T0XJ9QZ59YDN7QG3RE40SB2HY7W0ASR1EKF4WZDGZ1G159RSQC80 +TINY_AMOUNT = EUR:0.01 + +[auditordb-postgres] +CONFIG = "postgres:///talercheck" + +[bank] +HTTP_PORT = 8082 + +[exchange] +TERMS_ETAG = tos +PRIVACY_ETAG = 0 +AML_THRESHOLD = EUR:10 +PORT = 8081 +MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG +DB = postgres +BASE_URL = "http://localhost:8081/" +EXPIRE_SHARD_SIZE ="300 ms" +EXPIRE_IDLE_SLEEP_INTERVAL ="1 s" + +[exchangedb-postgres] +CONFIG = "postgres:///talercheck" + +[taler-exchange-secmod-cs] +LOOKAHEAD_SIGN = "24 days" + +[taler-exchange-secmod-rsa] +LOOKAHEAD_SIGN = "24 days" + +[taler-exchange-secmod-eddsa] +LOOKAHEAD_SIGN = "24 days" +DURATION = "14 days" + + +[exchange-account-1] +PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES + +[exchange-accountcredentials-1] +WIRE_GATEWAY_AUTH_METHOD = none +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/42/taler-wire-gateway/" + +[admin-accountcredentials-1] +WIRE_GATEWAY_AUTH_METHOD = none +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/42/taler-wire-gateway/" + +[exchange-account-2] +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES + +[exchange-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + +[admin-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + + +[kyc-provider-test-oauth2] +COST = 0 +LOGIC = oauth2 +USER_TYPE = INDIVIDUAL +PROVIDED_CHECKS = DUMMY +KYC_OAUTH2_VALIDITY = forever +KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token +KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login +KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me +KYC_OAUTH2_CLIENT_ID = taler-exchange +KYC_OAUTH2_CLIENT_SECRET = exchange-secret +KYC_OAUTH2_POST_URL = http://example.com/ +KYC_OAUTH2_CONVERTER_HELPER = taler-exchange-kyc-oauth2-test-converter.sh + +[kyc-legitimization-balance-high] +OPERATION_TYPE = BALANCE +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:20 + +[kyc-legitimization-deposit-any] +OPERATION_TYPE = DEPOSIT +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:10 +TIMEFRAME = 1d + +[kyc-legitimization-withdraw] +OPERATION_TYPE = WITHDRAW +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:15 +TIMEFRAME = 1d + +[kyc-legitimization-merge] +OPERATION_TYPE = MERGE +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:15 +TIMEFRAME = 1d + + +[exchange-extension-age_restriction] +ENABLED = YES +#AGE_GROUPS = "8:10:12:14:16:18:21" diff --git a/src/testing/test_exchange_api_conflicts-cs.conf b/src/testing/test_exchange_api_conflicts-cs.conf new file mode 100644 index 000000000..c15d55490 --- /dev/null +++ b/src/testing/test_exchange_api_conflicts-cs.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ test_exchange_api_conflicts.conf +@INLINE@ coins-cs.conf diff --git a/src/testing/test_exchange_api_conflicts-rsa.conf b/src/testing/test_exchange_api_conflicts-rsa.conf new file mode 100644 index 000000000..f56111eee --- /dev/null +++ b/src/testing/test_exchange_api_conflicts-rsa.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +# +@INLINE@ test_exchange_api_conflicts.conf +@INLINE@ coins-rsa.conf diff --git a/src/testing/test_exchange_api_conflicts.c b/src/testing/test_exchange_api_conflicts.c new file mode 100644 index 000000000..070809d9d --- /dev/null +++ b/src/testing/test_exchange_api_conflicts.c @@ -0,0 +1,312 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/test_exchange_api_conflicts.c + * @brief testcase to test exchange's handling of coin conflicts: same private + * keys but different denominations or age restrictions + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> +#include <microhttpd.h> +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_testing_lib.h" +#include "taler_extensions.h" + +/** + * Configuration file we use. One (big) configuration is used + * for the various components for this test. + */ +static char *config_file; + +/** + * Our credentials. + */ +static struct TALER_TESTING_Credentials cred; + +/** + * Some tests behave differently when using CS as we cannot + * reuse the coin private key for different denominations + * due to the derivation of it with the /csr values. Hence + * some tests behave differently in CS mode, hence this + * flag. + */ +static bool uses_cs; + +/** + * Execute the taler-exchange-wirewatch command with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_WIREWATCH(label) \ + TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, \ + "exchange-account-2") + +/** + * Execute the taler-exchange-aggregator, closer and transfer commands with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_AGGREGATOR(label) \ + TALER_TESTING_cmd_sleep ("sleep-before-aggregator", 2), \ + TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \ + TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file) + + +/** + * Run wire transfer of funds from some user's account to the + * exchange. + * + * @param label label to use for the command. + * @param amount amount to transfer, i.e. "EUR:1" + */ +#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ + TALER_TESTING_cmd_admin_add_incoming (label, amount, \ + &cred.ba, \ + cred.user42_payto) + +/** + * Main function that will tell the interpreter what commands to + * run. + * + * @param cls closure + * @param is interpreter we use to run commands + */ +static void +run (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + (void) cls; + /** + * Test withdrawal with conflicting coins. + */ + struct TALER_TESTING_Command withdraw_conflict_denom[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-denom", + "EUR:21.14"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-denom", + "EUR:21.14", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-denom"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-conflict-denom"), + /** + * Withdraw EUR:0.10, EUR:1, EUR:5, EUR:15, but using the same private key each time. + */ + TALER_TESTING_cmd_batch_withdraw_with_conflict ("withdraw-coin-denom-1", + "create-reserve-denom", + true, + 0, /* age */ + MHD_HTTP_OK, + "EUR:1", + "EUR:5", + "EUR:10", + "EUR:0.10", + NULL), + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command spend_conflict_denom[] = { + /** + * Spend the coin. + */ + TALER_TESTING_cmd_deposit ("deposit-denom", + "withdraw-coin-denom-1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.99", + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit ("deposit-denom-conflict", + "withdraw-coin-denom-1", + 1, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:4.99", + /* Note: For CS, even though the master secret is the + * same for each coin, their private keys differ due + * to the random choice of the nonce by the exchange. */ + uses_cs ? MHD_HTTP_OK : MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_deposit ("deposit-denom-conflict-2", + "withdraw-coin-denom-1", + 2, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:9.99", + /* Note: For CS, even though the master secret is the + * same for each coin, their private keys differ due + * to the random choice of the nonce by the exchange. */ + uses_cs ? MHD_HTTP_OK : MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_deposit ("deposit-denom-conflict-3", + "withdraw-coin-denom-1", + 3, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.09", + /* Note: For CS, even though the master secret is the + * same for each coin, their private keys differ due + * to the random choice of the nonce by the exchange. */ + uses_cs ? MHD_HTTP_OK : MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command withdraw_conflict_age[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-age", + "EUR:3.03"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-age", + "EUR:3.03", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-age"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-conflict-age"), + /** + * Withdraw EUR:1, EUR:5, EUR:15, but using the same private key each time. + */ + TALER_TESTING_cmd_batch_withdraw_with_conflict ("withdraw-coin-age-1", + "create-reserve-age", + true, + 10, /* age */ + MHD_HTTP_OK, + "EUR:1", + "EUR:1", + "EUR:1", + NULL), + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command spend_conflict_age[] = { + /** + * Spend the coin. + */ + TALER_TESTING_cmd_deposit ("deposit-age", + "withdraw-coin-age-1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.99", + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit ("deposit-age-conflict", + "withdraw-coin-age-1", + 1, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.99", + MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_deposit ("deposit-age-conflict-2", + "withdraw-coin-age-1", + 2, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:0.99", + MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_end () + }; + + + { + struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_batch ("withdraw-conflict-denom", + withdraw_conflict_denom), + TALER_TESTING_cmd_batch ("spend-conflict-denom", + spend_conflict_denom), + TALER_TESTING_cmd_batch ("withdraw-conflict-age", + withdraw_conflict_age), + TALER_TESTING_cmd_batch ("spend-conflict-age", + spend_conflict_age), + /* End the suite. */ + TALER_TESTING_cmd_end () + }; + + TALER_TESTING_run (is, + commands); + } +} + + +int +main (int argc, + char *const *argv) +{ + (void) argc; + { + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + uses_cs = (0 == strcmp (cipher, + "cs")); + GNUNET_asprintf (&config_file, + "test_exchange_api_conflicts-%s.conf", + cipher); + GNUNET_free (cipher); + } + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); +} + + +/* end of test_exchange_api_conflicts.c */ diff --git a/src/testing/test_exchange_api_conflicts.conf b/src/testing/test_exchange_api_conflicts.conf new file mode 100644 index 000000000..d04379f05 --- /dev/null +++ b/src/testing/test_exchange_api_conflicts.conf @@ -0,0 +1,81 @@ +# This file is in the public domain. +# + +[PATHS] +TALER_TEST_HOME = test_exchange_api_home/ + +[taler] +CURRENCY = EUR +CURRENCY_ROUND_UNIT = EUR:0.01 + +[auditor] +BASE_URL = "http://localhost:8083/" +PORT = 8083 +PUBLIC_KEY = T0XJ9QZ59YDN7QG3RE40SB2HY7W0ASR1EKF4WZDGZ1G159RSQC80 +TINY_AMOUNT = EUR:0.01 + +[auditordb-postgres] +CONFIG = "postgres:///talercheck" + +[bank] +HTTP_PORT = 8082 + +[exchange] +TERMS_ETAG = tos +PRIVACY_ETAG = 0 +PORT = 8081 +AML_THRESHOLD = "EUR:99999999" +MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG +DB = postgres +BASE_URL = "http://localhost:8081/" +EXPIRE_SHARD_SIZE ="300 ms" +EXPIRE_IDLE_SLEEP_INTERVAL ="1 s" + +[exchangedb-postgres] +CONFIG = "postgres:///talercheck" + +[taler-exchange-secmod-cs] +LOOKAHEAD_SIGN = "24 days" + +[taler-exchange-secmod-rsa] +LOOKAHEAD_SIGN = "24 days" + +[taler-exchange-secmod-eddsa] +LOOKAHEAD_SIGN = "24 days" +DURATION = "14 days" + + +[exchange-account-1] +PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES + +[exchange-accountcredentials-1] +WIRE_GATEWAY_AUTH_METHOD = none +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/42/taler-wire-gateway/" + +[admin-accountcredentials-1] +WIRE_GATEWAY_AUTH_METHOD = none +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/42/taler-wire-gateway/" + +[exchange-account-2] +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES + +[exchange-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + +[admin-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + + +[exchange-extension-age_restriction] +ENABLED = YES +#AGE_GROUPS = "8:10:12:14:16:18:21" diff --git a/src/testing/test_exchange_api_expire_reserve_now-cs.conf b/src/testing/test_exchange_api_expire_reserve_now-cs.conf new file mode 100644 index 000000000..2cc4e0917 --- /dev/null +++ b/src/testing/test_exchange_api_expire_reserve_now-cs.conf @@ -0,0 +1,4 @@ +@INLINE@ test_exchange_api-cs.conf + +[exchangedb] +IDLE_RESERVE_EXPIRATION_TIME = 0 s diff --git a/src/testing/test_exchange_api_expire_reserve_now-rsa.conf b/src/testing/test_exchange_api_expire_reserve_now-rsa.conf new file mode 100644 index 000000000..52b829871 --- /dev/null +++ b/src/testing/test_exchange_api_expire_reserve_now-rsa.conf @@ -0,0 +1,4 @@ +@INLINE@ test_exchange_api-rsa.conf + +[exchangedb] +IDLE_RESERVE_EXPIRATION_TIME = 0 s diff --git a/src/testing/test_exchange_api_home/.config/taler/account-2.json b/src/testing/test_exchange_api_home/.config/taler/account-2.json deleted file mode 100644 index 21a7500b4..000000000 --- a/src/testing/test_exchange_api_home/.config/taler/account-2.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "payto_uri": "payto://x-taler-bank/localhost/2", - "master_sig": "HEWC1XDS0QZ53YQR451VRKD4N968NXWGZXS30HJ59MJ0PESACK1ZYPYCAT15P08WD58C7D7F6EVN26D59JKA75XEBDQCM8VYFETK82R" -}
\ No newline at end of file diff --git a/src/testing/test_exchange_api_home/.local/share/taler/auditor/offline-keys/auditor.priv b/src/testing/test_exchange_api_home/.local/share/taler/auditor/offline-keys/auditor.priv Binary files differnew file mode 100644 index 000000000..5b1c8fa05 --- /dev/null +++ b/src/testing/test_exchange_api_home/.local/share/taler/auditor/offline-keys/auditor.priv diff --git a/src/testing/test_exchange_api_home/.local/share/taler/exchange-offline/master.priv b/src/testing/test_exchange_api_home/.local/share/taler/exchange-offline/master.priv Binary files differindex 394926938..391b6ea73 100644 --- a/src/testing/test_exchange_api_home/.local/share/taler/exchange-offline/master.priv +++ b/src/testing/test_exchange_api_home/.local/share/taler/exchange-offline/master.priv diff --git a/src/testing/test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv b/src/testing/test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv deleted file mode 100644 index 394926938..000000000 --- a/src/testing/test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv +++ /dev/null @@ -1 +0,0 @@ -pÚ^ó-Ú33ˆ€XXÁ!ˆ\0qúýµmUþ_‰ˆ
\ No newline at end of file diff --git a/src/testing/test_exchange_api_home/taler/auditor/offline-keys/auditor.priv b/src/testing/test_exchange_api_home/taler/auditor/offline-keys/auditor.priv new file mode 100644 index 000000000..7e712e483 --- /dev/null +++ b/src/testing/test_exchange_api_home/taler/auditor/offline-keys/auditor.priv @@ -0,0 +1 @@ +·G,U²†{ ~#ræ-½HꑶÒÆÏâÿñ
\ No newline at end of file diff --git a/src/testing/test_exchange_api_keys_cherry_picking-cs.conf b/src/testing/test_exchange_api_keys_cherry_picking-cs.conf new file mode 100644 index 000000000..d25ef3c00 --- /dev/null +++ b/src/testing/test_exchange_api_keys_cherry_picking-cs.conf @@ -0,0 +1,18 @@ +# This file is in the public domain. +# +@INLINE@ test_exchange_api_keys_cherry_picking.conf + +[taler-exchange-secmod-cs] +OVERLAP_DURATION = 1 s +LOOKAHEAD_SIGN = 20 s + +[coin_eur_1] +value = EUR:1 +duration_withdraw = 5 s +duration_spend = 6 s +duration_legal = 7 s +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = CS diff --git a/src/testing/test_exchange_api_keys_cherry_picking-rsa.conf b/src/testing/test_exchange_api_keys_cherry_picking-rsa.conf new file mode 100644 index 000000000..672639b3f --- /dev/null +++ b/src/testing/test_exchange_api_keys_cherry_picking-rsa.conf @@ -0,0 +1,19 @@ +# This file is in the public domain. +# +@INLINE@ test_exchange_api_keys_cherry_picking.conf + +[taler-exchange-secmod-rsa] +OVERLAP_DURATION = 1 s +LOOKAHEAD_SIGN = 20 s + +[coin_eur_1] +value = EUR:1 +duration_withdraw = 5 s +duration_spend = 6 s +duration_legal = 7 s +fee_withdraw = EUR:0.01 +fee_deposit = EUR:0.01 +fee_refresh = EUR:0.03 +fee_refund = EUR:0.01 +CIPHER = RSA +rsa_keysize = 1024 diff --git a/src/testing/test_exchange_api_keys_cherry_picking.c b/src/testing/test_exchange_api_keys_cherry_picking.c index e79bc2c55..2919ea8d5 100644 --- a/src/testing/test_exchange_api_keys_cherry_picking.c +++ b/src/testing/test_exchange_api_keys_cherry_picking.c @@ -29,6 +29,7 @@ lished #include "taler_exchange_service.h" #include "taler_json_lib.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_bank_service.h" #include "taler_fakebank_lib.h" @@ -39,12 +40,12 @@ lished * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_exchange_api_keys_cherry_picking.conf" +static char *config_file; /** - * Exchange configuration data. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; +static struct TALER_TESTING_Credentials cred; /** @@ -58,47 +59,31 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command commands[] = { - TALER_TESTING_cmd_auditor_add ("add-auditor-OK", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", - CONFIG_FILE, - "EUR:0.01", - "EUR:0.01"), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_check_keys_pull_all_keys ("initial-/keys", - 1), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), TALER_TESTING_cmd_sleep ("sleep", 6 /* seconds */), - TALER_TESTING_cmd_check_keys ("check-keys-1", - 2 /* generation */), - TALER_TESTING_cmd_check_keys_with_last_denom ("check-keys-2", - 3 /* generation */, - "check-keys-1"), - TALER_TESTING_cmd_serialize_keys ("serialize-keys"), - TALER_TESTING_cmd_connect_with_state ("reconnect-with-state", - "serialize-keys"), - /** - * Make sure we have the same keys situation as - * it was before the serialization. - */ - TALER_TESTING_cmd_check_keys ("check-keys-after-deserialization", - 4), - /** - * Use one of the deserialized keys. - */ - TALER_TESTING_cmd_wire ("wire-with-serialized-keys", - "x-taler-bank", - NULL, - MHD_HTTP_OK), + TALER_TESTING_cmd_get_exchange ("get-exchange-1", + cred.cfg, + "get-exchange", + true, + true), + TALER_TESTING_cmd_get_exchange ("get-exchange-2", + cred.cfg, + "get-exchange-1", + true, + true), TALER_TESTING_cmd_end () }; + (void) cls; TALER_TESTING_run (is, commands); } @@ -108,41 +93,25 @@ int main (int argc, char *const *argv) { - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api-cherry-picking", - "DEBUG", - NULL); - TALER_TESTING_cleanup_files (CONFIG_FILE); - /* @helpers. Run keyup, create tables, ... Note: it - * fetches the port number from config in order to see - * if it's available. */ - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (GNUNET_OK != - /* Set up event loop and reschedule context, plus - * start/stop the exchange. It calls TALER_TESTING_setup - * which creates the 'is' object. - */ - TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE)) - return 1; - break; - default: - GNUNET_break (0); - return 1; + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + GNUNET_asprintf (&config_file, + "test_exchange_api_keys_cherry_picking-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_exchange_api_keys_cherry_picking.conf b/src/testing/test_exchange_api_keys_cherry_picking.conf index d7dd95352..142242424 100644 --- a/src/testing/test_exchange_api_keys_cherry_picking.conf +++ b/src/testing/test_exchange_api_keys_cherry_picking.conf @@ -3,52 +3,23 @@ [PATHS] # Persistent data storage for the testcase TALER_TEST_HOME = test_exchange_api_keys_cherry_picking_home/ -TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ - -# Persistent data storage -TALER_DATA_HOME = $TALER_HOME/.local/share/taler/ - -# Configuration files -TALER_CONFIG_HOME = $TALER_HOME/.config/taler/ - -# Cached data, no big deal if lost -TALER_CACHE_HOME = $TALER_HOME/.cache/taler/ [taler] -# Currency supported by the exchange (can only be one) CURRENCY = EUR - -[taler-exchange-secmod-rsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days +CURRENCY_ROUND_UNIT = EUR:0.01 [taler-exchange-secmod-eddsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days -# Reduce from 12 weeks to ensure we have multiple -DURATION = 14 days - -[auditor] -BASE_URL = "http://localhost:8083/" - -# HTTP port the auditor listens to -PORT = 8083 +OVERLAP_DURATION = 1 s +DURATION = 30 s +LOOKAHEAD_SIGN = 20 s [exchange] -# HTTP port the exchange listens to +AML_THRESHOLD = EUR:1000000 PORT = 8081 - -# Master public key used to sign the exchange's various keys MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG - -# How to access our database DB = postgres - -# Base URL of the exchange. Must be set to a URL where the -# exchange (or the twister) is actually listening. BASE_URL = "http://localhost:8081/" - [exchangedb-postgres] CONFIG = "postgres:///talercheck" @@ -56,43 +27,32 @@ CONFIG = "postgres:///talercheck" CONFIG = "postgres:///talercheck" [exchange-account-1] -PAYTO_URI = payto://x-taler-bank/localhost/42 +PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES [exchange-accountcredentials-1] -WIRE_GATEWAY_URL = "http://localhost:9082/42/" +WIRE_GATEWAY_URL = "http://localhost:9082/accounts/42/taler-wire-gateway/" + +[admin-accountcredentials-1] +WIRE_GATEWAY_URL = "http://localhost:9082/accounts/42/taler-wire-gateway/" [exchange-account-2] -PAYTO_URI = payto://x-taler-bank/localhost/2 +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" ENABLE_DEBIT = YES ENABLE_CREDIT = YES [exchange-accountcredentials-2] -WIRE_GATEWAY_URL = "http://localhost:9082/2/" +WIRE_GATEWAY_URL = "http://localhost:9082/accounts/2/taler-wire-gateway/" +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x -# Authentication information for basic authentication -TALER_BANK_AUTH_METHOD = "basic" -USERNAME = user -PASSWORD = pass +[admin-accountcredentials-2] +WIRE_GATEWAY_URL = "http://localhost:9082/accounts/2/taler-wire-gateway/" +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x [bank] HTTP_PORT=8082 - -[taler-exchange-secmod-rsa] -OVERLAP_DURATION = 1 s -LOOKAHEAD_SIGN = 20 s - -[taler-exchange-secmod-eddsa] -OVERLAP_DURATION = 1 s -DURATION = 30 s -LOOKAHEAD_SIGN = 20 s - -[coin_eur_1] -value = EUR:1 -duration_withdraw = 5 s -duration_spend = 6 s -duration_legal = 7 s -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 diff --git a/src/testing/test_exchange_api_keys_cherry_picking_home/.config/taler/x-taler-bank.json b/src/testing/test_exchange_api_keys_cherry_picking_home/.config/taler/x-taler-bank.json deleted file mode 100644 index 21a7500b4..000000000 --- a/src/testing/test_exchange_api_keys_cherry_picking_home/.config/taler/x-taler-bank.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "payto_uri": "payto://x-taler-bank/localhost/2", - "master_sig": "HEWC1XDS0QZ53YQR451VRKD4N968NXWGZXS30HJ59MJ0PESACK1ZYPYCAT15P08WD58C7D7F6EVN26D59JKA75XEBDQCM8VYFETK82R" -}
\ No newline at end of file diff --git a/src/testing/test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-offline/master.priv b/src/testing/test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-offline/master.priv deleted file mode 100644 index 394926938..000000000 --- a/src/testing/test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange-offline/master.priv +++ /dev/null @@ -1 +0,0 @@ -pÚ^ó-Ú33ˆ€XXÁ!ˆ\0qúýµmUþ_‰ˆ
\ No newline at end of file diff --git a/src/testing/test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange/offline-keys/master.priv b/src/testing/test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange/offline-keys/master.priv deleted file mode 100644 index 394926938..000000000 --- a/src/testing/test_exchange_api_keys_cherry_picking_home/.local/share/taler/exchange/offline-keys/master.priv +++ /dev/null @@ -1 +0,0 @@ -pÚ^ó-Ú33ˆ€XXÁ!ˆ\0qúýµmUþ_‰ˆ
\ No newline at end of file diff --git a/src/testing/test_exchange_api_overlapping_keys_bug.c b/src/testing/test_exchange_api_overlapping_keys_bug.c index 74039cce4..33b547a37 100644 --- a/src/testing/test_exchange_api_overlapping_keys_bug.c +++ b/src/testing/test_exchange_api_overlapping_keys_bug.c @@ -31,6 +31,7 @@ #include "taler_exchange_service.h" #include "taler_json_lib.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_bank_service.h" #include "taler_fakebank_lib.h" @@ -40,12 +41,12 @@ * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_exchange_api_keys_cherry_picking.conf" +static char *config_file; /** - * Exchange configuration data. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; +static struct TALER_TESTING_Credentials cred; /** @@ -59,26 +60,32 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command commands[] = { - TALER_TESTING_cmd_auditor_add ("add-auditor-OK", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1), - TALER_TESTING_cmd_check_keys ("first-download", - 1), - /* Causes GET /keys?last_denom_issue=0 */ - TALER_TESTING_cmd_check_keys_with_last_denom ("second-download", - 1, - "zero"), + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_get_exchange ("get-exchange-1", + cred.cfg, + "get-exchange", + true, + true), + TALER_TESTING_cmd_get_exchange ("get-exchange-2", + cred.cfg, + NULL, + true, + true), TALER_TESTING_cmd_end () }; + (void) cls; TALER_TESTING_run (is, commands); } @@ -88,40 +95,25 @@ int main (int argc, char *const *argv) { - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api-overlapping-keys-bug", - "DEBUG", NULL); - TALER_TESTING_cleanup_files (CONFIG_FILE); - /* @helpers. Run keyup, create tables, ... Note: it - * fetches the port number from config in order to see - * if it's available. */ - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (GNUNET_OK != - /* Set up event loop and reschedule context, plus - * start/stop the exchange. It calls TALER_TESTING_setup - * which creates the 'is' object. - */ - TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE)) - return 1; - break; - default: - GNUNET_break (0); - return 1; + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + GNUNET_asprintf (&config_file, + "test_exchange_api_keys_cherry_picking-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_exchange_api_revocation.c b/src/testing/test_exchange_api_revocation.c index 62be6a0e3..92e36a30a 100644 --- a/src/testing/test_exchange_api_revocation.c +++ b/src/testing/test_exchange_api_revocation.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014--2020 Taler Systems SA + Copyright (C) 2014--2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -29,6 +29,7 @@ #include "taler_exchange_service.h" #include "taler_json_lib.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_bank_service.h" #include "taler_fakebank_lib.h" @@ -38,17 +39,12 @@ * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_exchange_api.conf" +static char *config_file; /** - * Exchange configuration data. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; - -/** - * Bank configuration data. - */ -static struct TALER_TESTING_BankConfiguration bc; +static struct TALER_TESTING_Credentials cred; /** @@ -62,51 +58,55 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command revocation[] = { - TALER_TESTING_cmd_auditor_add ("add-auditor-OK", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1), + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), /** * Fill reserve with EUR:10.02, as withdraw fee is 1 ct per * config. */ TALER_TESTING_cmd_admin_add_incoming ("create-reserve-1", "EUR:10.02", - &bc.exchange_auth, - bc.user42_payto), + &cred.ba, + cred.user42_payto), TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1", "EUR:10.02", - bc.user42_payto, - bc.exchange_payto, + cred.user42_payto, + cred.exchange_payto, "create-reserve-1"), /** * Run wire-watch to trigger the reserve creation. */ - TALER_TESTING_cmd_exec_wirewatch ("wirewatch-4", - CONFIG_FILE), + TALER_TESTING_cmd_exec_wirewatch2 ("wirewatch-4", + config_file, + "exchange-account-2"), /* Withdraw a 5 EUR coin, at fee of 1 ct */ TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-1", "create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /* Withdraw another 5 EUR coin, at fee of 1 ct */ TALER_TESTING_cmd_withdraw_amount ("withdraw-revocation-coin-2", "create-reserve-1", "EUR:5", + 0, /* age restriction off */ MHD_HTTP_OK), /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full) * (merchant would receive EUR:0.99 due to 1 ct deposit fee) */// TALER_TESTING_cmd_deposit ("deposit-partial", "withdraw-revocation-coin-1", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", @@ -115,14 +115,15 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-full", "withdraw-revocation-coin-2", 0, - bc.user42_payto, + cred.user42_payto, "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK), /** * Melt SOME of the rest of the coin's value - * (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */ + * (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) + */ TALER_TESTING_cmd_melt ("refresh-melt-1", "withdraw-revocation-coin-1", MHD_HTTP_OK, @@ -134,64 +135,63 @@ run (void *cls, "refresh-melt-1", MHD_HTTP_OK), /* Try to recoup before it's allowed */ - TALER_TESTING_cmd_recoup ("recoup-not-allowed", - MHD_HTTP_GONE, - "refresh-reveal-1#0", - "refresh-melt-1", - NULL), + TALER_TESTING_cmd_recoup_refresh ("recoup-not-allowed", + MHD_HTTP_GONE, + "refresh-reveal-1#0", + "refresh-melt-1", + "EUR:0.1"), /* Make refreshed coin invalid */ TALER_TESTING_cmd_revoke ("revoke-2-EUR:5", MHD_HTTP_OK, "refresh-melt-1", - CONFIG_FILE), + config_file), /* Also make fully spent coin invalid (should be same denom) */ TALER_TESTING_cmd_revoke ("revoke-2-EUR:5", MHD_HTTP_OK, "withdraw-revocation-coin-2", - CONFIG_FILE), + config_file), /* Refund fully spent coin (which should fail) */ TALER_TESTING_cmd_recoup ("recoup-fully-spent", MHD_HTTP_CONFLICT, "withdraw-revocation-coin-2", - NULL, - NULL), + "EUR:0.1"), /* Refund coin to original coin */ - TALER_TESTING_cmd_recoup ("recoup-1a", - MHD_HTTP_OK, - "refresh-reveal-1#0", - "refresh-melt-1", - NULL), - TALER_TESTING_cmd_recoup ("recoup-1b", - MHD_HTTP_OK, - "refresh-reveal-1#1", - "refresh-melt-1", - NULL), - TALER_TESTING_cmd_recoup ("recoup-1c", - MHD_HTTP_OK, - "refresh-reveal-1#2", - "refresh-melt-1", - NULL), + TALER_TESTING_cmd_recoup_refresh ("recoup-1a", + MHD_HTTP_OK, + "refresh-reveal-1#0", + "refresh-melt-1", + "EUR:1"), + TALER_TESTING_cmd_recoup_refresh ("recoup-1b", + MHD_HTTP_OK, + "refresh-reveal-1#1", + "refresh-melt-1", + "EUR:1"), + TALER_TESTING_cmd_recoup_refresh ("recoup-1c", + MHD_HTTP_OK, + "refresh-reveal-1#2", + "refresh-melt-1", + "EUR:1"), /* Repeat recoup to test idempotency */ - TALER_TESTING_cmd_recoup ("recoup-1c", - MHD_HTTP_OK, - "refresh-reveal-1#2", - "refresh-melt-1", - NULL), - TALER_TESTING_cmd_recoup ("recoup-1c", - MHD_HTTP_OK, - "refresh-reveal-1#2", - "refresh-melt-1", - NULL), - TALER_TESTING_cmd_recoup ("recoup-1c", - MHD_HTTP_OK, - "refresh-reveal-1#2", - "refresh-melt-1", - NULL), - TALER_TESTING_cmd_recoup ("recoup-1c", - MHD_HTTP_OK, - "refresh-reveal-1#2", - "refresh-melt-1", - NULL), + TALER_TESTING_cmd_recoup_refresh ("recoup-1c", + MHD_HTTP_OK, + "refresh-reveal-1#2", + "refresh-melt-1", + "EUR:1"), + TALER_TESTING_cmd_recoup_refresh ("recoup-1c", + MHD_HTTP_OK, + "refresh-reveal-1#2", + "refresh-melt-1", + "EUR:1"), + TALER_TESTING_cmd_recoup_refresh ("recoup-1c", + MHD_HTTP_OK, + "refresh-reveal-1#2", + "refresh-melt-1", + "EUR:1"), + TALER_TESTING_cmd_recoup_refresh ("recoup-1c", + MHD_HTTP_OK, + "refresh-reveal-1#2", + "refresh-melt-1", + "EUR:1"), /* Now we have EUR:3.83 EUR back after 3x EUR:1 in recoups */ /* Melt original coin AGAIN, but only create one 0.1 EUR coin; This costs EUR:0.03 in refresh and EUR:01 in withdraw fees, @@ -211,24 +211,23 @@ run (void *cls, TALER_TESTING_cmd_revoke ("revoke-3-EUR:0.1", MHD_HTTP_OK, "refresh-reveal-2", - CONFIG_FILE), + config_file), /* Revoke also original coin denomination */ TALER_TESTING_cmd_revoke ("revoke-4-EUR:5", MHD_HTTP_OK, "withdraw-revocation-coin-1", - CONFIG_FILE), + config_file), /* Refund coin EUR:0.1 to original coin, creating zombie! */ - TALER_TESTING_cmd_recoup ("recoup-2", - MHD_HTTP_OK, - "refresh-reveal-2", - "refresh-melt-2", - NULL), + TALER_TESTING_cmd_recoup_refresh ("recoup-2", + MHD_HTTP_OK, + "refresh-reveal-2", + "refresh-melt-2", + "EUR:0.1"), /* Due to recoup, original coin is now at EUR:3.79 */ /* Refund original (now zombie) coin to reserve */ TALER_TESTING_cmd_recoup ("recoup-3", MHD_HTTP_OK, "withdraw-revocation-coin-1", - NULL, "EUR:3.79"), /* Check the money is back with the reserve */ TALER_TESTING_cmd_status ("recoup-reserve-status-1", @@ -238,9 +237,9 @@ run (void *cls, TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - revocation, - bc.exchange_auth.wire_gateway_url); + (void) cls; + TALER_TESTING_run (is, + revocation); } @@ -248,47 +247,25 @@ int main (int argc, char *const *argv) { - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api-revocation", - "INFO", - NULL); - /* Check fakebank port is available and get config */ - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (CONFIG_FILE, - "exchange-account-2", - &bc)) - return 77; - TALER_TESTING_cleanup_files (CONFIG_FILE); - /* @helpers. Run keyup, create tables, ... Note: it - * fetches the port number from config in order to see - * if it's available. */ - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (GNUNET_OK != - /* Set up event loop and reschedule context, plus - * start/stop the exchange. It calls TALER_TESTING_setup - * which creates the 'is' object. - */ - TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE)) - return 1; - break; - default: - GNUNET_break (0); - return 1; + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + GNUNET_asprintf (&config_file, + "test_exchange_api-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_exchange_api_twisted-cs.conf b/src/testing/test_exchange_api_twisted-cs.conf new file mode 100644 index 000000000..ae953e732 --- /dev/null +++ b/src/testing/test_exchange_api_twisted-cs.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +@INLINE@ test_exchange_api-cs.conf +@INLINE@ test_exchange_api-twisted.conf + diff --git a/src/testing/test_exchange_api_twisted-rsa.conf b/src/testing/test_exchange_api_twisted-rsa.conf new file mode 100644 index 000000000..3fd8f4ff1 --- /dev/null +++ b/src/testing/test_exchange_api_twisted-rsa.conf @@ -0,0 +1,4 @@ +# This file is in the public domain. +@INLINE@ test_exchange_api-rsa.conf +@INLINE@ test_exchange_api-twisted.conf + diff --git a/src/testing/test_exchange_api_twisted.c b/src/testing/test_exchange_api_twisted.c index 818b54deb..75ffe1f15 100644 --- a/src/testing/test_exchange_api_twisted.c +++ b/src/testing/test_exchange_api_twisted.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -29,6 +29,7 @@ #include "taler_exchange_service.h" #include "taler_json_lib.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_bank_service.h" #include "taler_fakebank_lib.h" @@ -40,22 +41,17 @@ * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_exchange_api_twisted.conf" +static char *config_file; /** - * (real) Twister URL. Used at startup time to check if it runs. - */ -static char *twister_url; - -/** - * Exchange configuration data. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; +static struct TALER_TESTING_Credentials cred; /** - * Bank configuration data. + * (real) Twister URL. Used at startup time to check if it runs. */ -static struct TALER_TESTING_BankConfiguration bc; +static char *twister_url; /** * Twister process. @@ -72,8 +68,9 @@ static struct GNUNET_OS_Process *twisterd; static struct TALER_TESTING_Command CMD_EXEC_WIREWATCH (const char *label) { - return TALER_TESTING_cmd_exec_wirewatch (label, - CONFIG_FILE); + return TALER_TESTING_cmd_exec_wirewatch2 (label, + config_file, + "exchange-account-2"); } @@ -91,8 +88,8 @@ CMD_TRANSFER_TO_EXCHANGE (const char *label, { return TALER_TESTING_cmd_admin_add_incoming (label, amount, - &bc.exchange_auth, - bc.user42_payto); + &cred.ba, + cred.user42_payto); } @@ -111,8 +108,9 @@ run (void *cls, * response from a refresh-reveal operation. */ struct TALER_TESTING_Command refresh_409_conflict[] = { - CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve", - "EUR:5.01"), + CMD_TRANSFER_TO_EXCHANGE ( + "refresh-create-reserve", + "EUR:5.01"), /** * Make previous command effective. */ @@ -120,33 +118,38 @@ run (void *cls, /** * Withdraw EUR:5. */ - TALER_TESTING_cmd_withdraw_amount ("refresh-withdraw-coin", - "refresh-create-reserve", - "EUR:5", - MHD_HTTP_OK), - TALER_TESTING_cmd_deposit ("refresh-deposit-partial", - "refresh-withdraw-coin", - 0, - bc.user42_payto, - "{\"items\":[{\"name\":\"ice cream\",\ - \"value\":\"EUR:1\"}]}", - GNUNET_TIME_UNIT_ZERO, - "EUR:1", - MHD_HTTP_OK), + TALER_TESTING_cmd_withdraw_amount ( + "refresh-withdraw-coin", + "refresh-create-reserve", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit ( + "refresh-deposit-partial", + "refresh-withdraw-coin", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), /** * Melt the rest of the coin's value * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ - TALER_TESTING_cmd_melt ("refresh-melt", - "refresh-withdraw-coin", - MHD_HTTP_OK, - NULL), + TALER_TESTING_cmd_melt ( + "refresh-melt", + "refresh-withdraw-coin", + MHD_HTTP_OK, + NULL), /* Trigger 409 Conflict. */ - TALER_TESTING_cmd_flip_upload ("flip-upload", - CONFIG_FILE, - "transfer_privs.0"), - TALER_TESTING_cmd_refresh_reveal ("refresh-(flipped-)reveal", - "refresh-melt", - MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_flip_upload ( + "flip-upload", + config_file, + "transfer_privs.0"), + TALER_TESTING_cmd_refresh_reveal ( + "refresh-(flipped-)reveal", + "refresh-melt", + MHD_HTTP_CONFLICT), TALER_TESTING_cmd_end () }; @@ -155,50 +158,53 @@ run (void *cls, * NOTE: not all CMDs actually need the twister, * so it may be better to move those into the "main" * lib test suite. - */struct TALER_TESTING_Command refund[] = { - CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r1", - "EUR:5.01"), + */ + struct TALER_TESTING_Command refund[] = { + CMD_TRANSFER_TO_EXCHANGE ( + "create-reserve-r1", + "EUR:5.01"), CMD_EXEC_WIREWATCH ("wirewatch-r1"), - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1", - "create-reserve-r1", - "EUR:5", - MHD_HTTP_OK), - TALER_TESTING_cmd_deposit ("deposit-refund-1", - "withdraw-coin-r1", - 0, - bc.user42_payto, - "{\"items\":[{\"name\":\"ice cream\"," - "\"value\":\"EUR:5\"}]}", - GNUNET_TIME_UNIT_MINUTES, - "EUR:5", - MHD_HTTP_OK), + TALER_TESTING_cmd_withdraw_amount ( + "withdraw-coin-r1", + "create-reserve-r1", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), + TALER_TESTING_cmd_deposit ( + "deposit-refund-1", + "withdraw-coin-r1", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:5\"}]}", + GNUNET_TIME_UNIT_MINUTES, + "EUR:5", + MHD_HTTP_OK), TALER_TESTING_cmd_refund ("refund-currency-mismatch", MHD_HTTP_BAD_REQUEST, "USD:5", "deposit-refund-1"), TALER_TESTING_cmd_flip_upload ("flip-upload", - CONFIG_FILE, + config_file, "merchant_sig"), TALER_TESTING_cmd_refund ("refund-bad-sig", MHD_HTTP_FORBIDDEN, "EUR:5", "deposit-refund-1"), - /* This next deposit CMD is only used to provide a * good merchant signature to the next (failing) refund * operations. */ - TALER_TESTING_cmd_deposit ("deposit-refund-to-fail", - "withdraw-coin-r1", - 0, /* coin index. */ - bc.user42_payto, - /* This parameter will make any comparison about - h_contract_terms fail, when /refund will be handled. - So in other words, this is h_contract mismatch. */ - "{\"items\":[{\"name\":\"ice skate\"," - "\"value\":\"EUR:5\"}]}", - GNUNET_TIME_UNIT_MINUTES, - "EUR:5", - MHD_HTTP_CONFLICT), + TALER_TESTING_cmd_deposit ( + "deposit-refund-to-fail", + "withdraw-coin-r1", + 0, /* coin index. */ + cred.user42_payto, + /* This parameter will make any comparison about + h_contract_terms fail, when /refund will be handled. + So in other words, this is h_contract mismatch. */ + "{\"items\":[{\"name\":\"ice skate\",\"value\":\"EUR:5\"}]}", + GNUNET_TIME_UNIT_MINUTES, + "EUR:5", + MHD_HTTP_CONFLICT), TALER_TESTING_cmd_refund ("refund-deposit-not-found", MHD_HTTP_NOT_FOUND, "EUR:5", @@ -210,15 +216,17 @@ run (void *cls, TALER_TESTING_cmd_end () }; +#if 0 /** * Test that we don't get errors when the keys from the exchange * are out of date. */ struct TALER_TESTING_Command expired_keys[] = { - TALER_TESTING_cmd_modify_header_dl ("modify-expiration", - CONFIG_FILE, - MHD_HTTP_HEADER_EXPIRES, - "Wed, 19 Jan 586524 08:01:49 GMT"), + TALER_TESTING_cmd_modify_header_dl ( + "modify-expiration", + config_file, + MHD_HTTP_HEADER_EXPIRES, + "Wed, 19 Jan 586524 08:01:49 GMT"), TALER_TESTING_cmd_check_keys_pull_all_keys ( "check-keys-expiration-0", 2), @@ -228,41 +236,52 @@ run (void *cls, CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r2", "EUR:55.01"), CMD_EXEC_WIREWATCH ("wirewatch-r2"), - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r2", - "create-reserve-r2", - "EUR:5", - MHD_HTTP_OK), + TALER_TESTING_cmd_withdraw_amount ( + "withdraw-coin-r2", + "create-reserve-r2", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), TALER_TESTING_cmd_end () }; +#endif struct TALER_TESTING_Command commands[] = { - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1), - TALER_TESTING_cmd_batch ("refresh-reveal-409-conflict", - refresh_409_conflict), - TALER_TESTING_cmd_batch ("refund", - refund), + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_batch ( + "refresh-reveal-409-conflict", + refresh_409_conflict), + TALER_TESTING_cmd_batch ( + "refund", + refund), +#if 0 TALER_TESTING_cmd_batch ("expired-keys", expired_keys), +#endif TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - commands, - bc.exchange_auth.wire_gateway_url); + (void) cls; + TALER_TESTING_run (is, + commands); } /** * Kill, wait, and destroy convenience function. * - * @param process process to purge. + * @param[in] process process to purge. */ static void purge_process (struct GNUNET_OS_Process *process) @@ -280,47 +299,35 @@ main (int argc, { int ret; - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api-twisted", - "DEBUG", - NULL); - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (CONFIG_FILE, - "exchange-account-2", - &bc)) - return 77; - if (NULL == (twister_url = TALER_TWISTER_prepare_twister - (CONFIG_FILE))) - return 77; - TALER_TESTING_cleanup_files (CONFIG_FILE); - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (NULL == (twisterd = TALER_TWISTER_run_twister (CONFIG_FILE))) - return 77; - ret = TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE); - purge_process (twisterd); - GNUNET_free (twister_url); + char *cipher; - if (GNUNET_OK != ret) - return 1; - break; - default: - GNUNET_break (0); - return 1; + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + GNUNET_asprintf (&config_file, + "test_exchange_api_twisted-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + /* FIXME: introduce commands for twister! */ + twister_url = TALER_TWISTER_prepare_twister (config_file); + if (NULL == twister_url) + return 77; + twisterd = TALER_TWISTER_run_twister (config_file); + if (NULL == twisterd) + return 77; + ret = TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); + purge_process (twisterd); + GNUNET_free (twister_url); + return ret; } diff --git a/src/testing/test_exchange_api_twisted.conf b/src/testing/test_exchange_api_twisted.conf deleted file mode 100644 index a41cfd43c..000000000 --- a/src/testing/test_exchange_api_twisted.conf +++ /dev/null @@ -1,150 +0,0 @@ -# This file is in the public domain. -[PATHS] -# Persistent data storage for the testcase -TALER_TEST_HOME = test_exchange_api_home/ -TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ - -[taler-exchange-secmod-rsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days - -[taler-exchange-secmod-eddsa] -# Reduce from 1 year to speed up test -LOOKAHEAD_SIGN = 24 days -# Reduce from 12 weeks to ensure we have multiple -DURATION = 14 days - -[taler] -# Currency supported by the exchange (can only be one) -CURRENCY = EUR -CURRENCY_ROUND_UNIT = EUR:0.01 - -[exchange] -# HTTP port the exchange listens to -PORT = 8081 - -# Master public key used to sign the exchange's various keys -MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG - -# How to access our database -DB = postgres - -# Base URL of the exchange ('S PROXY). This URL is where the -# twister listens at, so that it will be able to get all the -# connection addressed to the exchange. In fact, the presence -# of the twister is 100% transparent to the test case, as it -# only seeks the exchange/BASE_URL URL to connect to the exchange. -BASE_URL = "http://localhost:8888/" - -[exchangedb-postgres] -CONFIG = "postgres:///talercheck" - -[auditor] -BASE_URL = "http://localhost:8083/" -PORT = 8083 - -[auditordb-postgres] -CONFIG = "postgres:///talercheck" - -[exchange-account-1] -# What is the URL of our account? -PAYTO_URI = "payto://x-taler-bank/localhost/42" - -[exchange-accountcredentials-1] -WIRE_GATEWAY_URL = "http://localhost:9081/42/" -WIRE_GATEWAY_AUTH_METHOD = NONE - -[exchange-account-2] -PAYTO_URI = payto://x-taler-bank/localhost/2 -ENABLE_DEBIT = YES -ENABLE_CREDIT = YES - -[exchange-accountcredentials-2] -WIRE_GATEWAY_URL = "http://localhost:8082/2/" -WIRE_GATEWAY_AUTH_METHOD = BASIC -USERNAME = user -PASSWORD = pass - -[bank] -HTTP_PORT = 8082 - -[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 - - -[coin_eur_ct_1] -value = EUR:0.01 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.00 -fee_deposit = EUR:0.00 -fee_refresh = EUR:0.01 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_ct_10] -value = EUR:0.10 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_1] -value = EUR:1 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_5] -value = EUR:5 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 - -[coin_eur_10] -value = EUR:10 -duration_withdraw = 7 days -duration_spend = 2 years -duration_legal = 3 years -fee_withdraw = EUR:0.01 -fee_deposit = EUR:0.01 -fee_refresh = EUR:0.03 -fee_refund = EUR:0.01 -rsa_keysize = 1024 diff --git a/src/testing/test_exchange_management_api.c b/src/testing/test_exchange_management_api.c index 0cf968237..7cce61b55 100644 --- a/src/testing/test_exchange_management_api.c +++ b/src/testing/test_exchange_management_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -25,6 +25,7 @@ #include "taler_util.h" #include "taler_exchange_service.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> #include <microhttpd.h> #include "taler_testing_lib.h" @@ -32,18 +33,12 @@ * Configuration file we use. One (big) configuration is used * for the various components for this test. */ -#define CONFIG_FILE "test_exchange_api.conf" - +static char *config_file; /** - * Exchange configuration data. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; - -/** - * Bank configuration data. - */ -static struct TALER_TESTING_BankConfiguration bc; +static struct TALER_TESTING_Credentials cred; /** @@ -57,10 +52,21 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command commands[] = { - /* this currently fails, because the - auditor is already added by the test setup logic */ - TALER_TESTING_cmd_auditor_del ("del-auditor-NOT-FOUND", - MHD_HTTP_NOT_FOUND, + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-u", "exchange-account-2", + "-ae", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_get_auditor ("get-auditor", + cred.cfg, + true), + TALER_TESTING_cmd_auditor_del ("del-auditor-FROM-SETUP", + MHD_HTTP_NO_CONTENT, false), TALER_TESTING_cmd_auditor_add ("add-auditor-BAD-SIG", MHD_HTTP_FORBIDDEN, @@ -111,43 +117,51 @@ run (void *cls, MHD_HTTP_NO_CONTENT, false), TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/42", + "payto://x-taler-bank/localhost/42?receiver-name=42", MHD_HTTP_NO_CONTENT, false), TALER_TESTING_cmd_wire_add ("add-wire-account-idempotent", - "payto://x-taler-bank/localhost/42", + "payto://x-taler-bank/localhost/42?receiver-name=42", MHD_HTTP_NO_CONTENT, false), TALER_TESTING_cmd_wire_add ("add-wire-account-another", - "payto://x-taler-bank/localhost/43", + "payto://x-taler-bank/localhost/43?receiver-name=43", MHD_HTTP_NO_CONTENT, false), TALER_TESTING_cmd_wire_add ("add-wire-account-bad-signature", - "payto://x-taler-bank/localhost/44", + "payto://x-taler-bank/localhost/44?receiver-name=44", MHD_HTTP_FORBIDDEN, true), TALER_TESTING_cmd_wire_del ("del-wire-account-not-found", - "payto://x-taler-bank/localhost/44", + "payto://x-taler-bank/localhost/44?receiver-name=44", MHD_HTTP_NOT_FOUND, false), TALER_TESTING_cmd_wire_del ("del-wire-account-bad-signature", - "payto://x-taler-bank/localhost/43", + "payto://x-taler-bank/localhost/43?receiver-name=43", MHD_HTTP_FORBIDDEN, true), TALER_TESTING_cmd_wire_del ("del-wire-account-ok", - "payto://x-taler-bank/localhost/43", + "payto://x-taler-bank/localhost/43?receiver-name=43", MHD_HTTP_NO_CONTENT, false), TALER_TESTING_cmd_exec_offline_sign_keys ("download-future-keys", - CONFIG_FILE), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1), + config_file), + TALER_TESTING_cmd_get_exchange ("get-exchange-1", + cred.cfg, + "get-exchange", + true, + true), + TALER_TESTING_cmd_get_exchange ("get-exchange-2", + cred.cfg, + NULL, + true, + true), TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - commands, - bc.exchange_auth.wire_gateway_url); + (void) cls; + TALER_TESTING_run (is, + commands); } @@ -155,47 +169,25 @@ int main (int argc, char *const *argv) { - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-management-api", - "INFO", - NULL); - /* Check fakebank port is available and get config */ - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (CONFIG_FILE, - "exchange-account-2", - &bc)) - return 77; - TALER_TESTING_cleanup_files (CONFIG_FILE); - /* @helpers. Create tables, ... Note: it - * fetches the port number from config in order to see - * if it's available. */ - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - GNUNET_YES, /* reset DB? */ - &ec)) + (void) argc; { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - case GNUNET_OK: - if (GNUNET_OK != - /* Set up event loop and reschedule context, plus - * start/stop the exchange. It calls TALER_TESTING_setup - * which creates the 'is' object. - */ - TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE)) - return 1; - break; - default: - GNUNET_break (0); - return 1; + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + GNUNET_asprintf (&config_file, + "test_exchange_api-%s.conf", + cipher); + GNUNET_free (cipher); } - return 0; + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c new file mode 100644 index 000000000..093730ff2 --- /dev/null +++ b/src/testing/test_exchange_p2p.c @@ -0,0 +1,557 @@ +/* + This file is part of TALER + Copyright (C) 2014--2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/test_exchange_p2p.c + * @brief testcase to test exchange's P2P payments + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_attributes.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_testing_lib.h> +#include <microhttpd.h> +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_testing_lib.h" +#include "taler_extensions.h" + +/** + * Configuration file we use. One (big) configuration is used + * for the various components for this test. + */ +static char *config_file; + +/** + * Our credentials. + */ +struct TALER_TESTING_Credentials cred; + +/** + * Some tests behave differently when using CS as we cannot + * reuse the coin private key for different denominations + * due to the derivation of it with the /csr values. Hence + * some tests behave differently in CS mode, hence this + * flag. + */ +static bool uses_cs; + +/** + * Execute the taler-exchange-wirewatch command with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_WIREWATCH(label) \ + TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, "exchange-account-2") + +/** + * Execute the taler-exchange-aggregator, closer and transfer commands with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_AGGREGATOR(label) \ + TALER_TESTING_cmd_sleep ("sleep-before-aggregator", 2), \ + TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \ + TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file) + + +/** + * Run wire transfer of funds from some user's account to the + * exchange. + * + * @param label label to use for the command. + * @param amount amount to transfer, i.e. "EUR:1" + */ +#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ + TALER_TESTING_cmd_admin_add_incoming (label, amount, \ + &cred.ba, \ + cred.user42_payto) + +/** + * Main function that will tell the interpreter what commands to + * run. + * + * @param cls closure + * @param is interpreter we use to run commands + */ +static void +run (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + /** + * Test withdrawal plus spending. + */ + struct TALER_TESTING_Command withdraw[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1", + "EUR:5.04"), + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-2", + "EUR:5.01"), + TALER_TESTING_cmd_reserve_poll ("poll-reserve-1", + "create-reserve-1", + "EUR:5.04", + GNUNET_TIME_UNIT_MINUTES, + MHD_HTTP_OK), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1", + "EUR:5.04", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-1"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-2", + "EUR:5.01", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-2"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-1"), + TALER_TESTING_cmd_reserve_poll_finish ("finish-poll-reserve-1", + GNUNET_TIME_UNIT_SECONDS, + "poll-reserve-1"), + /** + * Withdraw EUR:5. + */ + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", + "create-reserve-1", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), + /** + * Check the reserve is depleted. + */ + TALER_TESTING_cmd_status ("status-1", + "create-reserve-1", + "EUR:0.03", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command push[] = { + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit-for-delete", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "withdraw-coin-1", + "EUR:1.01", + NULL), + TALER_TESTING_cmd_purse_delete ( + "purse-with-deposit-delete", + MHD_HTTP_NO_CONTENT, + "purse-with-deposit-for-delete"), + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit", + MHD_HTTP_OK, + "{\"amount\":\"EUR:0.99\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "withdraw-coin-1", + "EUR:1.00", + NULL), + TALER_TESTING_cmd_purse_poll ( + "push-poll-purse-before-merge", + MHD_HTTP_OK, + "purse-with-deposit", + "EUR:0.99", + true, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_contract_get ( + "push-get-contract", + MHD_HTTP_OK, + true, /* for merge */ + "purse-with-deposit"), + TALER_TESTING_cmd_purse_merge ( + "purse-merge-into-reserve", + MHD_HTTP_OK, + "push-get-contract", + "create-reserve-1"), + TALER_TESTING_cmd_purse_poll_finish ( + "push-merge-purse-poll-finish", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 5), + "push-poll-purse-before-merge"), + TALER_TESTING_cmd_status ( + "push-check-post-merge-reserve-balance-get", + "create-reserve-1", + "EUR:1.02", + MHD_HTTP_OK), + /* POST history doesn't yet support P2P transfers */ + TALER_TESTING_cmd_reserve_history ( + "push-check-post-merge-reserve-balance-post", + "create-reserve-1", + "EUR:1.02", + MHD_HTTP_OK), + /* Test conflicting merge */ + TALER_TESTING_cmd_purse_merge ( + "purse-merge-into-reserve", + MHD_HTTP_CONFLICT, + "push-get-contract", + "create-reserve-2"), + + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command pull[] = { + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + true /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "create-reserve-1"), + TALER_TESTING_cmd_contract_get ( + "pull-get-contract", + MHD_HTTP_OK, + false, /* for deposit */ + "purse-create-with-reserve"), + TALER_TESTING_cmd_purse_poll ( + "pull-poll-purse-before-deposit", + MHD_HTTP_OK, + "purse-create-with-reserve", + "EUR:1", + false, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_purse_deposit_coins ( + "purse-deposit-coins", + MHD_HTTP_OK, + 0 /* min age */, + "purse-create-with-reserve", + "withdraw-coin-1", + "EUR:1.01", + NULL), + TALER_TESTING_cmd_purse_poll_finish ( + "pull-deposit-purse-poll-finish", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 5), + "pull-poll-purse-before-deposit"), + TALER_TESTING_cmd_status ( + "pull-check-post-merge-reserve-balance-get", + "create-reserve-1", + "EUR:2.02", + MHD_HTTP_OK), + TALER_TESTING_cmd_reserve_history ( + "push-check-post-merge-reserve-balance-post", + "create-reserve-1", + "EUR:2.02", + MHD_HTTP_OK), + TALER_TESTING_cmd_purse_deposit_coins ( + "purse-deposit-coins-idempotent", + MHD_HTTP_OK, + 0 /* min age */, + "purse-create-with-reserve", + "withdraw-coin-1", + "EUR:1.01", + NULL), + /* create 2nd purse for a deposit conflict */ + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve-2", + MHD_HTTP_OK, + "{\"amount\":\"EUR:4\",\"summary\":\"beer\"}", + true /* upload contract */, + true /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "create-reserve-1"), + TALER_TESTING_cmd_purse_deposit_coins ( + "purse-deposit-coins-conflict", + MHD_HTTP_CONFLICT, + 0 /* min age */, + "purse-create-with-reserve-2", + "withdraw-coin-1", + "EUR:4.01", + NULL), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command expire[] = { + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve-expire", + MHD_HTTP_OK, + "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", + true /* upload contract */, + true /* pay purse fee */, + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 1), /* expiration */ + "create-reserve-1"), + TALER_TESTING_cmd_purse_poll ( + "pull-poll-purse-before-expire", + MHD_HTTP_GONE, + "purse-create-with-reserve-expire", + "EUR:1", + false, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit-expire", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 1), /* expiration */ + "withdraw-coin-1", + "EUR:1.01", + NULL), + TALER_TESTING_cmd_purse_poll ( + "push-poll-purse-before-expire", + MHD_HTTP_GONE, + "purse-with-deposit-expire", + "EUR:1", + true, + GNUNET_TIME_UNIT_MINUTES), + /* This should fail, as too much of the coin + is already spend / in a purse */ + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit-overspending", + MHD_HTTP_CONFLICT, + "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 1), /* expiration */ + "withdraw-coin-1", + "EUR:2.01", + NULL), + TALER_TESTING_cmd_sleep ("sleep", + 2 /* seconds */), + TALER_TESTING_cmd_exec_expire ("exec-expire", + config_file), + TALER_TESTING_cmd_purse_poll_finish ( + "push-merge-purse-poll-finish-expire", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 15), + "push-poll-purse-before-expire"), + TALER_TESTING_cmd_purse_poll_finish ( + "pull-deposit-purse-poll-expire-finish", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 15), + "pull-poll-purse-before-expire"), + /* coin was refunded, so now this should be OK */ + /* This should fail, as too much of the coin + is already spend / in a purse */ + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit-refunded", + MHD_HTTP_OK, + "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 1), /* expiration */ + "withdraw-coin-1", + "EUR:2.01", + NULL), + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command reserves[] = { + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-100", + "EUR:1.04"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-100", + "EUR:1.04", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-100"), + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-101", + "EUR:1.04"), + TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-101", + "EUR:1.04", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-101"), + CMD_EXEC_WIREWATCH ("wirewatch-100"), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-100", + "create-reserve-100", + "EUR:1", + 0, /* age restriction off */ + MHD_HTTP_OK), + TALER_TESTING_cmd_reserve_open ("reserve-open-101-fail", + "create-reserve-101", + "EUR:0", + GNUNET_TIME_UNIT_YEARS, + 5, /* min purses */ + MHD_HTTP_PAYMENT_REQUIRED, + NULL, + NULL), + TALER_TESTING_cmd_reserve_open ("reserve-open-101-ok-a", + "create-reserve-101", + "EUR:0.01", + GNUNET_TIME_UNIT_MONTHS, + 1, /* min purses */ + MHD_HTTP_OK, + NULL, + NULL), + TALER_TESTING_cmd_status ("status-101-open-paid", + "create-reserve-101", + "EUR:1.03", + MHD_HTTP_OK), + TALER_TESTING_cmd_reserve_open ("reserve-open-101-ok-b", + "create-reserve-101", + "EUR:0", + GNUNET_TIME_UNIT_MONTHS, + 2, /* min purses */ + MHD_HTTP_OK, + "withdraw-coin-100", + "EUR:0.03", /* 0.02 for the reserve open, 0.01 for deposit fee */ + NULL, + NULL), + /* Use purse creation with purse quota here */ + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve-101-a", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + false /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "create-reserve-101"), + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve-101-b", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + false /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "create-reserve-101"), + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve-101-fail", + MHD_HTTP_CONFLICT, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + false /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "create-reserve-101"), + TALER_TESTING_cmd_reserve_get_attestable ("reserve-101-attestable", + "create-reserve-101", + MHD_HTTP_NOT_FOUND, + NULL), + TALER_TESTING_cmd_reserve_get_attestable ("reserve-101-attest", + "create-reserve-101", + MHD_HTTP_NOT_FOUND, + "nx-attribute-name", + NULL), + TALER_TESTING_cmd_oauth ("start-oauth-service", + 6666), + TALER_TESTING_cmd_reserve_close ("reserve-101-close-kyc", + "create-reserve-101", + /* 42b => not to origin */ + "payto://x-taler-bank/localhost/42?receiver-name=42b", + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + + TALER_TESTING_cmd_check_kyc_get ("check-kyc-close-pending", + "reserve-101-close-kyc", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-close-kyc", + "reserve-101-close-kyc", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-close-ok", + "reserve-101-close-kyc", + MHD_HTTP_NO_CONTENT), + /* Now it should pass */ + TALER_TESTING_cmd_reserve_close ("reserve-101-close", + "create-reserve-101", + /* 42b => not to origin */ + "payto://x-taler-bank/localhost/42?receiver-name=42b", + MHD_HTTP_OK), + TALER_TESTING_cmd_exec_closer ("close-reserves-101", + config_file, + "EUR:1.02", + "EUR:0.01", + "create-reserve-101"), + TALER_TESTING_cmd_exec_transfer ("close-reserves-101-transfer", + config_file), + TALER_TESTING_cmd_status ("reserve-101-closed-status", + "create-reserve-101", + "EUR:0", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + config_file, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_batch ("withdraw", + withdraw), + TALER_TESTING_cmd_batch ("push", + push), + TALER_TESTING_cmd_batch ("pull", + pull), + TALER_TESTING_cmd_batch ("expire", + expire), + TALER_TESTING_cmd_batch ("reserves", + reserves), + /* End the suite. */ + TALER_TESTING_cmd_end () + }; + + (void) cls; + TALER_TESTING_run (is, + commands); +} + + +int +main (int argc, + char *const *argv) +{ + (void) argc; + { + char *cipher; + + cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_assert (NULL != cipher); + uses_cs = (0 == strcmp (cipher, "cs")); + GNUNET_asprintf (&config_file, + "test_exchange_api-%s.conf", + cipher); + GNUNET_free (cipher); + } + return TALER_TESTING_main (argv, + "INFO", + config_file, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); +} + + +/* end of test_exchange_p2p.c */ diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c new file mode 100644 index 000000000..0844c5818 --- /dev/null +++ b/src/testing/test_kyc_api.c @@ -0,0 +1,581 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/test_kyc_api.c + * @brief testcase to test the KYC processes + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_attributes.h" +#include "taler_signatures.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_util_lib.h> +#include <microhttpd.h> +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_testing_lib.h" + + +/** + * Configuration file we use. One (big) configuration is used + * for the various components for this test. + */ +#define CONFIG_FILE "test_kyc_api.conf" + +/** + * Our credentials. + */ +struct TALER_TESTING_Credentials cred; + + +/** + * Execute the taler-exchange-wirewatch command with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_WIREWATCH(label) \ + TALER_TESTING_cmd_exec_wirewatch2 (label, CONFIG_FILE, "exchange-account-2") + +/** + * Execute the taler-exchange-aggregator, closer and transfer commands with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_AGGREGATOR(label) \ + TALER_TESTING_cmd_sleep (label "-sleep", 1), \ + TALER_TESTING_cmd_exec_aggregator_with_kyc (label, CONFIG_FILE), \ + TALER_TESTING_cmd_exec_transfer (label, CONFIG_FILE) + +/** + * Run wire transfer of funds from some user's account to the + * exchange. + * + * @param label label to use for the command. + * @param amount amount to transfer, i.e. "EUR:1" + */ +#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ + TALER_TESTING_cmd_admin_add_incoming (label, amount, \ + &cred.ba, \ + cred.user42_payto) + +/** + * 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 withdraw[] = { + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1", + "EUR:15.02"), + TALER_TESTING_cmd_check_bank_admin_transfer ( + "check-create-reserve-1", + "EUR:15.02", + cred.user42_payto, + cred.exchange_payto, + "create-reserve-1"), + CMD_EXEC_WIREWATCH ("wirewatch-1"), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-no-kyc", + "create-reserve-1", + "EUR:10", + 0, /* age restriction off */ + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", + "create-reserve-1", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + /** + * Test withdraw with KYC. + */ + struct TALER_TESTING_Command withdraw_kyc[] = { + CMD_EXEC_WIREWATCH ("wirewatch-1"), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-lacking-kyc", + "create-reserve-1", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-withdraw", + "withdraw-coin-1-lacking-kyc", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc", + "withdraw-coin-1-lacking-kyc", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc", + "create-reserve-1", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), + /* Attestations above are bound to the originating *bank* account, + not to the reserve (!). Hence, they are NOT found here! */ + TALER_TESTING_cmd_reserve_get_attestable ("reserve-get-attestable", + "create-reserve-1", + MHD_HTTP_NOT_FOUND, + NULL), + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command spend[] = { + TALER_TESTING_cmd_deposit ( + "deposit-simple", + "withdraw-coin-1", + 0, + cred.user43_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:5", + MHD_HTTP_OK), + TALER_TESTING_cmd_track_transaction ( + "track-deposit", + "deposit-simple", + 0, + MHD_HTTP_ACCEPTED, + NULL), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command track[] = { + CMD_EXEC_AGGREGATOR ("run-aggregator-before-kyc"), + TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-no-kyc"), + TALER_TESTING_cmd_track_transaction ( + "track-deposit-kyc-ready", + "deposit-simple", + 0, + MHD_HTTP_ACCEPTED, + NULL), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-deposit", + "track-deposit-kyc-ready", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-no-service", + "track-deposit-kyc-ready", + "kyc-provider-test-oauth2", + "bad", + MHD_HTTP_BAD_GATEWAY), + TALER_TESTING_cmd_oauth ("start-oauth-service", + 6666), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-fail", + "track-deposit-kyc-ready", + "kyc-provider-test-oauth2", + "bad", + MHD_HTTP_FORBIDDEN), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-deposit-again", + "track-deposit-kyc-ready", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-pass", + "track-deposit-kyc-ready", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + CMD_EXEC_AGGREGATOR ("run-aggregator-after-kyc"), + TALER_TESTING_cmd_check_bank_transfer ( + "check_bank_transfer-499c", + cred.exchange_url, + "EUR:4.98", + cred.exchange_payto, + cred.user43_payto), + TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command wallet_kyc[] = { + TALER_TESTING_cmd_wallet_kyc_get ("wallet-kyc-fail", + NULL, + "EUR:1000000", + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-wallet", + "wallet-kyc-fail", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-wallet-kyc", + "wallet-kyc-fail", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + TALER_TESTING_cmd_check_kyc_get ("wallet-kyc-check", + "wallet-kyc-fail", + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_reserve_get_attestable ("wallet-get-attestable", + "wallet-kyc-fail", + MHD_HTTP_OK, + TALER_ATTRIBUTE_FULL_NAME, + NULL), + TALER_TESTING_cmd_reserve_attest ("wallet-get-attest", + "wallet-kyc-fail", + MHD_HTTP_OK, + TALER_ATTRIBUTE_FULL_NAME, + NULL), + TALER_TESTING_cmd_end () + }; + + /** + * Test withdrawal for P2P + */ + struct TALER_TESTING_Command p2p_withdraw[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("p2p_create-reserve-1", + "EUR:5.04"), + CMD_TRANSFER_TO_EXCHANGE ("p2p_create-reserve-2", + "EUR:5.01"), + CMD_TRANSFER_TO_EXCHANGE ("p2p_create-reserve-3", + "EUR:0.03"), + TALER_TESTING_cmd_reserve_poll ("p2p_poll-reserve-1", + "p2p_create-reserve-1", + "EUR:5.04", + GNUNET_TIME_UNIT_MINUTES, + MHD_HTTP_OK), + TALER_TESTING_cmd_check_bank_admin_transfer ("p2p_check-create-reserve-1", + "EUR:5.04", + cred.user42_payto, + cred.exchange_payto, + "p2p_create-reserve-1"), + TALER_TESTING_cmd_check_bank_admin_transfer ("p2p_check-create-reserve-2", + "EUR:5.01", + cred.user42_payto, + cred.exchange_payto, + "p2p_create-reserve-2"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("p2p_wirewatch-1"), + TALER_TESTING_cmd_reserve_poll_finish ("p2p_finish-poll-reserve-1", + GNUNET_TIME_UNIT_SECONDS, + "p2p_poll-reserve-1"), + /** + * Withdraw EUR:5. + */ + TALER_TESTING_cmd_withdraw_amount ("p2p_withdraw-coin-1", + "p2p_create-reserve-1", + "EUR:5", + 0, /* age restriction off */ + MHD_HTTP_OK), + /** + * Check the reserve is depleted. + */ + TALER_TESTING_cmd_status ("p2p_status-1", + "p2p_create-reserve-1", + "EUR:0.03", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command push[] = { + TALER_TESTING_cmd_purse_create_with_deposit ( + "purse-with-deposit", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true, /* upload contract */ + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "p2p_withdraw-coin-1", + "EUR:1.01", + NULL), + TALER_TESTING_cmd_coin_history ("coin-history-purse-with-deposit", + "p2p_withdraw-coin-1#0", + "EUR:3.99", + MHD_HTTP_OK), + TALER_TESTING_cmd_purse_poll ( + "push-poll-purse-before-merge", + MHD_HTTP_OK, + "purse-with-deposit", + "EUR:1", + true, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_contract_get ( + "push-get-contract", + MHD_HTTP_OK, + true, /* for merge */ + "purse-with-deposit"), + TALER_TESTING_cmd_purse_merge ( + "purse-merge-into-reserve", + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, + "push-get-contract", + "p2p_create-reserve-1"), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-purse-merge", + "purse-merge-into-reserve", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("p2p_proof-kyc", + "purse-merge-into-reserve", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + TALER_TESTING_cmd_purse_merge ( + "purse-merge-into-reserve", + MHD_HTTP_OK, + "push-get-contract", + "p2p_create-reserve-1"), + TALER_TESTING_cmd_purse_poll_finish ( + "push-merge-purse-poll-finish", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 5), + "push-poll-purse-before-merge"), + TALER_TESTING_cmd_status ( + "push-check-post-merge-reserve-balance-get", + "p2p_create-reserve-1", + "EUR:1.03", + MHD_HTTP_OK), + TALER_TESTING_cmd_reserve_history ( + "push-check-post-merge-reserve-balance-post", + "p2p_create-reserve-1", + "EUR:1.03", + MHD_HTTP_OK), + + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command pull[] = { + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve", + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + true /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "p2p_create-reserve-3"), + TALER_TESTING_cmd_check_kyc_get ("check-kyc-purse-create", + "purse-create-with-reserve", + MHD_HTTP_ACCEPTED), + TALER_TESTING_cmd_proof_kyc_oauth2 ("p2p_proof-kyc-pull", + "purse-create-with-reserve", + "kyc-provider-test-oauth2", + "pass", + MHD_HTTP_SEE_OTHER), + TALER_TESTING_cmd_purse_create_with_reserve ( + "purse-create-with-reserve", + MHD_HTTP_OK, + "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", + true /* upload contract */, + true /* pay purse fee */, + GNUNET_TIME_UNIT_MINUTES, /* expiration */ + "p2p_create-reserve-3"), + TALER_TESTING_cmd_contract_get ( + "pull-get-contract", + MHD_HTTP_OK, + false, /* for deposit */ + "purse-create-with-reserve"), + TALER_TESTING_cmd_purse_poll ( + "pull-poll-purse-before-deposit", + MHD_HTTP_OK, + "purse-create-with-reserve", + "EUR:1", + false, + GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_purse_deposit_coins ( + "purse-deposit-coins", + MHD_HTTP_OK, + 0 /* min age */, + "purse-create-with-reserve", + "p2p_withdraw-coin-1", + "EUR:1.01", + NULL), + TALER_TESTING_cmd_coin_history ("coin-history-purse-pull-deposit", + "p2p_withdraw-coin-1#0", + "EUR:2.98", + MHD_HTTP_OK), + TALER_TESTING_cmd_purse_poll_finish ( + "pull-deposit-purse-poll-finish", + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + 5), + "pull-poll-purse-before-deposit"), + TALER_TESTING_cmd_status ( + "pull-check-post-merge-reserve-balance-get-2", + "p2p_create-reserve-3", + "EUR:1.03", + MHD_HTTP_OK), + TALER_TESTING_cmd_reserve_history ( + "push-check-post-merge-reserve-balance-post-2", + "p2p_create-reserve-3", + "EUR:1.03", + MHD_HTTP_OK), + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command aml[] = { + /* Trigger something upon which an AML officer could act */ + TALER_TESTING_cmd_wallet_kyc_get ("wallet-trigger-kyc-for-aml", + NULL, + "EUR:1000", + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1", + NULL, + "Peter Falk", + true, + false), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-none-normal", + "create-aml-officer-1", + TALER_AML_NORMAL, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-none-pending", + "create-aml-officer-1", + TALER_AML_PENDING, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-none-frozen", + "create-aml-officer-1", + TALER_AML_FROZEN, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_sleep ("sleep-1a", + 1), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1-disable", + "create-aml-officer-1", + "Peter Falk", + true, + true), + /* Test that we are not allowed to take AML decisions as our + AML staff account is on read-only */ + TALER_TESTING_cmd_take_aml_decision ("aml-decide-while-disabled", + "create-aml-officer-1", + "wallet-trigger-kyc-for-aml", + "EUR:10000", + "party time", + TALER_AML_NORMAL, + NULL, + MHD_HTTP_FORBIDDEN), + /* Check that no decision was taken, but that we are allowed + to read this information */ + TALER_TESTING_cmd_check_aml_decision ("check-aml-decision-empty", + "create-aml-officer-1", + "aml-decide-while-disabled", + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_sleep ("sleep-1b", + 1), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1-enable", + "create-aml-officer-1", + "Peter Falk", + true, + false), + TALER_TESTING_cmd_take_aml_decision ("aml-decide", + "create-aml-officer-1", + "wallet-trigger-kyc-for-aml", + "EUR:10000", + "party time", + TALER_AML_NORMAL, + NULL, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-one-normal", + "create-aml-officer-1", + TALER_AML_NORMAL, + MHD_HTTP_OK), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-zero-frozen", + "create-aml-officer-1", + TALER_AML_FROZEN, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_check_aml_decision ("check-aml-decision", + "create-aml-officer-1", + "aml-decide", + MHD_HTTP_OK), + TALER_TESTING_cmd_sleep ("sleep-1c", + 1), + TALER_TESTING_cmd_take_aml_decision ("aml-decide-freeze", + "create-aml-officer-1", + "wallet-trigger-kyc-for-aml", + "EUR:1000", + "party over", + TALER_AML_FROZEN, + NULL, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-one-frozen", + "create-aml-officer-1", + TALER_AML_FROZEN, + MHD_HTTP_OK), + TALER_TESTING_cmd_check_aml_decisions ("check-decisions-zero-normal", + "create-aml-officer-1", + TALER_AML_NORMAL, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_sleep ("sleep-1d", + 1), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1-disable", + "create-aml-officer-1", + "Peter Falk", + false, + true), + /* Test that we are NOT allowed to read AML decisions now that + our AML staff account is disabled */ + TALER_TESTING_cmd_check_aml_decision ("check-aml-decision-disabled", + "create-aml-officer-1", + "aml-decide", + MHD_HTTP_FORBIDDEN), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command commands[] = { + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-2"), + TALER_TESTING_cmd_system_start ("start-taler", + CONFIG_FILE, + "-e", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_batch ("withdraw", + withdraw), + TALER_TESTING_cmd_batch ("spend", + spend), + TALER_TESTING_cmd_batch ("track", + track), + TALER_TESTING_cmd_batch ("withdraw-kyc", + withdraw_kyc), + TALER_TESTING_cmd_batch ("wallet-kyc", + wallet_kyc), + TALER_TESTING_cmd_batch ("p2p_withdraw", + p2p_withdraw), + TALER_TESTING_cmd_batch ("push", + push), + TALER_TESTING_cmd_batch ("pull", + pull), + TALER_TESTING_cmd_batch ("aml", + aml), + TALER_TESTING_cmd_end () + }; + + (void) cls; + TALER_TESTING_run (is, + commands); +} + + +int +main (int argc, + char *const *argv) +{ + (void) argc; + return TALER_TESTING_main (argv, + "INFO", + CONFIG_FILE, + "exchange-account-2", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); +} + + +/* end of test_kyc_api.c */ diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf new file mode 100644 index 000000000..b6bfdb055 --- /dev/null +++ b/src/testing/test_kyc_api.conf @@ -0,0 +1,42 @@ +# This file is in the public domain. +# +@INLINE@ coins-rsa.conf +@INLINE@ test_exchange_api.conf + +[kyc-provider-test-oauth2] +COST = 0 +LOGIC = oauth2 +USER_TYPE = INDIVIDUAL +PROVIDED_CHECKS = DUMMY +KYC_OAUTH2_VALIDITY = forever +KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token +KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login +KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me +KYC_OAUTH2_CLIENT_ID = taler-exchange +KYC_OAUTH2_CLIENT_SECRET = exchange-secret +KYC_OAUTH2_POST_URL = http://example.com/ +KYC_OAUTH2_CONVERTER_HELPER = taler-exchange-kyc-oauth2-test-converter.sh +# "{"full_name":"{{last_name}}, {{first_name}}"}" + +[kyc-legitimization-balance-high] +OPERATION_TYPE = BALANCE +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:8 + +[kyc-legitimization-deposit-any] +OPERATION_TYPE = DEPOSIT +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:0 +TIMEFRAME = 1d + +[kyc-legitimization-withdraw] +OPERATION_TYPE = WITHDRAW +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:10 +TIMEFRAME = 1d + +[kyc-legitimization-merge] +OPERATION_TYPE = MERGE +REQUIRED_CHECKS = DUMMY +THRESHOLD = EUR:0 +TIMEFRAME = 1d diff --git a/src/testing/test_taler_exchange_aggregator.c b/src/testing/test_taler_exchange_aggregator.c index 684fe9697..2d7acc6dc 100644 --- a/src/testing/test_taler_exchange_aggregator.c +++ b/src/testing/test_taler_exchange_aggregator.c @@ -31,24 +31,9 @@ /** - * Helper structure to keep exchange configuration values. + * Our credentials. */ -static struct TALER_TESTING_ExchangeConfiguration ec; - -/** - * Bank configuration data. - */ -static struct TALER_TESTING_BankConfiguration bc; - -/** - * Contains plugin. - */ -static struct TALER_TESTING_DatabaseConnection dbc; - -/** - * Return value from main(). - */ -static int result; +struct TALER_TESTING_Credentials cred; /** * Name of the configuration file to use. @@ -71,24 +56,6 @@ static char *config_filename; /** - * Function run on shutdown to unload the DB plugin. - * - * @param cls NULL - */ -static void -unload_db (void *cls) -{ - (void) cls; - if (NULL != dbc.plugin) - { - dbc.plugin->drop_tables (dbc.plugin->cls); - TALER_EXCHANGEDB_plugin_unload (dbc.plugin); - dbc.plugin = NULL; - } -} - - -/** * Collects all the tests. */ static void @@ -96,11 +63,13 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command all[] = { - TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", - config_filename, - "EUR:0.01", - "EUR:0.01"), - // check no aggregation happens on a empty database + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-1"), + TALER_TESTING_cmd_system_start ("start-taler", + config_filename, + "-e", + NULL), CMD_EXEC_AGGREGATOR ("run-aggregator-on-empty-db", config_filename), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-on-start"), @@ -108,10 +77,10 @@ run (void *cls, /* check aggregation happens on the simplest case: one deposit into the database. */ TALER_TESTING_cmd_insert_deposit ("do-deposit-1", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:1", "EUR:0.1"), @@ -119,27 +88,27 @@ run (void *cls, config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-1", - ec.exchange_url, + cred.exchange_url, "EUR:0.89", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-after-1"), /* check aggregation accumulates well. */ TALER_TESTING_cmd_insert_deposit ("do-deposit-2a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:1", "EUR:0.1"), TALER_TESTING_cmd_insert_deposit ("do-deposit-2b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:1", "EUR:0.1"), @@ -148,34 +117,34 @@ run (void *cls, config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-2", - ec.exchange_url, + cred.exchange_url, "EUR:1.79", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-after-2"), /* check that different merchants stem different aggregations. */ TALER_TESTING_cmd_insert_deposit ("do-deposit-3a", - &dbc, + cred.cfg, "bob", "4", - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:1", "EUR:0.1"), TALER_TESTING_cmd_insert_deposit ("do-deposit-3b", - &dbc, + cred.cfg, "bob", "5", - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:1", "EUR:0.1"), TALER_TESTING_cmd_insert_deposit ("do-deposit-3c", - &dbc, + cred.cfg, "alice", "4", - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:1", "EUR:0.1"), @@ -183,38 +152,38 @@ run (void *cls, config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-3a", - ec.exchange_url, + cred.exchange_url, "EUR:0.89", - bc.exchange_payto, - "payto://x-taler-bank/localhost/4"), + cred.exchange_payto, + "payto://x-taler-bank/localhost/4?receiver-name=4"), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-3b", - ec.exchange_url, + cred.exchange_url, "EUR:0.89", - bc.exchange_payto, - "payto://x-taler-bank/localhost/4"), + cred.exchange_payto, + "payto://x-taler-bank/localhost/4?receiver-name=4"), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-3c", - ec.exchange_url, + cred.exchange_url, "EUR:0.89", - bc.exchange_payto, - "payto://x-taler-bank/localhost/5"), + cred.exchange_payto, + "payto://x-taler-bank/localhost/5?receiver-name=5"), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-after-3"), /* checking that aggregator waits for the deadline. */ TALER_TESTING_cmd_insert_deposit ("do-deposit-4a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), "EUR:0.2", "EUR:0.1"), TALER_TESTING_cmd_insert_deposit ("do-deposit-4b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), @@ -230,17 +199,17 @@ run (void *cls, CMD_EXEC_AGGREGATOR ("run-aggregator-deposit-4-delayed", config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-4", - ec.exchange_url, + cred.exchange_url, "EUR:0.19", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), // test picking all deposits at earliest deadline TALER_TESTING_cmd_insert_deposit ("do-deposit-5a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), @@ -248,10 +217,10 @@ run (void *cls, "EUR:0.1"), TALER_TESTING_cmd_insert_deposit ("do-deposit-5b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), @@ -267,16 +236,16 @@ run (void *cls, CMD_EXEC_AGGREGATOR ("run-aggregator-deposit-5-delayed", config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-5", - ec.exchange_url, + cred.exchange_url, "EUR:0.19", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /* Test NEVER running 'tiny' unless they make up minimum unit */ TALER_TESTING_cmd_insert_deposit ("do-deposit-6a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.102", "EUR:0.1"), @@ -285,18 +254,18 @@ run (void *cls, TALER_TESTING_cmd_check_bank_empty ( "expect-empty-transactions-after-6a-tiny"), TALER_TESTING_cmd_insert_deposit ("do-deposit-6b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.102", "EUR:0.1"), TALER_TESTING_cmd_insert_deposit ("do-deposit-6c", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.102", "EUR:0.1"), @@ -305,10 +274,10 @@ run (void *cls, TALER_TESTING_cmd_check_bank_empty ( "expect-empty-transactions-after-6c-tiny"), TALER_TESTING_cmd_insert_deposit ("do-deposit-6d", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.102", "EUR:0.1"), @@ -317,27 +286,27 @@ run (void *cls, TALER_TESTING_cmd_check_bank_empty ( "expect-empty-transactions-after-6d-tiny"), TALER_TESTING_cmd_insert_deposit ("do-deposit-6e", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.112", "EUR:0.1"), CMD_EXEC_AGGREGATOR ("run-aggregator-deposit-6e", config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-6", - ec.exchange_url, + cred.exchange_url, "EUR:0.01", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /* Test profiteering if wire deadline is short */ TALER_TESTING_cmd_insert_deposit ("do-deposit-7a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.109", "EUR:0.1"), @@ -346,44 +315,44 @@ run (void *cls, TALER_TESTING_cmd_check_bank_empty ( "expect-empty-transactions-after-7a-tiny"), TALER_TESTING_cmd_insert_deposit ("do-deposit-7b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.119", "EUR:0.1"), CMD_EXEC_AGGREGATOR ("run-aggregator-deposit-7-profit", config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-7", - ec.exchange_url, + cred.exchange_url, "EUR:0.01", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /* Now check profit was actually taken */ TALER_TESTING_cmd_insert_deposit ("do-deposit-7c", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.122", "EUR:0.1"), CMD_EXEC_AGGREGATOR ("run-aggregator-deposit-7-loss", config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-7", - ec.exchange_url, + cred.exchange_url, "EUR:0.01", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /* Test that aggregation would happen fully if wire deadline is long */ TALER_TESTING_cmd_insert_deposit ("do-deposit-8a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), @@ -394,10 +363,10 @@ run (void *cls, TALER_TESTING_cmd_check_bank_empty ( "expect-empty-transactions-after-8a-tiny"), TALER_TESTING_cmd_insert_deposit ("do-deposit-8b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), @@ -410,27 +379,27 @@ run (void *cls, /* now trigger aggregate with large transaction and short deadline */ TALER_TESTING_cmd_insert_deposit ("do-deposit-8c", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.122", "EUR:0.1"), CMD_EXEC_AGGREGATOR ("run-aggregator-deposit-8", config_filename), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-8", - ec.exchange_url, + cred.exchange_url, "EUR:0.03", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), /* Test aggregation with fees and rounding profits. */ TALER_TESTING_cmd_insert_deposit ("do-deposit-9a", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), @@ -441,10 +410,10 @@ run (void *cls, TALER_TESTING_cmd_check_bank_empty ( "expect-empty-transactions-after-9a-tiny"), TALER_TESTING_cmd_insert_deposit ("do-deposit-9b", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), @@ -457,10 +426,10 @@ run (void *cls, /* now trigger aggregate with large transaction and short deadline */ TALER_TESTING_cmd_insert_deposit ("do-deposit-9c", - &dbc, + cred.cfg, "bob", USER42_ACCOUNT, - GNUNET_TIME_absolute_get (), + GNUNET_TIME_timestamp_get (), GNUNET_TIME_UNIT_ZERO, "EUR:0.112", "EUR:0.1"), @@ -468,49 +437,15 @@ run (void *cls, config_filename), /* 0.009 + 0.009 + 0.022 - 0.001 - 0.002 - 0.008 = 0.029 => 0.02 */ TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-9", - ec.exchange_url, + cred.exchange_url, "EUR:0.01", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - all, - bc.exchange_auth.wire_gateway_url); -} - - -/** - * Prepare database and launch the test. - * - * @param cls unused - * @param is interpreter to use - */ -static void -prepare_database (void *cls, - struct TALER_TESTING_Interpreter *is) -{ - dbc.plugin = TALER_EXCHANGEDB_plugin_load (is->cfg); - if (NULL == dbc.plugin) - { - GNUNET_break (0); - result = 77; - TALER_TESTING_interpreter_fail (is); - return; - } - if (GNUNET_OK != - dbc.plugin->preflight (dbc.plugin->cls)) - { - GNUNET_break (0); - result = 77; - TALER_TESTING_interpreter_fail (is); - return; - } - GNUNET_SCHEDULER_add_shutdown (&unload_db, - NULL); - run (NULL, - is); + TALER_TESTING_run (is, + all); } @@ -519,7 +454,6 @@ main (int argc, char *const argv[]) { const char *plugin_name; - char *testname; if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) { @@ -527,52 +461,17 @@ main (int argc, return -1; } plugin_name++; - (void) GNUNET_asprintf (&testname, - "test-taler-exchange-aggregator-%s", - plugin_name); (void) GNUNET_asprintf (&config_filename, - "%s.conf", - testname); - - GNUNET_log_setup ("test_taler_exchange_aggregator", - "DEBUG", - NULL); - - /* these might get in the way */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - - TALER_TESTING_cleanup_files (config_filename); - - if (GNUNET_OK != - TALER_TESTING_prepare_exchange (config_filename, - GNUNET_YES, - &ec)) - { - TALER_LOG_WARNING ("Could not prepare the exchange.\n"); - return 77; - } - - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (config_filename, - "exchange-account-1", - &bc)) - { - TALER_LOG_WARNING ("Could not prepare the fakebank\n"); - return 77; - } - result = GNUNET_OK; - if (GNUNET_OK != - TALER_TESTING_setup_with_exchange (&prepare_database, - NULL, - config_filename)) - { - TALER_LOG_WARNING ("Could not prepare database for tests.\n"); - return result; - } - GNUNET_free (config_filename); - GNUNET_free (testname); - return GNUNET_OK == result ? 0 : 1; + "test-taler-exchange-aggregator-%s.conf", + plugin_name); + return TALER_TESTING_main (argv, + "INFO", + config_filename, + "exchange-account-1", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/test_taler_exchange_httpd_home/.config/taler/account-1.json b/src/testing/test_taler_exchange_httpd_home/.config/taler/account-1.json deleted file mode 100644 index 21a7500b4..000000000 --- a/src/testing/test_taler_exchange_httpd_home/.config/taler/account-1.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "payto_uri": "payto://x-taler-bank/localhost/2", - "master_sig": "HEWC1XDS0QZ53YQR451VRKD4N968NXWGZXS30HJ59MJ0PESACK1ZYPYCAT15P08WD58C7D7F6EVN26D59JKA75XEBDQCM8VYFETK82R" -}
\ No newline at end of file diff --git a/src/testing/test_taler_exchange_httpd_home/.local/share/taler/exchange-offline/master.priv b/src/testing/test_taler_exchange_httpd_home/.local/share/taler/exchange-offline/master.priv deleted file mode 100644 index 394926938..000000000 --- a/src/testing/test_taler_exchange_httpd_home/.local/share/taler/exchange-offline/master.priv +++ /dev/null @@ -1 +0,0 @@ -pÚ^ó-Ú33ˆ€XXÁ!ˆ\0qúýµmUþ_‰ˆ
\ No newline at end of file diff --git a/src/testing/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv b/src/testing/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv deleted file mode 100644 index 394926938..000000000 --- a/src/testing/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv +++ /dev/null @@ -1 +0,0 @@ -pÚ^ó-Ú33ˆ€XXÁ!ˆ\0qúýµmUþ_‰ˆ
\ No newline at end of file diff --git a/src/testing/test_taler_exchange_wirewatch.c b/src/testing/test_taler_exchange_wirewatch.c index 8477e628e..86b616456 100644 --- a/src/testing/test_taler_exchange_wirewatch.c +++ b/src/testing/test_taler_exchange_wirewatch.c @@ -34,14 +34,9 @@ /** - * Bank configuration data. + * Our credentials. */ -static struct TALER_TESTING_BankConfiguration bc; - -/** - * Helper structure to keep exchange configuration values. - */ -static struct TALER_TESTING_ExchangeConfiguration ec; +static struct TALER_TESTING_Credentials cred; /** * Name of the configuration file to use. @@ -66,8 +61,8 @@ transfer_to_exchange (const char *label, { return TALER_TESTING_cmd_admin_add_incoming (label, amount, - &bc.exchange_auth, - bc.user42_payto); + &cred.ba, + cred.user42_payto); } @@ -82,21 +77,19 @@ run (void *cls, struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command all[] = { - TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", - config_filename, - "EUR:0.01", - "EUR:0.01"), - TALER_TESTING_cmd_auditor_add ("add-auditor-OK", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_wire_add ("add-wire-account", - "payto://x-taler-bank/localhost/2", - MHD_HTTP_NO_CONTENT, - false), - TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys", - config_filename), - TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", - 1), + TALER_TESTING_cmd_run_fakebank ("run-fakebank", + cred.cfg, + "exchange-account-1"), + TALER_TESTING_cmd_system_start ("start-taler", + config_filename, + "-e", + "-u", "exchange-account-1", + NULL), + TALER_TESTING_cmd_get_exchange ("get-exchange", + cred.cfg, + NULL, + true, + true), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-on-start"), CMD_EXEC_AGGREGATOR ("run-aggregator-on-empty"), TALER_TESTING_cmd_exec_wirewatch ("run-wirewatch-on-empty", @@ -111,8 +104,8 @@ run (void *cls, TALER_TESTING_cmd_check_bank_admin_transfer ( "clear-good-transfer-to-the-exchange", "EUR:5", - bc.user42_payto, // debit - bc.exchange_payto, // credit + cred.user42_payto, // debit + cred.exchange_payto, // credit "run-transfer-good-to-exchange"), TALER_TESTING_cmd_exec_closer ("run-closer-non-expired-reserve", @@ -135,17 +128,17 @@ run (void *cls, CMD_EXEC_AGGREGATOR ("run-closer-on-expired-reserve"), TALER_TESTING_cmd_check_bank_transfer ("expect-deposit-1", - ec.exchange_url, + cred.exchange_url, "EUR:4.99", - bc.exchange_payto, - bc.user42_payto), + cred.exchange_payto, + cred.user42_payto), TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-2"), TALER_TESTING_cmd_end () }; - TALER_TESTING_run_with_fakebank (is, - all, - bc.exchange_auth.wire_gateway_url); + (void) cls; + TALER_TESTING_run (is, + all); } @@ -153,68 +146,29 @@ int main (int argc, char *const argv[]) { - const char *plugin_name; - - /* these might get in the way */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test_taler_exchange_wirewatch", - "DEBUG", - NULL); - - if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) + (void) argc; { - GNUNET_break (0); - return -1; - } - plugin_name++; - { - char *testname; - - GNUNET_asprintf (&testname, - "test-taler-exchange-wirewatch-%s", - plugin_name); + const char *plugin_name; + + plugin_name = strrchr (argv[0], (int) '-'); + if (NULL == plugin_name) + { + GNUNET_break (0); + return -1; + } + plugin_name++; GNUNET_asprintf (&config_filename, - "%s.conf", - testname); - GNUNET_free (testname); - } - /* check database is working */ - { - struct GNUNET_PQ_Context *conn; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - conn = GNUNET_PQ_connect ("postgres:///talercheck", - NULL, - es, - NULL); - if (NULL == conn) - return 77; - GNUNET_PQ_disconnect (conn); - } - - TALER_TESTING_cleanup_files (config_filename); - if (GNUNET_OK != - TALER_TESTING_prepare_exchange (config_filename, - GNUNET_YES, - &ec)) - { - TALER_LOG_INFO ("Could not prepare the exchange\n"); - return 77; + "test-taler-exchange-wirewatch-%s.conf", + plugin_name); } - - if (GNUNET_OK != - TALER_TESTING_prepare_fakebank (config_filename, - "exchange-account-1", - &bc)) - return 77; - - return (GNUNET_OK == - TALER_TESTING_setup_with_exchange (&run, - NULL, - config_filename)) ? 0 : 1; + return TALER_TESTING_main (argv, + "INFO", + config_filename, + "exchange-account-1", + TALER_TESTING_BS_FAKEBANK, + &cred, + &run, + NULL); } diff --git a/src/testing/testing_api_cmd_age_withdraw.c b/src/testing/testing_api_cmd_age_withdraw.c new file mode 100644 index 000000000..6ad22809e --- /dev/null +++ b/src/testing/testing_api_cmd_age_withdraw.c @@ -0,0 +1,756 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_age_withdraw.c + * @brief implements the age-withdraw command + * @author Özgür Kesim + */ + +#include "platform.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_common.h> +#include <microhttpd.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_extensions.h" +#include "taler_testing_lib.h" + +/* + * The output state of coin + */ +struct CoinOutputState +{ + + /** + * The calculated details during "age-withdraw", for the selected coin. + */ + struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details; + + /** + * The (wanted) value of the coin, MUST be the same as input.denom_pub.value; + */ + struct TALER_Amount amount; + + /** + * Reserve history entry that corresponds to this coin. + * Will be of type #TALER_EXCHANGE_RTT_AGEWITHDRAWAL. + */ + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; +}; + +/** + * State for a "age withdraw" CMD: + */ + +struct AgeWithdrawState +{ + + /** + * Interpreter state (during command) + */ + struct TALER_TESTING_Interpreter *is; + + /** + * The age-withdraw handle + */ + struct TALER_EXCHANGE_AgeWithdrawHandle *handle; + + /** + * Exchange base URL. Only used as offered trait. + */ + char *exchange_url; + + /** + * URI of the reserve we are withdrawing from. + */ + char *reserve_payto_uri; + + /** + * Private key of the reserve we are withdrawing from. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Public key of the reserve we are withdrawing from. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Which reserve should we withdraw from? + */ + const char *reserve_reference; + + /** + * Expected HTTP response code to the request. + */ + unsigned int expected_response_code; + + /** + * Age mask + */ + struct TALER_AgeMask mask; + + /** + * The maximum age we commit to + */ + uint8_t max_age; + + /** + * Number of coins to withdraw + */ + size_t num_coins; + + /** + * The @e num_coins input that is provided to the + * `TALER_EXCHANGE_age_withdraw` API. + * Each contains kappa secrets, from which we will have + * to disclose kappa-1 in a subsequent age-withdraw-reveal operation. + */ + struct TALER_EXCHANGE_AgeWithdrawCoinInput *coin_inputs; + + /** + * The output state of @e num_coins coins, calculated during the + * "age-withdraw" operation. + */ + struct CoinOutputState *coin_outputs; + + /** + * The index returned by the exchange for the "age-withdraw" operation, + * of the kappa coin candidates that we do not disclose and keep. + */ + uint8_t noreveal_index; + + /** + * The blinded hashes of the non-revealed (to keep) @e num_coins coins. + */ + const struct TALER_BlindedCoinHashP *blinded_coin_hs; + + /** + * The hash of the commitment, needed for the reveal step. + */ + struct TALER_AgeWithdrawCommitmentHashP h_commitment; + + /** + * Set to the KYC requirement payto hash *if* the exchange replied with a + * request for KYC. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC. + */ + uint64_t requirement_row; + +}; + +/** + * Callback for the "age-withdraw" ooperation; It checks that the response + * code is expected and store the exchange signature in the state. + * + * @param cls Closure of type `struct AgeWithdrawState *` + * @param response Response details + */ +static void +age_withdraw_cb ( + void *cls, + const struct TALER_EXCHANGE_AgeWithdrawResponse *response) +{ + struct AgeWithdrawState *aws = cls; + struct TALER_TESTING_Interpreter *is = aws->is; + + aws->handle = NULL; + if (aws->expected_response_code != response->hr.http_status) + { + TALER_TESTING_unexpected_status_with_body (is, + response->hr.http_status, + aws->expected_response_code, + response->hr.reply); + return; + } + + switch (response->hr.http_status) + { + case MHD_HTTP_OK: + aws->noreveal_index = response->details.ok.noreveal_index; + aws->h_commitment = response->details.ok.h_commitment; + + GNUNET_assert (aws->num_coins == response->details.ok.num_coins); + for (size_t n = 0; n < aws->num_coins; n++) + { + aws->coin_outputs[n].details = response->details.ok.coin_details[n]; + TALER_age_commitment_proof_deep_copy ( + &response->details.ok.coin_details[n].age_commitment_proof, + &aws->coin_outputs[n].details.age_commitment_proof); + } + aws->blinded_coin_hs = response->details.ok.blinded_coin_hs; + break; + case MHD_HTTP_FORBIDDEN: + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_GONE: + /* nothing to check */ + break; + case MHD_HTTP_CONFLICT: + /* TODO[oec]: Add this to the response-type and handle it here */ + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + default: + /* Unsupported status code (by test harness) */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "test command for age-withdraw not support status code %u, body:\n" + ">>%s<<\n", + response->hr.http_status, + json_dumps (response->hr.reply, JSON_INDENT (2))); + GNUNET_break (0); + break; + } + + /* We are done with this command, pick the next one */ + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command for age-withdraw. + */ +static void +age_withdraw_run ( + void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AgeWithdrawState *aws = cls; + struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is); + const struct TALER_ReservePrivateKeyP *rp; + const struct TALER_TESTING_Command *create_reserve; + const struct TALER_EXCHANGE_DenomPublicKey *dpk; + + aws->is = is; + + /* Prepare the reserve related data */ + create_reserve + = TALER_TESTING_interpreter_lookup_command ( + is, + aws->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &rp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL == aws->exchange_url) + aws->exchange_url + = GNUNET_strdup (TALER_TESTING_get_exchange_url (is)); + aws->reserve_priv = *rp; + GNUNET_CRYPTO_eddsa_key_get_public (&aws->reserve_priv.eddsa_priv, + &aws->reserve_pub.eddsa_pub); + aws->reserve_payto_uri + = TALER_reserve_make_payto (aws->exchange_url, + &aws->reserve_pub); + + aws->coin_inputs = GNUNET_new_array ( + aws->num_coins, + struct TALER_EXCHANGE_AgeWithdrawCoinInput); + + for (unsigned int i = 0; i<aws->num_coins; i++) + { + struct TALER_EXCHANGE_AgeWithdrawCoinInput *input = &aws->coin_inputs[i]; + struct CoinOutputState *cos = &aws->coin_outputs[i]; + + /* randomly create the secrets for the kappa coin-candidates */ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &input->secrets, + sizeof(input->secrets)); + /* Find denomination */ + dpk = TALER_TESTING_find_pk (keys, + &cos->amount, + true); /* _always_ use denominations with age-striction */ + if (NULL == dpk) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to determine denomination key for amount at %s\n", + (NULL != cmd) ? cmd->label : "<retried command>"); + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + /* We copy the denomination key, as re-querying /keys + * would free the old one. */ + input->denom_pub = TALER_EXCHANGE_copy_denomination_key (dpk); + cos->reserve_history.type = TALER_EXCHANGE_RTT_AGEWITHDRAWAL; + GNUNET_assert (0 <= + TALER_amount_add (&cos->reserve_history.amount, + &cos->amount, + &input->denom_pub->fees.withdraw)); + cos->reserve_history.details.withdraw.fee = input->denom_pub->fees.withdraw; + } + + /* Execute the age-withdraw protocol */ + aws->handle = + TALER_EXCHANGE_age_withdraw ( + TALER_TESTING_interpreter_get_context (is), + keys, + TALER_TESTING_get_exchange_url (is), + rp, + aws->num_coins, + aws->coin_inputs, + aws->max_age, + &age_withdraw_cb, + aws); + + if (NULL == aws->handle) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "age withdraw" CMD, and possibly cancel a + * pending operation thereof + * + * @param cls Closure of type `struct AgeWithdrawState` + * @param cmd The command being freed. + */ +static void +age_withdraw_cleanup ( + void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AgeWithdrawState *aws = cls; + + if (NULL != aws->handle) + { + TALER_TESTING_command_incomplete (aws->is, + cmd->label); + TALER_EXCHANGE_age_withdraw_cancel (aws->handle); + aws->handle = NULL; + } + + if (NULL != aws->coin_inputs) + { + for (size_t n = 0; n < aws->num_coins; n++) + { + struct TALER_EXCHANGE_AgeWithdrawCoinInput *in = &aws->coin_inputs[n]; + struct CoinOutputState *out = &aws->coin_outputs[n]; + + if (NULL != in && NULL != in->denom_pub) + { + TALER_EXCHANGE_destroy_denomination_key (in->denom_pub); + in->denom_pub = NULL; + } + if (NULL != out) + TALER_age_commitment_proof_free (&out->details.age_commitment_proof); + } + GNUNET_free (aws->coin_inputs); + } + GNUNET_free (aws->coin_outputs); + GNUNET_free (aws->exchange_url); + GNUNET_free (aws->reserve_payto_uri); + GNUNET_free (aws); +} + + +/** + * Offer internal data of a "age withdraw" CMD state to other commands. + * + * @param cls Closure of type `struct AgeWithdrawState` + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param idx index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +age_withdraw_traits ( + void *cls, + const void **ret, + const char *trait, + unsigned int idx) +{ + struct AgeWithdrawState *aws = cls; + uint8_t k = aws->noreveal_index; + struct TALER_EXCHANGE_AgeWithdrawCoinInput *in = &aws->coin_inputs[idx]; + struct CoinOutputState *out = &aws->coin_outputs[idx]; + struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails *details = + &aws->coin_outputs[idx].details; + struct TALER_TESTING_Trait traits[] = { + /* history entry MUST be first due to response code logic below! */ + TALER_TESTING_make_trait_reserve_history (idx, + &out->reserve_history), + TALER_TESTING_make_trait_denom_pub (idx, + in->denom_pub), + TALER_TESTING_make_trait_reserve_priv (&aws->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (&aws->reserve_pub), + TALER_TESTING_make_trait_amounts (idx, + &out->amount), + /* TODO[oec]: add legal requirement to response and handle it here, as well + TALER_TESTING_make_trait_legi_requirement_row (&aws->requirement_row), + TALER_TESTING_make_trait_h_payto (&aws->h_payto), + */ + TALER_TESTING_make_trait_h_blinded_coin (idx, + &aws->blinded_coin_hs[idx]), + TALER_TESTING_make_trait_payto_uri (aws->reserve_payto_uri), + TALER_TESTING_make_trait_exchange_url (aws->exchange_url), + TALER_TESTING_make_trait_coin_priv (idx, + &details->coin_priv), + TALER_TESTING_make_trait_planchet_secrets (idx, + &in->secrets[k]), + TALER_TESTING_make_trait_blinding_key (idx, + &details->blinding_key), + TALER_TESTING_make_trait_exchange_wd_value (idx, + &details->alg_values), + TALER_TESTING_make_trait_age_commitment_proof ( + idx, + &details->age_commitment_proof), + TALER_TESTING_make_trait_h_age_commitment ( + idx, + &details->h_age_commitment), + }; + + if (idx >= aws->num_coins) + return GNUNET_NO; + + return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK) + ? &traits[0] /* we have reserve history */ + : &traits[1], /* skip reserve history */ + ret, + trait, + idx); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_age_withdraw (const char *label, + const char *reserve_reference, + uint8_t max_age, + unsigned int expected_response_code, + const char *amount, + ...) +{ + struct AgeWithdrawState *aws; + unsigned int cnt; + va_list ap; + + aws = GNUNET_new (struct AgeWithdrawState); + aws->reserve_reference = reserve_reference; + aws->expected_response_code = expected_response_code; + aws->mask = TALER_extensions_get_age_restriction_mask (); + aws->max_age = TALER_get_lowest_age (&aws->mask, max_age); + + cnt = 1; + va_start (ap, amount); + while (NULL != (va_arg (ap, const char *))) + cnt++; + aws->num_coins = cnt; + aws->coin_outputs = GNUNET_new_array (cnt, + struct CoinOutputState); + va_end (ap); + va_start (ap, amount); + + for (unsigned int i = 0; i<aws->num_coins; i++) + { + struct CoinOutputState *out = &aws->coin_outputs[i]; + if (GNUNET_OK != + TALER_string_to_amount (amount, + &out->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + amount, + label); + GNUNET_assert (0); + } + /* move on to next vararg! */ + amount = va_arg (ap, const char *); + } + + GNUNET_assert (NULL == amount); + va_end (ap); + + { + struct TALER_TESTING_Command cmd = { + .cls = aws, + .label = label, + .run = &age_withdraw_run, + .cleanup = &age_withdraw_cleanup, + .traits = &age_withdraw_traits, + }; + + return cmd; + } +} + + +/** + * The state for the age-withdraw-reveal operation + */ +struct AgeWithdrawRevealState +{ + /** + * The reference to the CMD resembling the previous call to age-withdraw + */ + const char *age_withdraw_reference; + + /** + * The state to the previous age-withdraw command + */ + const struct AgeWithdrawState *aws; + + /** + * The expected response code from the call to the + * age-withdraw-reveal operation + */ + unsigned int expected_response_code; + + /** + * Interpreter state (during command) + */ + struct TALER_TESTING_Interpreter *is; + + /** + * The handle to the reveal-operation + */ + struct TALER_EXCHANGE_AgeWithdrawRevealHandle *handle; + + + /** + * Number of coins, extracted form the age withdraw command + */ + size_t num_coins; + + /** + * The signatures of the @e num_coins coins returned + */ + struct TALER_DenominationSignature *denom_sigs; + +}; + +/* + * Callback for the reveal response + * + * @param cls Closure of type `struct AgeWithdrawRevealState` + * @param awr The response + */ +static void +age_withdraw_reveal_cb ( + void *cls, + const struct TALER_EXCHANGE_AgeWithdrawRevealResponse *response) +{ + struct AgeWithdrawRevealState *awrs = cls; + struct TALER_TESTING_Interpreter *is = awrs->is; + + awrs->handle = NULL; + if (awrs->expected_response_code != response->hr.http_status) + { + TALER_TESTING_unexpected_status_with_body (is, + response->hr.http_status, + awrs->expected_response_code, + response->hr.reply); + return; + } + switch (response->hr.http_status) + { + case MHD_HTTP_OK: + { + const struct AgeWithdrawState *aws = awrs->aws; + GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs); + awrs->denom_sigs = GNUNET_new_array (awrs->num_coins, + struct TALER_DenominationSignature); + for (size_t n = 0; n < awrs->num_coins; n++) + { + GNUNET_assert (GNUNET_OK == + TALER_denom_sig_unblind ( + &awrs->denom_sigs[n], + &response->details.ok.blinded_denom_sigs[n], + &aws->coin_outputs[n].details.blinding_key, + &aws->coin_outputs[n].details.h_coin_pub, + &aws->coin_outputs[n].details.alg_values, + &aws->coin_inputs[n].denom_pub->key)); + TALER_denom_sig_free (&awrs->denom_sigs[n]); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "age-withdraw reveal success!\n"); + GNUNET_free (awrs->denom_sigs); + } + break; + case MHD_HTTP_NOT_FOUND: + case MHD_HTTP_FORBIDDEN: + /* nothing to check */ + break; + /* TODO[oec]: handle more cases !? */ + default: + /* Unsupported status code (by test harness) */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Age withdraw reveal test command does not support status code %u\n", + response->hr.http_status); + GNUNET_break (0); + break; + } + + /* We are done with this command, pick the next one */ + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command for age-withdraw-reveal + */ +static void +age_withdraw_reveal_run ( + void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AgeWithdrawRevealState *awrs = cls; + const struct TALER_TESTING_Command *age_withdraw_cmd; + const struct AgeWithdrawState *aws; + + (void) cmd; + awrs->is = is; + + /* + * Get the command and state for the previous call to "age witdraw" + */ + age_withdraw_cmd = + TALER_TESTING_interpreter_lookup_command (is, + awrs->age_withdraw_reference); + if (NULL == age_withdraw_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run); + aws = age_withdraw_cmd->cls; + awrs->aws = aws; + awrs->num_coins = aws->num_coins; + + awrs->handle = + TALER_EXCHANGE_age_withdraw_reveal ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + aws->num_coins, + aws->coin_inputs, + aws->noreveal_index, + &aws->h_commitment, + &aws->reserve_pub, + age_withdraw_reveal_cb, + awrs); +} + + +/** + * Free the state of a "age-withdraw-reveal" CMD, and possibly + * cancel a pending operation thereof + * + * @param cls Closure of type `struct AgeWithdrawRevealState` + * @param cmd The command being freed. + */ +static void +age_withdraw_reveal_cleanup ( + void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AgeWithdrawRevealState *awrs = cls; + + if (NULL != awrs->handle) + { + TALER_TESTING_command_incomplete (awrs->is, + cmd->label); + TALER_EXCHANGE_age_withdraw_reveal_cancel (awrs->handle); + awrs->handle = NULL; + } + GNUNET_free (awrs->denom_sigs); + awrs->denom_sigs = NULL; + GNUNET_free (awrs); +} + + +/** + * Offer internal data of a "age withdraw reveal" CMD state to other commands. + * + * @param cls Closure of they `struct AgeWithdrawRevealState` + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param idx index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +age_withdraw_reveal_traits ( + void *cls, + const void **ret, + const char *trait, + unsigned int idx) +{ + struct AgeWithdrawRevealState *awrs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_denom_sig (idx, + &awrs->denom_sigs[idx]), + /* FIXME: shall we provide the traits from the previous + * call to "age withdraw" as well? */ + }; + + if (idx >= awrs->num_coins) + return GNUNET_NO; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + idx); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_age_withdraw_reveal ( + const char *label, + const char *age_withdraw_reference, + unsigned int expected_response_code) +{ + struct AgeWithdrawRevealState *awrs = + GNUNET_new (struct AgeWithdrawRevealState); + + awrs->age_withdraw_reference = age_withdraw_reference; + awrs->expected_response_code = expected_response_code; + + struct TALER_TESTING_Command cmd = { + .cls = awrs, + .label = label, + .run = age_withdraw_reveal_run, + .cleanup = age_withdraw_reveal_cleanup, + .traits = age_withdraw_reveal_traits, + }; + + return cmd; +} + + +/* end of testing_api_cmd_age_withdraw.c */ diff --git a/src/testing/testing_api_cmd_auditor_add.c b/src/testing/testing_api_cmd_auditor_add.c index 2c59f4194..ac9c2c8fb 100644 --- a/src/testing/testing_api_cmd_auditor_add.c +++ b/src/testing/testing_api_cmd_auditor_add.c @@ -18,7 +18,7 @@ */ /** * @file testing/testing_api_cmd_auditor_add.c - * @brief command for testing /auditor_add. + * @brief command for testing auditor_add * @author Christian Grothoff */ #include "platform.h" @@ -62,27 +62,22 @@ struct AuditorAddState * if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param aer response details */ static void -auditor_add_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) +auditor_add_cb ( + void *cls, + const struct TALER_EXCHANGE_ManagementAuditorEnableResponse *aer) { struct AuditorAddState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &aer->hr; ds->dh = NULL; if (ds->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response_code); return; } TALER_TESTING_interpreter_next (ds->is); @@ -102,13 +97,45 @@ auditor_add_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct AuditorAddState *ds = cls; - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp now; struct TALER_MasterSignatureP master_sig; + const struct TALER_AuditorPublicKeyP *auditor_pub; + const struct TALER_TESTING_Command *auditor_cmd; + const struct TALER_TESTING_Command *exchange_cmd; + const char *exchange_url; + const char *auditor_url; (void) cmd; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + now = GNUNET_TIME_timestamp_get (); ds->is = is; + + auditor_cmd = TALER_TESTING_interpreter_get_command (is, + "auditor"); + if (NULL == auditor_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_auditor_pub (auditor_cmd, + &auditor_pub)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_auditor_url (auditor_cmd, + &auditor_url)); + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + + if (ds->bad_sig) { memset (&master_sig, @@ -117,17 +144,22 @@ auditor_add_run (void *cls, } else { - TALER_exchange_offline_auditor_add_sign (&is->auditor_pub, - is->auditor_url, + const struct TALER_MasterPrivateKeyP *master_priv; + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); + TALER_exchange_offline_auditor_add_sign (auditor_pub, + auditor_url, now, - &is->master_priv, + master_priv, &master_sig); } ds->dh = TALER_EXCHANGE_management_enable_auditor ( - is->ctx, - is->exchange_url, - &is->auditor_pub, - is->auditor_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, + auditor_pub, + auditor_url, "test-case auditor", /* human-readable auditor name */ now, &master_sig, @@ -157,10 +189,8 @@ auditor_add_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); TALER_EXCHANGE_management_enable_auditor_cancel (ds->dh); ds->dh = NULL; } @@ -168,26 +198,6 @@ auditor_add_cleanup (void *cls, } -/** - * Offer internal data from a "auditor_add" CMD, to other commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * - * @return #GNUNET_OK on success. - */ -static int -auditor_add_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_NO; -} - - struct TALER_TESTING_Command TALER_TESTING_cmd_auditor_add (const char *label, unsigned int expected_http_status, @@ -203,8 +213,7 @@ TALER_TESTING_cmd_auditor_add (const char *label, .cls = ds, .label = label, .run = &auditor_add_run, - .cleanup = &auditor_add_cleanup, - .traits = &auditor_add_traits + .cleanup = &auditor_add_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_auditor_add_denom_sig.c b/src/testing/testing_api_cmd_auditor_add_denom_sig.c index efa4a9d76..6b7776896 100644 --- a/src/testing/testing_api_cmd_auditor_add_denom_sig.c +++ b/src/testing/testing_api_cmd_auditor_add_denom_sig.c @@ -67,27 +67,22 @@ struct AuditorAddDenomSigState * if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param adr response details */ static void -denom_sig_add_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) +denom_sig_add_cb ( + void *cls, + const struct TALER_EXCHANGE_AuditorAddDenominationResponse *adr) { struct AuditorAddDenomSigState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr; ds->dh = NULL; if (ds->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response_code); return; } TALER_TESTING_interpreter_next (ds->is); @@ -108,8 +103,13 @@ auditor_add_run (void *cls, { struct AuditorAddDenomSigState *ds = cls; struct TALER_AuditorSignatureP auditor_sig; - struct GNUNET_HashCode h_denom_pub; + struct TALER_DenominationHashP h_denom_pub; const struct TALER_EXCHANGE_DenomPublicKey *dk; + const struct TALER_AuditorPublicKeyP *auditor_pub; + const struct TALER_TESTING_Command *auditor_cmd; + const struct TALER_TESTING_Command *exchange_cmd; + const char *exchange_url; + const char *auditor_url; (void) cmd; /* Get denom pub from trait */ @@ -118,7 +118,6 @@ auditor_add_run (void *cls, denom_cmd = TALER_TESTING_interpreter_lookup_command (is, ds->denom_ref); - if (NULL == denom_cmd) { GNUNET_break (0); @@ -131,6 +130,31 @@ auditor_add_run (void *cls, &dk)); } ds->is = is; + auditor_cmd = TALER_TESTING_interpreter_get_command (is, + "auditor"); + if (NULL == auditor_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_auditor_pub (auditor_cmd, + &auditor_pub)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_auditor_url (auditor_cmd, + &auditor_url)); + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); if (ds->bad_sig) { memset (&auditor_sig, @@ -139,31 +163,33 @@ auditor_add_run (void *cls, } else { - struct TALER_MasterPublicKeyP master_pub; + const struct TALER_MasterPublicKeyP *master_pub; + const struct TALER_AuditorPrivateKeyP *auditor_priv; - GNUNET_CRYPTO_eddsa_key_get_public (&is->master_priv.eddsa_priv, - &master_pub.eddsa_pub); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_pub (exchange_cmd, + &master_pub)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_auditor_priv (auditor_cmd, + &auditor_priv)); TALER_auditor_denom_validity_sign ( - is->auditor_url, + auditor_url, &dk->h_key, - &master_pub, + master_pub, dk->valid_from, dk->withdraw_valid_until, dk->expire_deposit, dk->expire_legal, &dk->value, - &dk->fee_withdraw, - &dk->fee_deposit, - &dk->fee_refresh, - &dk->fee_refund, - &is->auditor_priv, + &dk->fees, + auditor_priv, &auditor_sig); } ds->dh = TALER_EXCHANGE_add_auditor_denomination ( - is->ctx, - is->exchange_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, &h_denom_pub, - &is->auditor_pub, + auditor_pub, &auditor_sig, &denom_sig_add_cb, ds); @@ -191,10 +217,8 @@ auditor_add_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); TALER_EXCHANGE_add_auditor_denomination_cancel (ds->dh); ds->dh = NULL; } @@ -202,26 +226,6 @@ auditor_add_cleanup (void *cls, } -/** - * Offer internal data from a "auditor_add" CMD, to other commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * - * @return #GNUNET_OK on success. - */ -static int -auditor_add_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_NO; -} - - struct TALER_TESTING_Command TALER_TESTING_cmd_auditor_add_denom_sig (const char *label, unsigned int expected_http_status, @@ -239,8 +243,7 @@ TALER_TESTING_cmd_auditor_add_denom_sig (const char *label, .cls = ds, .label = label, .run = &auditor_add_run, - .cleanup = &auditor_add_cleanup, - .traits = &auditor_add_traits + .cleanup = &auditor_add_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_auditor_del.c b/src/testing/testing_api_cmd_auditor_del.c index 71525a10e..8256bc1c7 100644 --- a/src/testing/testing_api_cmd_auditor_del.c +++ b/src/testing/testing_api_cmd_auditor_del.c @@ -62,27 +62,23 @@ struct AuditorDelState * if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param adr response details */ static void -auditor_del_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) +auditor_del_cb ( + void *cls, + const struct TALER_EXCHANGE_ManagementAuditorDisableResponse *adr) + { struct AuditorDelState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr; ds->dh = NULL; if (ds->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response_code); return; } TALER_TESTING_interpreter_next (ds->is); @@ -103,12 +99,37 @@ auditor_del_run (void *cls, { struct AuditorDelState *ds = cls; struct TALER_MasterSignatureP master_sig; - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp now; + const struct TALER_AuditorPublicKeyP *auditor_pub; + const struct TALER_TESTING_Command *auditor_cmd; + const struct TALER_TESTING_Command *exchange_cmd; + const char *exchange_url; (void) cmd; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + now = GNUNET_TIME_timestamp_get (); ds->is = is; + auditor_cmd = TALER_TESTING_interpreter_get_command (is, + "auditor"); + if (NULL == auditor_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_auditor_pub (auditor_cmd, + &auditor_pub)); + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); if (ds->bad_sig) { memset (&master_sig, @@ -117,15 +138,20 @@ auditor_del_run (void *cls, } else { - TALER_exchange_offline_auditor_del_sign (&is->auditor_pub, + const struct TALER_MasterPrivateKeyP *master_priv; + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); + TALER_exchange_offline_auditor_del_sign (auditor_pub, now, - &is->master_priv, + master_priv, &master_sig); } ds->dh = TALER_EXCHANGE_management_disable_auditor ( - is->ctx, - is->exchange_url, - &is->auditor_pub, + TALER_TESTING_interpreter_get_context (is), + exchange_url, + auditor_pub, now, &master_sig, &auditor_del_cb, @@ -154,10 +180,8 @@ auditor_del_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); TALER_EXCHANGE_management_disable_auditor_cancel (ds->dh); ds->dh = NULL; } @@ -165,26 +189,6 @@ auditor_del_cleanup (void *cls, } -/** - * Offer internal data from a "auditor_del" CMD, to other commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * - * @return #GNUNET_OK on success. - */ -static int -auditor_del_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_NO; -} - - struct TALER_TESTING_Command TALER_TESTING_cmd_auditor_del (const char *label, unsigned int expected_http_status, @@ -200,8 +204,7 @@ TALER_TESTING_cmd_auditor_del (const char *label, .cls = ds, .label = label, .run = &auditor_del_run, - .cleanup = &auditor_del_cleanup, - .traits = &auditor_del_traits + .cleanup = &auditor_del_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c index f40d78c88..9477a5d7e 100644 --- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c +++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018 Taler Systems SA + Copyright (C) 2018-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -59,9 +59,9 @@ struct DepositConfirmationState const char *amount_without_fee; /** - * Which coin of the @e deposit_reference should we confirm. + * How many coins were there in the @e deposit_reference? */ - unsigned int coin_index; + unsigned int num_coins; /** * DepositConfirmation handle while operation is running. @@ -69,11 +69,6 @@ struct DepositConfirmationState struct TALER_AUDITOR_DepositConfirmationHandle *dc; /** - * Auditor connection. - */ - struct TALER_AUDITOR_Handle *auditor; - - /** * Interpreter state. */ struct TALER_TESTING_Interpreter *is; @@ -125,8 +120,7 @@ do_retry (void *cls) struct DepositConfirmationState *dcs = cls; dcs->retry_task = NULL; - dcs->is->commands[dcs->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (dcs->is); deposit_confirmation_run (dcs, NULL, dcs->is); @@ -138,13 +132,15 @@ do_retry (void *cls) * to check if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param dcr response details */ static void -deposit_confirmation_cb (void *cls, - const struct TALER_AUDITOR_HttpResponse *hr) +deposit_confirmation_cb ( + void *cls, + const struct TALER_AUDITOR_DepositConfirmationResponse *dcr) { struct DepositConfirmationState *dcs = cls; + const struct TALER_AUDITOR_HttpResponse *hr = &dcr->hr; dcs->dc = NULL; if (dcs->expected_response_code != hr->http_status) @@ -166,21 +162,16 @@ deposit_confirmation_cb (void *cls, else dcs->backoff = GNUNET_TIME_randomized_backoff (dcs->backoff, MAX_BACKOFF); - dcs->is->commands[dcs->is->ip].num_tries++; + TALER_TESTING_inc_tries (dcs->is); 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", - hr->http_status, - dcs->is->commands[dcs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, stderr, 0); - TALER_TESTING_interpreter_fail (dcs->is); + TALER_TESTING_unexpected_status (dcs->is, + hr->http_status, + dcs->expected_response_code); return; } TALER_TESTING_interpreter_next (dcs->is); @@ -199,28 +190,54 @@ deposit_confirmation_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { + static struct TALER_ExtensionPolicyHashP no_h_policy; struct DepositConfirmationState *dcs = cls; const struct TALER_TESTING_Command *deposit_cmd; - struct GNUNET_HashCode h_wire; - struct GNUNET_HashCode h_contract_terms; - const struct GNUNET_TIME_Absolute *exchange_timestamp = NULL; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_TIME_Absolute refund_deadline; + struct TALER_MerchantWireHashP h_wire; + struct TALER_PrivateContractHashP h_contract_terms; + const struct GNUNET_TIME_Timestamp *exchange_timestamp = NULL; + struct GNUNET_TIME_Timestamp timestamp; + const struct GNUNET_TIME_Timestamp *wire_deadline; + struct GNUNET_TIME_Timestamp refund_deadline + = GNUNET_TIME_UNIT_ZERO_TS; struct TALER_Amount amount_without_fee; - struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_CoinSpendPublicKeyP coin_pubs[dcs->num_coins]; + const struct TALER_CoinSpendPublicKeyP *coin_pubps[dcs->num_coins]; + const struct TALER_CoinSpendSignatureP *coin_sigps[dcs->num_coins]; const struct TALER_MerchantPrivateKeyP *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; + const char *auditor_url; (void) cmd; dcs->is = is; GNUNET_assert (NULL != dcs->deposit_reference); + { + const struct TALER_TESTING_Command *auditor_cmd; + + auditor_cmd + = TALER_TESTING_interpreter_get_command (is, + "auditor"); + if (NULL == auditor_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_auditor_url (auditor_cmd, + &auditor_url)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + } deposit_cmd = TALER_TESTING_interpreter_lookup_command (is, dcs->deposit_reference); @@ -233,25 +250,28 @@ deposit_confirmation_run (void *cls, GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_exchange_pub (deposit_cmd, - dcs->coin_index, + 0, &exchange_pub)); GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_exchange_sig (deposit_cmd, - dcs->coin_index, + 0, &exchange_sig)); GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_absolute_time (deposit_cmd, - dcs->coin_index, - &exchange_timestamp)); + TALER_TESTING_get_trait_timestamp (deposit_cmd, + 0, + &exchange_timestamp)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_wire_deadline (deposit_cmd, + 0, + &wire_deadline)); GNUNET_assert (NULL != exchange_timestamp); - keys = TALER_EXCHANGE_get_keys (dcs->is->exchange); + keys = TALER_TESTING_get_keys (is); GNUNET_assert (NULL != keys); spk = TALER_EXCHANGE_get_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); @@ -260,30 +280,44 @@ deposit_confirmation_run (void *cls, &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); + + for (unsigned int i = 0; i<dcs->num_coins; i++) + { + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_coin_priv (deposit_cmd, + i, + &coin_priv)); + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &coin_pubs[i].eddsa_pub); + coin_pubps[i] = &coin_pubs[i]; + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_coin_sig (deposit_cmd, + i, + &coin_sigps[i])); + } GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_merchant_priv (deposit_cmd, - dcs->coin_index, &merchant_priv)); GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_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[] = { - TALER_JSON_spec_absolute_time ("timestamp", ×tamp), + /* timestamp is mandatory */ + GNUNET_JSON_spec_timestamp ("timestamp", + ×tamp), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("refund_deadline", + &refund_deadline), + NULL), GNUNET_JSON_spec_end () }; @@ -296,39 +330,32 @@ deposit_confirmation_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - } - /* refund deadline is optional, defaults to zero */ - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_absolute_time ("refund_deadline", &refund_deadline), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (contract_terms, - spec, - NULL, NULL)) - { + if (GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time)) refund_deadline = timestamp; - } } - dcs->dc = TALER_AUDITOR_deposit_confirmation (dcs->auditor, - &h_wire, - &h_contract_terms, - *exchange_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); + dcs->dc = TALER_AUDITOR_deposit_confirmation ( + TALER_TESTING_interpreter_get_context (is), + auditor_url, + &h_wire, + &no_h_policy, + &h_contract_terms, + *exchange_timestamp, + *wire_deadline, + refund_deadline, + &amount_without_fee, + dcs->num_coins, + coin_pubps, + coin_sigps, + &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) { @@ -355,10 +382,8 @@ deposit_confirmation_cleanup (void *cls, if (NULL != dcs->dc) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - dcs->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (dcs->is, + cmd->label); TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc); dcs->dc = NULL; } @@ -371,61 +396,18 @@ deposit_confirmation_cleanup (void *cls, } -/** - * Offer internal data to other commands. - * - * @param cls closure. - * @param[out] ret 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) -{ - (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; -} - - -/** - * 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, + unsigned int num_coins, const char *amount_without_fee, unsigned int expected_response_code) { struct DepositConfirmationState *dcs; dcs = GNUNET_new (struct DepositConfirmationState); - dcs->auditor = auditor; dcs->deposit_reference = deposit_reference; - dcs->coin_index = coin_index; + dcs->num_coins = num_coins; dcs->amount_without_fee = amount_without_fee; dcs->expected_response_code = expected_response_code; @@ -434,8 +416,7 @@ TALER_TESTING_cmd_deposit_confirmation (const char *label, .cls = dcs, .label = label, .run = &deposit_confirmation_run, - .cleanup = &deposit_confirmation_cleanup, - .traits = &deposit_confirmation_traits + .cleanup = &deposit_confirmation_cleanup }; return cmd; @@ -443,13 +424,6 @@ TALER_TESTING_cmd_deposit_confirmation (const char *label, } -/** - * 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) diff --git a/src/testing/testing_api_cmd_auditor_exchanges.c b/src/testing/testing_api_cmd_auditor_exchanges.c deleted file mode 100644 index 1e412b2d1..000000000 --- a/src/testing/testing_api_cmd_auditor_exchanges.c +++ /dev/null @@ -1,380 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_cmd_auditor_exchanges.c - * @brief command for testing /exchanges of the auditor - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_auditor_service.h" -#include "taler_testing_lib.h" -#include "taler_signatures.h" -#include "backoff.h" - -/** - * How long do we wait AT MOST when retrying? - */ -#define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MILLISECONDS, 100) - - -/** - * How often do we retry before giving up? - */ -#define NUM_RETRIES 5 - - -/** - * 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; - - /** - * How often should we retry on (transient) failures? - */ - unsigned 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; - es->is->commands[es->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); - exchanges_run (es, - NULL, - es->is); -} - - -/** - * Callback to analyze the /exchanges response. - * - * @param cls closure. - * @param hr HTTP response details - * @param num_exchanges length of the @a ei array - * @param ei array with information about the exchanges - */ -static void -exchanges_cb (void *cls, - const struct TALER_AUDITOR_HttpResponse *hr, - unsigned int num_exchanges, - const struct TALER_AUDITOR_ExchangeInfo *ei) -{ - struct ExchangesState *es = cls; - - es->leh = NULL; - if (es->expected_response_code != hr->http_status) - { - if (0 != es->do_retry) - { - es->do_retry--; - if ( (0 == hr->http_status) || - (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying list exchanges failed with %u/%d\n", - hr->http_status, - (int) hr->ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) - es->backoff = GNUNET_TIME_UNIT_ZERO; - else - es->backoff = GNUNET_TIME_randomized_backoff (es->backoff, - MAX_BACKOFF); - es->is->commands[es->is->ip].num_tries++; - es->retry_task = GNUNET_SCHEDULER_add_delayed (es->backoff, - &do_retry, - es); - return; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - es->is->commands[es->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (es->is); - return; - } - if (NULL != es->exchange_url) - { - unsigned int found = GNUNET_NO; - - for (unsigned int i = 0; - i<num_exchanges; - i++) - if (0 == strcmp (es->exchange_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; - - (void) cmd; - 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[out] ret 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) -{ - (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; -} - - -/** - * 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 ExchangesState *es; - - es = GNUNET_new (struct ExchangesState); - es->auditor = auditor; - es->expected_response_code = expected_response_code; - - { - struct TALER_TESTING_Command cmd = { - .cls = es, - .label = label, - .run = &exchanges_run, - .cleanup = &exchanges_cleanup, - .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 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 ExchangesState *es; - - es = GNUNET_new (struct ExchangesState); - es->expected_response_code = expected_response_code; - es->exchange_url = exchange_url; - { - struct TALER_TESTING_Command cmd = { - .cls = es, - .label = label, - .run = &exchanges_run, - .cleanup = &exchanges_cleanup, - .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 = NUM_RETRIES; - return cmd; -} - - -/* end of testing_auditor_api_cmd_exchanges.c */ diff --git a/src/testing/testing_api_cmd_auditor_exec_auditor.c b/src/testing/testing_api_cmd_auditor_exec_auditor.c index 06dfe91bf..588be43d8 100644 --- a/src/testing/testing_api_cmd_auditor_exec_auditor.c +++ b/src/testing/testing_api_cmd_auditor_exec_auditor.c @@ -68,6 +68,7 @@ auditor_run (void *cls, "taler-auditor", "taler-auditor", "-c", ks->config_filename, + "-I", NULL); if (NULL == ks->auditor_proc) { @@ -115,7 +116,7 @@ auditor_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue auditor_traits (void *cls, const void **ret, const char *trait, @@ -123,7 +124,7 @@ auditor_traits (void *cls, { struct AuditorState *ks = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &ks->auditor_proc), + TALER_TESTING_make_trait_process (&ks->auditor_proc), TALER_TESTING_trait_end () }; @@ -134,13 +135,6 @@ auditor_traits (void *cls, } -/** - * 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) diff --git a/src/testing/testing_api_cmd_auditor_exec_auditor_dbinit.c b/src/testing/testing_api_cmd_auditor_exec_auditor_dbinit.c index c09ec2918..2ab5bda8b 100644 --- a/src/testing/testing_api_cmd_auditor_exec_auditor_dbinit.c +++ b/src/testing/testing_api_cmd_auditor_exec_auditor_dbinit.c @@ -124,7 +124,7 @@ auditor_dbinit_traits (void *cls, { struct AuditorDbinitState *ks = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &ks->auditor_dbinit_proc), + TALER_TESTING_make_trait_process (&ks->auditor_dbinit_proc), TALER_TESTING_trait_end () }; @@ -135,13 +135,6 @@ auditor_dbinit_traits (void *cls, } -/** - * 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) diff --git a/src/testing/testing_api_cmd_bank_admin_add_incoming.c b/src/testing/testing_api_cmd_bank_admin_add_incoming.c index 28f907fda..5c031d0b3 100644 --- a/src/testing/testing_api_cmd_bank_admin_add_incoming.c +++ b/src/testing/testing_api_cmd_bank_admin_add_incoming.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2020 Taler Systems SA + Copyright (C) 2018-2021 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 @@ -107,7 +107,7 @@ struct AdminAddIncomingState * the "sender_url" field is set to a 'const char *' and * MUST NOT be free()'ed. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; /** * Set to the wire transfer's unique ID. @@ -117,7 +117,7 @@ struct AdminAddIncomingState /** * Timestamp of the transaction (as returned from the bank). */ - struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Timestamp timestamp; /** * Merchant instance. Sometimes used to get the tip reserve @@ -180,8 +180,7 @@ do_retry (void *cls) struct AdminAddIncomingState *fts = cls; fts->retry_task = NULL; - fts->is->commands[fts->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (fts->is); admin_add_incoming_run (fts, NULL, fts->is); @@ -194,41 +193,54 @@ do_retry (void *cls) * 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 + * @param air response details */ 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) + const struct TALER_BANK_AdminAddIncomingResponse *air) { struct AdminAddIncomingState *fts = cls; struct TALER_TESTING_Interpreter *is = fts->is; - (void) json; - fts->reserve_history.details.in_details.timestamp = timestamp; - fts->reserve_history.details.in_details.wire_reference = serial_id; fts->aih = NULL; - switch (http_status) + /** + * Test case not caring about the HTTP status code. + * That helps when Fakebank and Libeufin diverge in + * the response status code. An example is the + * /admin/add-incoming: libeufin return ALWAYS '200 OK' + * (see note below) whereas the Fakebank responds with + * '409 Conflict' upon a duplicate reserve public key. + * + * Note: this decision aims at avoiding to put Taler + * logic into the Sandbox; that's because banks DO allow + * their customers to wire the same subject multiple + * times. Hence, instead of triggering any error, libeufin + * bounces the payment back in the same way it does for + * malformed reserve public keys. + */ + if (-1 == (int) fts->expected_http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + if (air->http_status != fts->expected_http_status) + { + TALER_TESTING_unexpected_status (is, + air->http_status, + fts->expected_http_status); + return; + } + switch (air->http_status) { case MHD_HTTP_OK: - if (fts->expected_http_status != - MHD_HTTP_OK) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - fts->serial_id = serial_id; - fts->timestamp = timestamp; + fts->reserve_history.details.in_details.timestamp + = air->details.ok.timestamp; + fts->reserve_history.details.in_details.wire_reference + = air->details.ok.serial_id; + fts->serial_id + = air->details.ok.serial_id; + fts->timestamp + = air->details.ok.timestamp; TALER_TESTING_interpreter_next (is); return; case MHD_HTTP_UNAUTHORIZED: @@ -246,35 +258,28 @@ confirmation_cb (void *cls, } break; case MHD_HTTP_CONFLICT: - if (fts->expected_http_status != - MHD_HTTP_CONFLICT) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } TALER_TESTING_interpreter_next (is); return; default: if (0 != fts->do_retry) { fts->do_retry--; - if ( (0 == http_status) || - (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) + if ( (0 == air->http_status) || + (TALER_EC_GENERIC_DB_SOFT_FAILURE == air->ec) || + (MHD_HTTP_INTERNAL_SERVER_ERROR == air->http_status) ) { GNUNET_log ( GNUNET_ERROR_TYPE_INFO, "Retrying fakebank transfer failed with %u/%d\n", - http_status, - (int) ec); + air->http_status, + (int) air->ec); /* on DB conflicts, do not use backoff */ - if (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) + if (TALER_EC_GENERIC_DB_SOFT_FAILURE == air->ec) fts->backoff = GNUNET_TIME_UNIT_ZERO; else fts->backoff = GNUNET_TIME_randomized_backoff (fts->backoff, MAX_BACKOFF); - fts->is->commands[fts->is->ip].num_tries++; + TALER_TESTING_inc_tries (fts->is); fts->retry_task = GNUNET_SCHEDULER_add_delayed ( fts->backoff, &do_retry, @@ -287,8 +292,8 @@ confirmation_cb (void *cls, GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Fakebank returned HTTP status %u/%d\n", - http_status, - (int) ec); + air->http_status, + (int) air->ec); TALER_TESTING_interpreter_fail (is); } @@ -309,6 +314,7 @@ admin_add_incoming_run (void *cls, bool have_public = false; (void) cmd; + fts->is = is; /* Use reserve public key as subject */ if (NULL != fts->reserve_reference) { @@ -326,11 +332,9 @@ admin_add_incoming_run (void *cls, } if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv (ref, - 0, &reserve_priv)) { if (GNUNET_OK != TALER_TESTING_get_trait_reserve_pub (ref, - 0, &reserve_pub)) { GNUNET_break (0); @@ -361,7 +365,6 @@ admin_add_incoming_run (void *cls, fts->reserve_history.amount = fts->amount; fts->reserve_history.details.in_details.sender_url = (char *) fts->payto_debit_account; /* remember to NOT free this one... */ - fts->is = is; fts->aih = TALER_BANK_admin_add_incoming ( TALER_TESTING_interpreter_get_context (is), @@ -395,9 +398,8 @@ admin_add_incoming_cleanup (void *cls, if (NULL != fts->aih) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %s did not complete\n", - cmd->label); + TALER_TESTING_command_incomplete (fts->is, + cmd->label); TALER_BANK_admin_add_incoming_cancel (fts->aih); fts->aih = NULL; } @@ -420,13 +422,14 @@ admin_add_incoming_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue admin_add_incoming_traits (void *cls, const void **ret, const char *trait, unsigned int index) { struct AdminAddIncomingState *fts = cls; + static const char *void_uri = "payto://void/the-exchange"; if (MHD_HTTP_OK != fts->expected_http_status) @@ -435,19 +438,17 @@ admin_add_incoming_traits (void *cls, { struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_bank_row (&fts->serial_id), - TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT, - fts->payto_debit_account), + TALER_TESTING_make_trait_debit_payto_uri (fts->payto_debit_account), + TALER_TESTING_make_trait_payto_uri (fts->payto_debit_account), /* Used as a marker, content does not matter */ - TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT, - "payto://void/the-exchange"), - TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BANK_ACCOUNT_URL, - 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_make_trait_credit_payto_uri (void_uri), + TALER_TESTING_make_trait_exchange_bank_account_url ( + fts->exchange_credit_url), + TALER_TESTING_make_trait_amount (&fts->amount), + TALER_TESTING_make_trait_timestamp (0, + &fts->timestamp), + TALER_TESTING_make_trait_reserve_priv (&fts->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (&fts->reserve_pub), TALER_TESTING_make_trait_reserve_history (0, &fts->reserve_history), TALER_TESTING_trait_end () @@ -462,17 +463,15 @@ admin_add_incoming_traits (void *cls, { struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_bank_row (&fts->serial_id), - TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT, - fts->payto_debit_account), + TALER_TESTING_make_trait_debit_payto_uri (fts->payto_debit_account), /* Used as a marker, content does not matter */ - TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT, - "payto://void/the-exchange"), - TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BANK_ACCOUNT_URL, - 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_pub (0, - &fts->reserve_pub), + TALER_TESTING_make_trait_credit_payto_uri (void_uri), + TALER_TESTING_make_trait_exchange_bank_account_url ( + fts->exchange_credit_url), + TALER_TESTING_make_trait_amount (&fts->amount), + TALER_TESTING_make_trait_timestamp (0, + &fts->timestamp), + TALER_TESTING_make_trait_reserve_pub (&fts->reserve_pub), TALER_TESTING_make_trait_reserve_history (0, &fts->reserve_history), TALER_TESTING_trait_end () @@ -543,11 +542,11 @@ make_command (const char *label, struct TALER_TESTING_Command -TALER_TESTING_cmd_admin_add_incoming (const char *label, - const char *amount, - const struct - TALER_BANK_AuthenticationData *auth, - const char *payto_debit_account) +TALER_TESTING_cmd_admin_add_incoming ( + const char *label, + const char *amount, + const struct TALER_BANK_AuthenticationData *auth, + const char *payto_debit_account) { return make_command (label, make_fts (amount, diff --git a/src/testing/testing_api_cmd_bank_admin_check.c b/src/testing/testing_api_cmd_bank_admin_check.c index 473f3f3f2..6406fe2c2 100644 --- a/src/testing/testing_api_cmd_bank_admin_check.c +++ b/src/testing/testing_api_cmd_bank_admin_check.c @@ -82,8 +82,30 @@ check_bank_admin_transfer_run (void *cls, const char *credit_payto; const struct TALER_ReservePublicKeyP *reserve_pub; const struct TALER_TESTING_Command *cmd_ref; + struct TALER_FAKEBANK_Handle *fakebank; (void) cmd; + { + const struct TALER_TESTING_Command *fakebank_cmd; + + fakebank_cmd + = TALER_TESTING_interpreter_get_command (is, + "fakebank"); + if (NULL == fakebank_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_fakebank (fakebank_cmd, + &fakebank)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + } cmd_ref = TALER_TESTING_interpreter_lookup_command (is, bcs->reserve_pub_ref); @@ -95,7 +117,6 @@ check_bank_admin_transfer_run (void *cls, } if (GNUNET_OK != TALER_TESTING_get_trait_reserve_pub (cmd_ref, - 0, &reserve_pub)) { GNUNET_break (0); @@ -103,7 +124,6 @@ check_bank_admin_transfer_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - TALER_LOG_INFO ("Deposit reference NOT given\n"); debit_payto = bcs->debit_payto; credit_payto = bcs->credit_payto; if (GNUNET_OK != @@ -111,9 +131,9 @@ check_bank_admin_transfer_run (void *cls, &amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", + "Failed to parse amount `%s' at %s\n", bcs->amount, - is->ip); + TALER_TESTING_interpreter_get_current_label (is)); TALER_TESTING_interpreter_fail (is); return; } @@ -124,7 +144,7 @@ check_bank_admin_transfer_run (void *cls, debit_payto, debit_account); if (GNUNET_OK != - TALER_FAKEBANK_check_credit (is->fakebank, + TALER_FAKEBANK_check_credit (fakebank, &amount, debit_account, credit_account, @@ -160,33 +180,6 @@ check_bank_admin_transfer_cleanup (void *cls, /** - * Offer internal data from a "bank admin check" CMD state. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -check_bank_admin_transfer_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_trait_end () - }; - - (void) cls; - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** * Make a "bank check" CMD. It checks whether a particular wire transfer to * the exchange (credit) has been made or not. * @@ -217,8 +210,7 @@ TALER_TESTING_cmd_check_bank_admin_transfer .label = label, .cls = bcs, .run = &check_bank_admin_transfer_run, - .cleanup = &check_bank_admin_transfer_cleanup, - .traits = &check_bank_admin_transfer_traits + .cleanup = &check_bank_admin_transfer_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_bank_check.c b/src/testing/testing_api_cmd_bank_check.c index c01bc709f..77d120e09 100644 --- a/src/testing/testing_api_cmd_bank_check.c +++ b/src/testing/testing_api_cmd_bank_check.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2020 Taler Systems SA + Copyright (C) 2018-2022 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 @@ -74,6 +74,7 @@ struct BankCheckState const char *deposit_reference; }; + /** * Run the command. * @@ -93,8 +94,30 @@ check_bank_transfer_run (void *cls, const char *exchange_base_url; const char *debit_payto; const char *credit_payto; + struct TALER_FAKEBANK_Handle *fakebank; (void) cmd; + { + const struct TALER_TESTING_Command *fakebank_cmd; + + fakebank_cmd + = TALER_TESTING_interpreter_get_command (is, + "fakebank"); + if (NULL == fakebank_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_fakebank (fakebank_cmd, + &fakebank)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + } if (NULL == bcs->deposit_reference) { TALER_LOG_INFO ("Deposit reference NOT given\n"); @@ -107,9 +130,9 @@ check_bank_transfer_run (void *cls, &amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", + "Failed to parse amount `%s' at %s\n", bcs->amount, - is->ip); + TALER_TESTING_interpreter_get_current_label (is)); TALER_TESTING_interpreter_fail (is); return; } @@ -130,41 +153,32 @@ check_bank_transfer_run (void *cls, if (NULL == deposit_cmd) TALER_TESTING_FAIL (is); if ( (GNUNET_OK != - TALER_TESTING_get_trait_amount_obj (deposit_cmd, - 0, - &amount_ptr)) || + TALER_TESTING_get_trait_amount (deposit_cmd, + &amount_ptr)) || (GNUNET_OK != - TALER_TESTING_get_trait_payto (deposit_cmd, - TALER_TESTING_PT_DEBIT, - &debit_payto)) || + TALER_TESTING_get_trait_debit_payto_uri (deposit_cmd, + &debit_payto)) || (GNUNET_OK != - TALER_TESTING_get_trait_payto (deposit_cmd, - TALER_TESTING_PT_CREDIT, - &credit_payto)) || + TALER_TESTING_get_trait_credit_payto_uri (deposit_cmd, + &credit_payto)) || (GNUNET_OK != - TALER_TESTING_get_trait_url (deposit_cmd, - TALER_TESTING_UT_EXCHANGE_BASE_URL, - &exchange_base_url)) ) + TALER_TESTING_get_trait_exchange_url (deposit_cmd, + &exchange_base_url)) ) TALER_TESTING_FAIL (is); amount = *amount_ptr; } - - debit_account = TALER_xtalerbank_account_from_payto (debit_payto); credit_account = TALER_xtalerbank_account_from_payto (credit_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "converted debit_payto (%s) to debit_account (%s)\n", debit_payto, debit_account); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "converted credit_payto (%s) to credit_account (%s)\n", credit_payto, credit_account); - if (GNUNET_OK != - TALER_FAKEBANK_check_debit (is->fakebank, + TALER_FAKEBANK_check_debit (fakebank, &amount, debit_account, credit_account, @@ -209,7 +223,7 @@ check_bank_transfer_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue check_bank_transfer_traits (void *cls, const void **ret, const char *trait, @@ -218,10 +232,9 @@ check_bank_transfer_traits (void *cls, struct BankCheckState *bcs = cls; struct TALER_WireTransferIdentifierRawP *wtid_ptr = &bcs->wtid; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_wtid (0, - wtid_ptr), - TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BASE_URL, - bcs->exchange_base_url), + TALER_TESTING_make_trait_wtid (wtid_ptr), + TALER_TESTING_make_trait_exchange_url ( + bcs->exchange_base_url), TALER_TESTING_trait_end () }; @@ -232,19 +245,6 @@ check_bank_transfer_traits (void *cls, } -/** - * Make a "bank check" CMD. It checks whether a - * particular wire transfer has been made or not. - * - * @param label the command label. - * @param exchange_base_url base url of the exchange involved in - * the wire transfer. - * @param amount the amount expected to be transferred. - * @param debit_payto the account that gave money. - * @param credit_payto the account that received money. - * - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_check_bank_transfer (const char *label, const char *exchange_base_url, @@ -274,19 +274,9 @@ TALER_TESTING_cmd_check_bank_transfer (const char *label, } -/** - * Define a "bank check" CMD that takes the input - * data from another CMD that offers it. - * - * @param label command label. - * @param deposit_reference reference to a CMD that is - * able to provide the "check bank transfer" operation - * input data. - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_check_bank_transfer_with_ref - (const char *label, +TALER_TESTING_cmd_check_bank_transfer_with_ref ( + const char *label, const char *deposit_reference) { struct BankCheckState *bcs; diff --git a/src/testing/testing_api_cmd_bank_check_empty.c b/src/testing/testing_api_cmd_bank_check_empty.c index 84976b0b5..60f00fbb4 100644 --- a/src/testing/testing_api_cmd_bank_check_empty.c +++ b/src/testing/testing_api_cmd_bank_check_empty.c @@ -58,9 +58,33 @@ check_bank_empty_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { + struct TALER_FAKEBANK_Handle *fakebank; + (void) cls; (void) cmd; - if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank)) + { + const struct TALER_TESTING_Command *fakebank_cmd; + + fakebank_cmd + = TALER_TESTING_interpreter_get_command (is, + "fakebank"); + if (NULL == fakebank_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_fakebank (fakebank_cmd, + &fakebank)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + } + if (GNUNET_OK != + TALER_FAKEBANK_check_empty (fakebank)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); diff --git a/src/testing/testing_api_cmd_bank_history_credit.c b/src/testing/testing_api_cmd_bank_history_credit.c index c052b55fb..956e6c857 100644 --- a/src/testing/testing_api_cmd_bank_history_credit.c +++ b/src/testing/testing_api_cmd_bank_history_credit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2020 Taler Systems SA + Copyright (C) 2018-2021 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 @@ -83,6 +83,11 @@ struct HistoryState struct TALER_BANK_CreditHistoryHandle *hh; /** + * The interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** * Authentication data for the operation. */ struct TALER_BANK_AuthenticationData auth; @@ -93,10 +98,10 @@ struct HistoryState uint64_t results_obtained; /** - * Set to GNUNET_YES if the callback detects something + * Set to true if the callback detects something * unexpected. */ - int failed; + bool failed; /** * Expected history. @@ -112,32 +117,6 @@ struct HistoryState /** - * Offer internal data to other commands. - * - * @param cls closure. - * @param[out] ret 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; -} - - -/** * Log which history we expected. Called when an error occurs. * * @param h what we expected. @@ -170,38 +149,161 @@ print_expected (struct History *h, /** + * Closure for command_cb(). + */ +struct IteratorContext +{ + /** + * Array of history items to return. + */ + struct History *h; + + /** + * Set to the row ID from where on we should actually process history items, + * or NULL if we should process all of them. + */ + const uint64_t *row_id_start; + + /** + * History state we are working on. + */ + struct HistoryState *hs; + + /** + * Current length of the @e h array. + */ + unsigned int total; + + /** + * Current write position in @e h array. + */ + unsigned int pos; + + /** + * Ok equals True whenever a starting row_id was provided AND was found + * among the CMDs, OR no starting row was given in the first place. + */ + bool ok; + +}; + + +/** + * Helper function of build_history() that expands + * the history for each relevant command encountered. + * + * @param[in,out] cls our `struct IteratorContext` + * @param cmd a command to process + */ +static void +command_cb (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct IteratorContext *ic = cls; + struct HistoryState *hs = ic->hs; + const uint64_t *row_id; + const char *credit_account; + const char *debit_account; + const struct TALER_Amount *amount; + const struct TALER_ReservePublicKeyP *reserve_pub; + const char *exchange_credit_url; + + /** + * 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_bank_row (cmd, + &row_id)) || + (GNUNET_OK != + TALER_TESTING_get_trait_credit_payto_uri (cmd, + &credit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_debit_payto_uri (cmd, + &debit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_amount (cmd, + &amount)) || + (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (cmd, + &reserve_pub)) || + (GNUNET_OK != + TALER_TESTING_get_trait_exchange_bank_account_url ( + cmd, + &exchange_credit_url)) ) + return; // Not an interesting event + + /** + * Is the interesting event a match with regard to + * the row_id value? If yes, store this condition + * to the state and analyze the next CMDs. + */ + if ( (NULL != ic->row_id_start) && + (*(ic->row_id_start) == *row_id) && + (! ic->ok) ) + { + ic->ok = true; + return; + } + /** + * The interesting event didn't match the wanted + * row_id value, analyze the next CMDs. Note: this + * branch is relevant only when row_id WAS given. + */ + if (! ic->ok) + return; + if (0 != strcasecmp (hs->account_url, + exchange_credit_url)) + return; // Account mismatch + if (ic->total >= GNUNET_MAX (hs->num_results, + -hs->num_results) ) + { + TALER_LOG_DEBUG ("Hit history limit\n"); + return; + } + TALER_LOG_INFO ("Found history: %s->%s for account %s\n", + debit_account, + credit_account, + hs->account_url); + /* found matching record, make sure we have room */ + if (ic->pos == ic->total) + GNUNET_array_grow (ic->h, + ic->total, + ic->pos * 2); + ic->h[ic->pos].url = GNUNET_strdup (debit_account); + ic->h[ic->pos].details.debit_account_uri = ic->h[ic->pos].url; + ic->h[ic->pos].details.amount = *amount; + ic->h[ic->pos].row_id = *row_id; + ic->h[ic->pos].details.reserve_pub = *reserve_pub; + ic->pos++; +} + + +/** * 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 hs history state * @param[out] rh history array to initialize. * @return number of entries in @a rh. */ static unsigned int -build_history (struct TALER_TESTING_Interpreter *is, +build_history (struct HistoryState *hs, struct History **rh) { - struct HistoryState *hs = is->commands[is->ip].cls; - unsigned int total; - unsigned int pos; - 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; + struct TALER_TESTING_Interpreter *is = hs->is; + struct IteratorContext ic = { + .hs = hs + }; if (NULL != hs->start_row_reference) { + const struct TALER_TESTING_Command *add_incoming_cmd; + TALER_LOG_INFO ("`%s': start row given via reference `%s'\n", TALER_TESTING_interpreter_get_current_label (is), hs->start_row_reference); @@ -210,124 +312,92 @@ build_history (struct TALER_TESTING_Interpreter *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)); + TALER_TESTING_get_trait_row (add_incoming_cmd, + &ic.row_id_start)); } + ic.ok = false; + if (NULL == ic.row_id_start) + ic.ok = true; + GNUNET_array_grow (ic.h, + ic.total, + 4); GNUNET_assert (0 != hs->num_results); - if (0 == is->ip) - { - TALER_LOG_DEBUG ("Checking history at FIRST transaction (EMPTY)\n"); - *rh = NULL; - return 0; - } + TALER_TESTING_iterate (is, + hs->num_results > 0, + &command_cb, + &ic); + GNUNET_assert (ic.ok); + GNUNET_array_grow (ic.h, + ic.total, + ic.pos); + if (0 == ic.pos) + TALER_LOG_DEBUG ("Empty credit history computed\n"); + *rh = ic.h; + return ic.pos; +} + - if (hs->num_results > 0) +/** + * Normalize IBAN-based payto URI in @a in. + * + * @param in input payto://-URI to normalize + * @return normalized IBAN for the test + */ +static char * +normalize (const char *in) +{ + char *npt; + const char *q = strchr (in, + '?'); + const char *mptr; + const char *bic; + const char *iban; + + if (NULL == q) + npt = GNUNET_strdup (in); + else + npt = GNUNET_strndup (in, + q - in); + if (0 != strncasecmp (npt, + "payto://", + strlen ("payto://"))) { - inc = 1; /* _inc_rement */ - start = 0; - end = is->ip - 1; + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - else + mptr = npt + strlen ("payto://"); + bic = strchr (mptr, '/'); + if (NULL == bic) { - inc = -1; - start = is->ip - 1; - end = 0; + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - - ok = GNUNET_NO; - if (NULL == row_id_start) - ok = GNUNET_YES; - h = NULL; - total = 0; - GNUNET_array_grow (h, - total, - 4); - pos = 0; - for (unsigned int off = start; off != end + inc; off += inc) + bic++; + iban = strchr (bic, '/'); + if (NULL != iban) { - const struct TALER_TESTING_Command *cmd = &is->commands[off]; - const uint64_t *row_id; - const char *credit_account; - const char *debit_account; - const struct TALER_Amount *amount; - const struct TALER_ReservePublicKeyP *reserve_pub; - const char *exchange_credit_url; - - /* 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_bank_row (cmd, - &row_id)) || - (GNUNET_OK != - TALER_TESTING_get_trait_payto (cmd, - TALER_TESTING_PT_CREDIT, - &credit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_payto (cmd, - TALER_TESTING_PT_DEBIT, - &debit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_amount_obj (cmd, - 0, - &amount)) || - (GNUNET_OK != - TALER_TESTING_get_trait_reserve_pub (cmd, - 0, - &reserve_pub)) || - (GNUNET_OK != - TALER_TESTING_get_trait_url (cmd, - TALER_TESTING_UT_EXCHANGE_BANK_ACCOUNT_URL, - &exchange_credit_url)) ) - continue; /* not an interesting event */ - /* Seek "/history/incoming" starting row. */ - if ( (NULL != row_id_start) && - (*row_id_start == *row_id) && - (GNUNET_NO == ok) ) - { - /* Until here, nothing counted. */ - ok = GNUNET_YES; - continue; - } - /* when 'start' was _not_ given, then ok == GNUNET_YES */ - if (GNUNET_NO == ok) - continue; /* skip until we find the marker */ - if (0 != strcasecmp (hs->account_url, - exchange_credit_url)) - continue; /* account mismatch */ - if (total >= GNUNET_MAX (hs->num_results, - -hs->num_results) ) - { - TALER_LOG_DEBUG ("Hit history limit\n"); - break; - } - TALER_LOG_INFO ("Found history: %s->%s for account %s\n", - debit_account, - credit_account, - hs->account_url); - /* found matching record, make sure we have room */ - if (pos == total) - GNUNET_array_grow (h, - total, - pos * 2); - h[pos].url = GNUNET_strdup (debit_account); - h[pos].details.debit_account_uri = h[pos].url; - h[pos].details.amount = *amount; - h[pos].row_id = *row_id; - h[pos].details.reserve_pub = *reserve_pub; - h[pos].details.credit_account_uri = exchange_credit_url; - pos++; + /* need to remove bic */ + char *n; + + iban++; + GNUNET_asprintf (&n, + "payto://%.*s/%s", + (int) ((bic - mptr) - 1), + mptr, + iban); + GNUNET_free (npt); + npt = n; } - GNUNET_assert (GNUNET_YES == ok); - GNUNET_array_grow (h, - total, - pos); - if (0 == pos) - TALER_LOG_DEBUG ("Empty credit history computed\n"); - *rh = h; - return total; + return npt; } @@ -342,12 +412,15 @@ build_history (struct TALER_TESTING_Interpreter *is, * @param details the expected transaction details. * @return #GNUNET_OK if the transaction is what we expect. */ -static int +static enum GNUNET_GenericReturnValue check_result (struct History *h, unsigned int total, unsigned int off, const struct TALER_BANK_CreditDetails *details) { + char *u1; + char *u2; + if (off >= total) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -360,25 +433,42 @@ check_result (struct History *h, off); return GNUNET_SYSERR; } + u1 = normalize (h[off].details.debit_account_uri); + if (NULL == u1) + return GNUNET_SYSERR; + u2 = normalize (details->debit_account_uri); + if (NULL == u2) + { + GNUNET_free (u1); + 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.debit_account_uri, - details->debit_account_uri)) ) + (0 != strcasecmp (u1, + u2)) ) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "expected debit_account_uri: %s\n", - details->debit_account_uri); + "expected debit_account_uri: %s with %s for %s\n", + u1, + TALER_amount2s (&h[off].details.amount), + TALER_B2S (&h[off].details.reserve_pub)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "actual debit_account_uri: %s\n", - h[off].details.debit_account_uri); + "actual debit_account_uri: %s with %s for %s\n", + u2, + TALER_amount2s (&details->amount), + TALER_B2S (&details->reserve_pub)); print_expected (h, total, off); + GNUNET_free (u1); + GNUNET_free (u2); return GNUNET_SYSERR; } + GNUNET_free (u1); + GNUNET_free (u2); return GNUNET_OK; } @@ -391,89 +481,86 @@ check_result (struct History *h, * 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 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 + * @param chr http response details */ -static int +static void 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) + const struct TALER_BANK_CreditHistoryResponse *chr) { - struct TALER_TESTING_Interpreter *is = cls; - struct HistoryState *hs = is->commands[is->ip].cls; + struct HistoryState *hs = cls; + struct TALER_TESTING_Interpreter *is = hs->is; - (void) row_id; - if (NULL == details) + hs->hh = NULL; + switch (chr->http_status) { - hs->hh = NULL; - if ( (hs->results_obtained != hs->total) || - (GNUNET_YES == hs->failed) || - (MHD_HTTP_NO_CONTENT != http_status) ) + case 0: + GNUNET_break (0); + goto error; + case MHD_HTTP_OK: + for (unsigned int i = 0; i<chr->details.ok.details_length; i++) { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expected history of length %u, got %llu;" - " HTTP status code: %u/%d, failed: %d\n", - hs->total, - (unsigned long long) hs->results_obtained, - http_status, - (int) ec, - hs->failed); - print_expected (hs->h, - hs->total, - UINT_MAX); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; + const struct TALER_BANK_CreditDetails *cd = + &chr->details.ok.details[i]; + + /* check current element */ + if (GNUNET_OK != + check_result (hs->h, + hs->total, + hs->results_obtained, + cd)) + { + GNUNET_break (0); + json_dumpf (chr->response, + stderr, + JSON_COMPACT); + hs->failed = true; + hs->hh = NULL; + TALER_TESTING_interpreter_fail (is); + return; + } + hs->results_obtained++; } TALER_TESTING_interpreter_next (is); - return GNUNET_OK; - } - if (MHD_HTTP_OK != http_status) - { + return; + case MHD_HTTP_NO_CONTENT: + if (0 == hs->total) + { + /* not found is OK for empty history */ + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_break (0); + goto error; + case MHD_HTTP_NOT_FOUND: + if (0 == hs->total) + { + /* not found is OK for empty history */ + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_break (0); + goto error; + default: hs->hh = NULL; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unwanted response code from /history/incoming: %u\n", - http_status); + chr->http_status); TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - - /* check current element */ - if (GNUNET_OK != check_result (hs->h, - hs->total, - 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; + return; } - hs->results_obtained++; - return GNUNET_OK; +error: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected history of length %u, got %llu;" + " HTTP status code: %u/%d, failed: %d\n", + hs->total, + (unsigned long long) hs->results_obtained, + chr->http_status, + (int) chr->ec, + hs->failed ? 1 : 0); + print_expected (hs->h, + hs->total, + UINT_MAX); + TALER_TESTING_interpreter_fail (is); } @@ -494,36 +581,37 @@ history_run (void *cls, const uint64_t *row_ptr; (void) cmd; + hs->is = is; /* 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); - + 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_get_trait_row (history_cmd, + &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->total = build_history (is, + hs->total = build_history (hs, &hs->h); - hs->hh = TALER_BANK_credit_history (is->ctx, - &hs->auth, - row_id, - hs->num_results, - GNUNET_TIME_UNIT_ZERO, - &history_cb, - is); + hs->hh = TALER_BANK_credit_history ( + TALER_TESTING_interpreter_get_context (is), + &hs->auth, + row_id, + hs->num_results, + GNUNET_TIME_UNIT_ZERO, + &history_cb, + hs); GNUNET_assert (NULL != hs->hh); } @@ -544,7 +632,8 @@ history_cleanup (void *cls, (void) cmd; if (NULL != hs->hh) { - TALER_LOG_WARNING ("/history/incoming did not complete\n"); + TALER_TESTING_command_incomplete (hs->is, + cmd->label); TALER_BANK_credit_history_cancel (hs->hh); } GNUNET_free (hs->account_url); @@ -574,8 +663,7 @@ TALER_TESTING_cmd_bank_credits ( .label = label, .cls = hs, .run = &history_run, - .cleanup = &history_cleanup, - .traits = &history_traits + .cleanup = &history_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_bank_history_debit.c b/src/testing/testing_api_cmd_bank_history_debit.c index 779facee8..1cb7320fa 100644 --- a/src/testing/testing_api_cmd_bank_history_debit.c +++ b/src/testing/testing_api_cmd_bank_history_debit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2020 Taler Systems SA + Copyright (C) 2018-2021 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 @@ -92,6 +92,11 @@ struct HistoryState struct TALER_BANK_DebitHistoryHandle *hh; /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** * Expected number of results (= rows). */ uint64_t results_obtained; @@ -116,32 +121,6 @@ struct HistoryState /** - * Offer internal data to other commands. - * - * @param cls closure. - * @param[out] ret 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; -} - - -/** * Log which history we expected. Called when an error occurs. * * @param h what we expected. @@ -173,169 +152,248 @@ print_expected (struct History *h, /** + * Closure for command_cb(). + */ +struct IteratorContext +{ + /** + * Array of history items to return. + */ + struct History *h; + + /** + * Set to the row ID from where on we should actually process history items, + * or NULL if we should process all of them. + */ + const uint64_t *row_id_start; + + /** + * History state we are working on. + */ + struct HistoryState *hs; + + /** + * Current length of the @e h array. + */ + unsigned int total; + + /** + * Current write position in @e h array. + */ + unsigned int pos; + + /** + * Ok equals True whenever a starting row_id was provided AND was found + * among the CMDs, OR no starting row was given in the first place. + */ + bool ok; + +}; + + +/** + * Helper function of build_history() that expands + * the history for each relevant command encountered. + * + * @param[in,out] cls our `struct IteratorContext` + * @param cmd a command to process + */ +static void +command_cb (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct IteratorContext *ic = cls; + struct HistoryState *hs = ic->hs; + + const uint64_t *row_id; + const char *debit_account; + const char *credit_account; + const struct TALER_Amount *amount; + const struct TALER_WireTransferIdentifierRawP *wtid; + const char *exchange_base_url; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Checking if command %s is relevant for debit history\n", + cmd->label); + if ( (GNUNET_OK != + TALER_TESTING_get_trait_bank_row (cmd, + &row_id)) || + (GNUNET_OK != + TALER_TESTING_get_trait_debit_payto_uri (cmd, + &debit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_credit_payto_uri (cmd, + &credit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_amount (cmd, + &amount)) || + (GNUNET_OK != + TALER_TESTING_get_trait_wtid (cmd, + &wtid)) || + (GNUNET_OK != + TALER_TESTING_get_trait_exchange_url (cmd, + &exchange_base_url)) ) + return; /* not an event we care about */ + /* Seek "/history/outgoing" starting row. */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Command %s is relevant for debit history!\n", + cmd->label); + if ( (NULL != ic->row_id_start) && + (*(ic->row_id_start) == *row_id) && + (! ic->ok) ) + { + /* Until here, nothing counted. */ + ic->ok = true; + return; + } + /* when 'start' was _not_ given, then ok == GNUNET_YES */ + if (! ic->ok) + return; /* skip until we find the marker */ + if (ic->total >= GNUNET_MAX (hs->num_results, + -hs->num_results) ) + { + TALER_LOG_DEBUG ("Hit history limit\n"); + return; + } + TALER_LOG_INFO ("Found history: %s->%s for account %s\n", + debit_account, + credit_account, + hs->account_url); + /* found matching record, make sure we have room */ + if (ic->pos == ic->total) + GNUNET_array_grow (ic->h, + ic->total, + ic->pos * 2); + ic->h[ic->pos].c_url = GNUNET_strdup (credit_account); + ic->h[ic->pos].d_url = GNUNET_strdup (debit_account); + ic->h[ic->pos].details.credit_account_uri = ic->h[ic->pos].c_url; + ic->h[ic->pos].details.amount = *amount; + ic->h[ic->pos].row_id = *row_id; + ic->h[ic->pos].details.wtid = *wtid; + ic->h[ic->pos].details.exchange_base_url = exchange_base_url; + ic->pos++; +} + + +/** * 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 hs history state command context * @param[out] rh history array to initialize. * @return number of entries in @a rh. */ static unsigned int -build_history (struct TALER_TESTING_Interpreter *is, +build_history (struct HistoryState *hs, struct History **rh) { - struct HistoryState *hs = is->commands[is->ip].cls; - unsigned int total; - unsigned int pos; - struct History *h; - const struct TALER_TESTING_Command *add_incoming_cmd; - int inc; - int start; - int end; - /* #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; + struct TALER_TESTING_Interpreter *is = hs->is; + struct IteratorContext ic = { + .hs = hs + }; if (NULL != hs->start_row_reference) { - TALER_LOG_INFO - ("`%s': start row given via reference `%s'\n", + const struct TALER_TESTING_Command *add_incoming_cmd; + + 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); + 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)); + TALER_TESTING_get_trait_row (add_incoming_cmd, + &ic.row_id_start)); } + ic.ok = false; + if (NULL == ic.row_id_start) + ic.ok = true; + GNUNET_array_grow (ic.h, + ic.total, + 4); GNUNET_assert (0 != hs->num_results); - if (0 == is->ip) - { - TALER_LOG_DEBUG ("Checking history at first CMD..\n"); - *rh = NULL; - return 0; - } + TALER_TESTING_iterate (is, + hs->num_results > 0, + &command_cb, + &ic); + GNUNET_assert (ic.ok); + GNUNET_array_grow (ic.h, + ic.total, + ic.pos); + if (0 == ic.pos) + TALER_LOG_DEBUG ("Empty credit history computed\n"); + *rh = ic.h; + return ic.pos; +} - /* AKA 'delta' */ - if (hs->num_results > 0) - { - inc = 1; /* _inc_rement: go forwards */ - start = 0; - end = is->ip; - } + +/** + * Normalize IBAN-based payto URI in @a in. + * + * @param in input payto://-URI to normalize + * @return normalized IBAN for the test + */ +static char * +normalize (const char *in) +{ + char *npt; + const char *q = strchr (in, + '?'); + const char *mptr; + const char *bic; + const char *iban; + + if (NULL == q) + npt = GNUNET_strdup (in); else + npt = GNUNET_strndup (in, + q - in); + if (0 != strncasecmp (npt, + "payto://", + strlen ("payto://"))) { - inc = -1; /* decrement: we go backwards */ - start = is->ip - 1; - end = -1; /* range is exclusive, do look at 0! */ + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - - ok = GNUNET_NO; - if (NULL == row_id_start) - ok = GNUNET_YES; - h = NULL; - total = 0; - GNUNET_array_grow (h, - total, - 4); - pos = 0; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Checking commands %u to %u for debit history\n", - start, - end); - for (int off = start; off != end; off += inc) + mptr = npt + strlen ("payto://"); + bic = strchr (mptr, '/'); + if (NULL == bic) { - const struct TALER_TESTING_Command *cmd = &is->commands[off]; - const uint64_t *row_id; - const char *debit_account; - const char *credit_account; - const struct TALER_Amount *amount; - const struct TALER_WireTransferIdentifierRawP *wtid; - const char *exchange_base_url; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Checking if command %s is relevant for debit history\n", - cmd->label); - if ( (GNUNET_OK != - TALER_TESTING_get_trait_bank_row (cmd, - &row_id)) || - (GNUNET_OK != - TALER_TESTING_get_trait_payto (cmd, - TALER_TESTING_PT_DEBIT, - &debit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_payto (cmd, - TALER_TESTING_PT_CREDIT, - &credit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_amount_obj (cmd, - 0, - &amount)) || - (GNUNET_OK != - TALER_TESTING_get_trait_wtid (cmd, - 0, - &wtid)) || - (GNUNET_OK != - TALER_TESTING_get_trait_url (cmd, - TALER_TESTING_UT_EXCHANGE_BASE_URL, - &exchange_base_url)) ) - continue; /* not an event we care about */ - /* Seek "/history/outgoing" starting row. */ + GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Command %s is relevant for debit history!\n", - cmd->label); - if ( (NULL != row_id_start) && - (*row_id_start == *row_id) && - (GNUNET_NO == ok) ) - { - /* Until here, nothing counted. */ - ok = GNUNET_YES; - continue; - } - /* when 'start' was _not_ given, then ok == GNUNET_YES */ - if (GNUNET_NO == ok) - continue; /* skip until we find the marker */ - if (total >= GNUNET_MAX (hs->num_results, - -hs->num_results) ) - { - TALER_LOG_DEBUG ("Hit history limit\n"); - break; - } - TALER_LOG_INFO ("Found history: %s->%s for account %s\n", - debit_account, - credit_account, - hs->account_url); - /* found matching record, make sure we have room */ - if (pos == total) - GNUNET_array_grow (h, - total, - pos * 2); - h[pos].c_url = GNUNET_strdup (credit_account); - h[pos].d_url = GNUNET_strdup (debit_account); - h[pos].details.credit_account_uri = h[pos].c_url; - h[pos].details.debit_account_uri = h[pos].d_url; - h[pos].details.amount = *amount; - h[pos].row_id = *row_id; - h[pos].details.wtid = *wtid; - h[pos].details.exchange_base_url = exchange_base_url; - pos++; + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; + } + bic++; + iban = strchr (bic, '/'); + if (NULL != iban) + { + /* need to remove bic */ + char *n; + + iban++; + GNUNET_asprintf (&n, + "payto://%.*s/%s", + (int) ((bic - mptr) - 1), + mptr, + iban); + GNUNET_free (npt); + npt = n; } - GNUNET_assert (GNUNET_YES == ok); - GNUNET_array_grow (h, - total, - pos); - if (0 == pos) - TALER_LOG_DEBUG ("Empty debit history computed\n"); - *rh = h; - return total; + return npt; } @@ -350,12 +408,15 @@ build_history (struct TALER_TESTING_Interpreter *is, * @param details the expected transaction details. * @return #GNUNET_OK if the transaction is what we expect. */ -static int +static enum GNUNET_GenericReturnValue check_result (struct History *h, uint64_t total, unsigned int off, const struct TALER_BANK_DebitDetails *details) { + char *u1; + char *u2; + if (off >= total) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -368,14 +429,33 @@ check_result (struct History *h, off); return GNUNET_SYSERR; } + u1 = normalize (h[off].details.credit_account_uri); + if (NULL == u1) + return GNUNET_SYSERR; + u2 = normalize (details->credit_account_uri); + if (NULL == u2) + { + GNUNET_free (u1); + 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.credit_account_uri, - details->credit_account_uri)) ) + (0 != strcasecmp (u1, + u2)) ) { GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "expected debit_account_uri: %s with %s for %s\n", + u1, + TALER_amount2s (&h[off].details.amount), + TALER_B2S (&h[off].details.wtid)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "actual debit_account_uri: %s with %s for %s\n", + u2, + TALER_amount2s (&details->amount), + TALER_B2S (&details->wtid)); print_expected (h, total, off); @@ -393,89 +473,86 @@ check_result (struct History *h, * 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 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 + * @param dhr http response details */ -static int +static void 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) + const struct TALER_BANK_DebitHistoryResponse *dhr) { - struct TALER_TESTING_Interpreter *is = cls; - struct HistoryState *hs = is->commands[is->ip].cls; + struct HistoryState *hs = cls; + struct TALER_TESTING_Interpreter *is = hs->is; - (void) row_id; - if (NULL == details) + hs->hh = NULL; + switch (dhr->http_status) { - hs->hh = NULL; - if ( (hs->results_obtained != hs->total) || - (GNUNET_YES == hs->failed) || - (MHD_HTTP_NO_CONTENT != http_status) ) + case 0: + GNUNET_break (0); + goto error; + case MHD_HTTP_OK: + for (unsigned int i = 0; i<dhr->details.ok.details_length; i++) { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expected history of length %u, got %llu;" - " HTTP status code: %u/%d, failed: %d\n", - hs->total, - (unsigned long long) hs->results_obtained, - http_status, - (int) ec, - hs->failed); - print_expected (hs->h, - hs->total, - UINT_MAX); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; + const struct TALER_BANK_DebitDetails *dd = + &dhr->details.ok.details[i]; + + /* check current element */ + if (GNUNET_OK != + check_result (hs->h, + hs->total, + hs->results_obtained, + dd)) + { + GNUNET_break (0); + json_dumpf (dhr->response, + stderr, + JSON_COMPACT); + hs->failed = true; + hs->hh = NULL; + TALER_TESTING_interpreter_fail (is); + return; + } + hs->results_obtained++; } TALER_TESTING_interpreter_next (is); - return GNUNET_OK; - } - if (MHD_HTTP_OK != http_status) - { + return; + case MHD_HTTP_NO_CONTENT: + if (0 == hs->total) + { + /* not found is OK for empty history */ + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_break (0); + goto error; + case MHD_HTTP_NOT_FOUND: + if (0 == hs->total) + { + /* not found is OK for empty history */ + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_break (0); + goto error; + default: hs->hh = NULL; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unwanted response code from /history/outgoing: %u\n", - http_status); + "Unwanted response code from /history/incoming: %u\n", + dhr->http_status); TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; + return; } - - /* check current element */ - if (GNUNET_OK != check_result (hs->h, - hs->total, - 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; +error: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected history of length %u, got %llu;" + " HTTP status code: %u/%d, failed: %d\n", + hs->total, + (unsigned long long) hs->results_obtained, + dhr->http_status, + (int) dhr->ec, + hs->failed ? 1 : 0); + print_expected (hs->h, + hs->total, + UINT_MAX); + TALER_TESTING_interpreter_fail (is); } @@ -496,6 +573,7 @@ history_run (void *cls, const uint64_t *row_ptr; (void) cmd; + hs->is = is; /* Get row_id from trait. */ if (NULL != hs->start_row_reference) { @@ -508,23 +586,24 @@ history_run (void *cls, if (NULL == history_cmd) TALER_TESTING_FAIL (is); if (GNUNET_OK != - TALER_TESTING_get_trait_uint64 (history_cmd, - 0, - &row_ptr)) + TALER_TESTING_get_trait_row (history_cmd, + &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->total = build_history (is, &hs->h); - hs->hh = TALER_BANK_debit_history (is->ctx, - &hs->auth, - row_id, - hs->num_results, - GNUNET_TIME_UNIT_ZERO, - &history_cb, - is); + hs->total = build_history (hs, + &hs->h); + hs->hh = TALER_BANK_debit_history ( + TALER_TESTING_interpreter_get_context (is), + &hs->auth, + row_id, + hs->num_results, + GNUNET_TIME_UNIT_ZERO, + &history_cb, + hs); GNUNET_assert (NULL != hs->hh); } @@ -545,7 +624,8 @@ history_cleanup (void *cls, (void) cmd; if (NULL != hs->hh) { - TALER_LOG_WARNING ("/history/outgoing did not complete\n"); + TALER_TESTING_command_incomplete (hs->is, + cmd->label); TALER_BANK_debit_history_cancel (hs->hh); } for (unsigned int off = 0; off<hs->total; off++) @@ -558,17 +638,6 @@ history_cleanup (void *cls, } -/** - * Make a "history" CMD. - * - * @param label command label. - * @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 struct TALER_BANK_AuthenticationData *auth, @@ -588,8 +657,7 @@ TALER_TESTING_cmd_bank_debits (const char *label, .label = label, .cls = hs, .run = &history_run, - .cleanup = &history_cleanup, - .traits = &history_traits + .cleanup = &history_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_bank_transfer.c b/src/testing/testing_api_cmd_bank_transfer.c index 565d6e460..bfb29e120 100644 --- a/src/testing/testing_api_cmd_bank_transfer.c +++ b/src/testing/testing_api_cmd_bank_transfer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2020 Taler Systems SA + Copyright (C) 2018-2021 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 @@ -97,7 +97,7 @@ struct TransferState /** * Timestamp of the transaction (as returned from the bank). */ - struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Timestamp timestamp; /** * Configuration filename. Used to get the tip reserve key @@ -149,8 +149,7 @@ do_retry (void *cls) struct TransferState *fts = cls; fts->retry_task = NULL; - fts->is->commands[fts->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (fts->is); transfer_run (fts, NULL, fts->is); @@ -163,43 +162,35 @@ do_retry (void *cls) * 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 tr response details */ static void confirmation_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t serial_id, - struct GNUNET_TIME_Absolute timestamp) + const struct TALER_BANK_TransferResponse *tr) { struct TransferState *fts = cls; struct TALER_TESTING_Interpreter *is = fts->is; fts->weh = NULL; - if (MHD_HTTP_OK != http_status) + if (MHD_HTTP_OK != tr->http_status) { if (0 != fts->do_retry) { fts->do_retry--; - if ( (0 == http_status) || - (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) + if ( (0 == tr->http_status) || + (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) || + (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retrying transfer failed with %u/%d\n", - http_status, - (int) ec); + tr->http_status, + (int) tr->ec); /* on DB conflicts, do not use backoff */ - if (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) + if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) fts->backoff = GNUNET_TIME_UNIT_ZERO; else fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); - fts->is->commands[fts->is->ip].num_tries++; + TALER_TESTING_inc_tries (fts->is); fts->retry_task = GNUNET_SCHEDULER_add_delayed (fts->backoff, &do_retry, @@ -207,17 +198,14 @@ confirmation_cb (void *cls, return; } } - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Bank returned HTTP status %u/%d\n", - http_status, - (int) ec); - TALER_TESTING_interpreter_fail (is); + TALER_TESTING_unexpected_status (is, + tr->http_status, + MHD_HTTP_OK); return; } - fts->serial_id = serial_id; - fts->timestamp = timestamp; + fts->serial_id = tr->details.ok.row_id; + fts->timestamp = tr->details.ok.timestamp; TALER_TESTING_interpreter_next (is); } @@ -284,9 +272,8 @@ transfer_cleanup (void *cls, if (NULL != fts->weh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %s did not complete\n", - cmd->label); + TALER_TESTING_command_incomplete (fts->is, + cmd->label); TALER_BANK_transfer_cancel (fts->weh); fts->weh = NULL; } @@ -310,7 +297,7 @@ transfer_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue transfer_traits (void *cls, const void **ret, const char *trait, @@ -318,17 +305,16 @@ transfer_traits (void *cls, { struct TransferState *fts = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BASE_URL, - fts->exchange_base_url), + TALER_TESTING_make_trait_exchange_url ( + fts->exchange_base_url), TALER_TESTING_make_trait_bank_row (&fts->serial_id), - TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT, - fts->payto_credit_account), - TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT, - fts->payto_debit_account), - 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_make_trait_credit_payto_uri ( + fts->payto_credit_account), + TALER_TESTING_make_trait_debit_payto_uri ( + fts->payto_debit_account), + TALER_TESTING_make_trait_amount (&fts->amount), + TALER_TESTING_make_trait_timestamp (0, &fts->timestamp), + TALER_TESTING_make_trait_wtid (&fts->wtid), TALER_TESTING_trait_end () }; @@ -339,18 +325,6 @@ transfer_traits (void *cls, } -/** - * Create transfer command. - * - * @param label command label. - * @param amount amount to transfer. - * @param auth authentication data to use - * @param payto_debit_account which account sends money. - * @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, @@ -394,13 +368,6 @@ TALER_TESTING_cmd_transfer (const char *label, } -/** - * 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) { diff --git a/src/testing/testing_api_cmd_batch.c b/src/testing/testing_api_cmd_batch.c index ca23d7fd9..5bb7b974e 100644 --- a/src/testing/testing_api_cmd_batch.c +++ b/src/testing/testing_api_cmd_batch.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-2021 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 @@ -38,6 +38,11 @@ struct BatchState struct TALER_TESTING_Command *batch; /** + * My command (the batch command). + */ + const struct TALER_TESTING_Command *cmd; + + /** * Internal command pointer. */ unsigned int batch_ip; @@ -58,6 +63,7 @@ batch_run (void *cls, { struct BatchState *bs = cls; + bs->cmd = cmd; if (NULL != bs->batch[bs->batch_ip].label) TALER_LOG_INFO ("Running batched command: %s\n", bs->batch[bs->batch_ip].label); @@ -97,8 +103,9 @@ batch_cleanup (void *cls, for (unsigned int i = 0; NULL != bs->batch[i].label; i++) - bs->batch[i].cleanup (bs->batch[i].cls, - &bs->batch[i]); + if (NULL != bs->batch[i].cleanup) + bs->batch[i].cleanup (bs->batch[i].cls, + &bs->batch[i]); GNUNET_free (bs->batch); GNUNET_free (bs); } @@ -113,22 +120,15 @@ batch_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue batch_traits (void *cls, const void **ret, const char *trait, unsigned int index) { -#define CURRENT_CMD_INDEX 0 -#define BATCH_INDEX 1 - struct BatchState *bs = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_cmd - (CURRENT_CMD_INDEX, &bs->batch[bs->batch_ip]), - TALER_TESTING_make_trait_cmd - (BATCH_INDEX, bs->batch), + TALER_TESTING_make_trait_batch_cmds (bs->batch), TALER_TESTING_trait_end () }; @@ -140,18 +140,6 @@ batch_traits (void *cls, } -/** - * Create a "batch" command. Such command takes a - * end_CMD-terminated array of CMDs and executed them. - * Once it hits the end CMD, it passes the control - * to the next top-level CMD, regardless of it being - * another batch or ordinary CMD. - * - * @param label the command label. - * @param batch array of CMDs to execute. - * - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_batch (const char *label, struct TALER_TESTING_Command *batch) @@ -168,9 +156,9 @@ TALER_TESTING_cmd_batch (const char *label, bs->batch = GNUNET_new_array (i + 1, struct TALER_TESTING_Command); - memcpy (bs->batch, - batch, - sizeof (struct TALER_TESTING_Command) * i); + GNUNET_memcpy (bs->batch, + batch, + sizeof (struct TALER_TESTING_Command) * i); { struct TALER_TESTING_Command cmd = { .cls = bs, @@ -185,44 +173,43 @@ TALER_TESTING_cmd_batch (const char *label, } -/** - * Advance internal pointer to next command. - * - * @param is interpreter state. - */ -void -TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is) +bool +TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is, + void *cls) { - struct BatchState *bs = is->commands[is->ip].cls; + struct BatchState *bs = cls; + struct TALER_TESTING_Command *bcmd = &bs->batch[bs->batch_ip]; - if (NULL == bs->batch[bs->batch_ip].label) + if (NULL == bcmd->label) { - is->commands[is->ip].finish_time = GNUNET_TIME_absolute_get (); - is->ip++; - return; + /* This batch is done */ + return true; + } + if (TALER_TESTING_cmd_is_batch (bcmd)) + { + if (TALER_TESTING_cmd_batch_next (is, + bcmd->cls)) + { + /* sub-batch is done */ + bcmd->finish_time = GNUNET_TIME_absolute_get (); + bs->batch_ip++; + return false; + } } - bs->batch[bs->batch_ip].finish_time = GNUNET_TIME_absolute_get (); + /* Simple command is done */ + bcmd->finish_time = GNUNET_TIME_absolute_get (); bs->batch_ip++; + return false; } -/** - * Test if this command is a batch command. - * - * @return false if not, true if it is a batch command - */ -int +bool TALER_TESTING_cmd_is_batch (const struct TALER_TESTING_Command *cmd) { return cmd->run == &batch_run; } -/** - * Obtain what command the batch is at. - * - * @return cmd current batch command - */ struct TALER_TESTING_Command * TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd) { @@ -233,12 +220,6 @@ TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd) } -/** - * Set what command the batch should be at. - * - * @param cmd current batch command - * @param new_ip where to move the IP - */ void TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd, unsigned int new_ip) diff --git a/src/testing/testing_api_cmd_batch_deposit.c b/src/testing/testing_api_cmd_batch_deposit.c new file mode 100644 index 000000000..5139d3524 --- /dev/null +++ b/src/testing/testing_api_cmd_batch_deposit.c @@ -0,0 +1,656 @@ +/* + This file is part of TALER + Copyright (C) 2018-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_batch_deposit.c + * @brief command for testing /batch-deposit. + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * How often do we retry before giving up? + */ +#define NUM_RETRIES 5 + +/** + * How long do we wait AT MOST when retrying? + */ +#define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_MILLISECONDS, 100) + + +/** + * Information per coin in the batch. + */ +struct Coin +{ + + /** + * Amount to deposit. + */ + struct TALER_Amount amount; + + /** + * Deposit fee. + */ + struct TALER_Amount deposit_fee; + + /** + * Our coin signature. + */ + struct TALER_CoinSpendSignatureP coin_sig; + + /** + * Reference to any command that is able to provide a coin, + * possibly using $LABEL#$INDEX notation. + */ + char *coin_reference; + + /** + * Denomination public key of the coin. + */ + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + + /** + * The command being referenced. + */ + const struct TALER_TESTING_Command *coin_cmd; + + /** + * Expected entry in the coin history created by this + * coin. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che; + + /** + * Index of the coin at @e coin_cmd. + */ + unsigned int coin_idx; +}; + + +/** + * State for a "batch deposit" CMD. + */ +struct BatchDepositState +{ + + /** + * Refund deadline. Zero for no refunds. + */ + struct GNUNET_TIME_Timestamp refund_deadline; + + /** + * Wire deadline. + */ + struct GNUNET_TIME_Timestamp wire_deadline; + + /** + * Timestamp of the /deposit operation in the wallet (contract signing time). + */ + struct GNUNET_TIME_Timestamp wallet_timestamp; + + /** + * How long do we wait until we retry? + */ + struct GNUNET_TIME_Relative backoff; + + /** + * When did the exchange receive the deposit? + */ + struct GNUNET_TIME_Timestamp exchange_timestamp; + + /** + * Signing key used by the exchange to sign the + * deposit confirmation. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * Set (by the interpreter) to a fresh private key. This + * key will be used to sign the deposit request. + */ + struct TALER_MerchantPrivateKeyP merchant_priv; + + /** + * Deposit handle while operation is running. + */ + struct TALER_EXCHANGE_BatchDepositHandle *dh; + + /** + * Array of coins to batch-deposit. + */ + struct Coin *coins; + + /** + * Wire details of who is depositing -- this would be merchant + * wire details in a normal scenario. + */ + json_t *wire_details; + + /** + * JSON string describing what a proposal is about. + */ + json_t *contract_terms; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Task scheduled to try later. + */ + struct GNUNET_SCHEDULER_Task *retry_task; + + /** + * Deposit confirmation signature from the exchange. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Reference to previous deposit operation. + * Only present if we're supposed to replay the previous deposit. + */ + const char *deposit_reference; + + /** + * If @e coin_reference refers to an operation that generated + * an array of coins, this value determines which coin to pick. + */ + unsigned int num_coins; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Set to true if the /deposit succeeded + * and we now can provide the resulting traits. + */ + bool deposit_succeeded; + +}; + + +/** + * Callback to analyze the /batch-deposit response, just used to check if the + * response code is acceptable. + * + * @param cls closure. + * @param dr deposit response details + */ +static void +batch_deposit_cb (void *cls, + const struct TALER_EXCHANGE_BatchDepositResult *dr) +{ + struct BatchDepositState *ds = cls; + + ds->dh = NULL; + if (ds->expected_response_code != dr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + dr->hr.http_status, + ds->expected_response_code); + return; + } + if (MHD_HTTP_OK == dr->hr.http_status) + { + ds->deposit_succeeded = GNUNET_YES; + ds->exchange_timestamp = dr->details.ok.deposit_timestamp; + ds->exchange_pub = *dr->details.ok.exchange_pub; + ds->exchange_sig = *dr->details.ok.exchange_sig; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +batch_deposit_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct BatchDepositState *ds = cls; + const struct TALER_DenominationSignature *denom_pub_sig; + struct TALER_MerchantPublicKeyP merchant_pub; + struct TALER_PrivateContractHashP h_contract_terms; + enum TALER_ErrorCode ec; + struct TALER_WireSaltP wire_salt; + struct TALER_MerchantWireHashP h_wire; + const char *payto_uri; + struct TALER_EXCHANGE_CoinDepositDetail cdds[ds->num_coins]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &payto_uri), + GNUNET_JSON_spec_fixed_auto ("salt", + &wire_salt), + GNUNET_JSON_spec_end () + }; + const char *exchange_url + = TALER_TESTING_get_exchange_url (is); + + (void) cmd; + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + memset (cdds, + 0, + sizeof (cdds)); + ds->is = is; + GNUNET_assert (NULL != ds->wire_details); + if (GNUNET_OK != + GNUNET_JSON_parse (ds->wire_details, + spec, + NULL, NULL)) + { + json_dumpf (ds->wire_details, + stderr, + JSON_INDENT (2)); + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_JSON_contract_hash (ds->contract_terms, + &h_contract_terms)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_JSON_merchant_wire_signature_hash (ds->wire_details, + &h_wire)); + if (! GNUNET_TIME_absolute_is_zero (ds->refund_deadline.abs_time)) + { + struct GNUNET_TIME_Relative refund_deadline; + + refund_deadline + = GNUNET_TIME_absolute_get_remaining (ds->refund_deadline.abs_time); + ds->wire_deadline + = + GNUNET_TIME_relative_to_timestamp ( + GNUNET_TIME_relative_multiply (refund_deadline, + 2)); + } + else + { + ds->refund_deadline = ds->wallet_timestamp; + ds->wire_deadline = GNUNET_TIME_timestamp_get (); + } + GNUNET_CRYPTO_eddsa_key_get_public (&ds->merchant_priv.eddsa_priv, + &merchant_pub.eddsa_pub); + + for (unsigned int i = 0; i<ds->num_coins; i++) + { + struct Coin *coin = &ds->coins[i]; + struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i]; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; + + GNUNET_assert (NULL != coin->coin_reference); + cdd->amount = coin->amount; + coin->coin_cmd = TALER_TESTING_interpreter_lookup_command ( + is, + coin->coin_reference); + if (NULL == coin->coin_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + + if ( (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin->coin_cmd, + coin->coin_idx, + &coin_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (coin->coin_cmd, + coin->coin_idx, + &age_commitment_proof)) + || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (coin->coin_cmd, + coin->coin_idx, + &coin->denom_pub)) || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_sig (coin->coin_cmd, + coin->coin_idx, + &denom_pub_sig)) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL != age_commitment_proof) + { + TALER_age_commitment_hash (&age_commitment_proof->commitment, + &cdd->h_age_commitment); + } + coin->deposit_fee = coin->denom_pub->fees.deposit; + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &cdd->coin_pub.eddsa_pub); + cdd->denom_sig = *denom_pub_sig; + cdd->h_denom_pub = coin->denom_pub->h_key; + TALER_wallet_deposit_sign (&coin->amount, + &coin->denom_pub->fees.deposit, + &h_wire, + &h_contract_terms, + NULL, /* wallet_data_hash */ + &cdd->h_age_commitment, + NULL, /* hash of extensions */ + &coin->denom_pub->h_key, + ds->wallet_timestamp, + &merchant_pub, + ds->refund_deadline, + coin_priv, + &cdd->coin_sig); + coin->coin_sig = cdd->coin_sig; + coin->che.type = TALER_EXCHANGE_CTT_DEPOSIT; + coin->che.amount = coin->amount; + coin->che.details.deposit.h_wire = h_wire; + coin->che.details.deposit.h_contract_terms = h_contract_terms; + coin->che.details.deposit.no_h_policy = true; + coin->che.details.deposit.no_wallet_data_hash = true; + coin->che.details.deposit.wallet_timestamp = ds->wallet_timestamp; + coin->che.details.deposit.merchant_pub = merchant_pub; + coin->che.details.deposit.refund_deadline = ds->refund_deadline; + coin->che.details.deposit.sig = cdd->coin_sig; + coin->che.details.deposit.no_hac = GNUNET_is_zero (&cdd->h_age_commitment); + coin->che.details.deposit.hac = cdd->h_age_commitment; + coin->che.details.deposit.deposit_fee = coin->denom_pub->fees.deposit; + } + + GNUNET_assert (NULL == ds->dh); + { + struct TALER_EXCHANGE_DepositContractDetail dcd = { + .wire_deadline = ds->wire_deadline, + .merchant_payto_uri = payto_uri, + .wire_salt = wire_salt, + .h_contract_terms = h_contract_terms, + .policy_details = NULL /* FIXME #7270-OEC */, + .wallet_timestamp = ds->wallet_timestamp, + .merchant_pub = merchant_pub, + .refund_deadline = ds->refund_deadline + }; + + ds->dh = TALER_EXCHANGE_batch_deposit ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + TALER_TESTING_get_keys (is), + &dcd, + ds->num_coins, + cdds, + &batch_deposit_cb, + ds, + &ec); + } + if (NULL == ds->dh) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not create deposit with EC %d\n", + (int) ec); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "batch-deposit" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct BatchDepositState`. + * @param cmd the command which is being cleaned up. + */ +static void +batch_deposit_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct BatchDepositState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_batch_deposit_cancel (ds->dh); + ds->dh = NULL; + } + if (NULL != ds->retry_task) + { + GNUNET_SCHEDULER_cancel (ds->retry_task); + ds->retry_task = NULL; + } + for (unsigned int i = 0; i<ds->num_coins; i++) + GNUNET_free (ds->coins[i].coin_reference); + GNUNET_free (ds->coins); + json_decref (ds->wire_details); + json_decref (ds->contract_terms); + GNUNET_free (ds); +} + + +/** + * Offer internal data from a "batch-deposit" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +batch_deposit_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct BatchDepositState *ds = cls; + const struct Coin *coin = &ds->coins[index]; + /* Will point to coin cmd internals. */ + const struct TALER_CoinSpendPrivateKeyP *coin_spent_priv; + struct TALER_CoinSpendPublicKeyP coin_spent_pub; + const struct TALER_AgeCommitmentProof *age_commitment_proof; + + if (index >= ds->num_coins) + { + GNUNET_break (0); + return GNUNET_NO; + } + if (NULL == coin->coin_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return GNUNET_NO; + } + if ( (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin->coin_cmd, + coin->coin_idx, + &coin_spent_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (coin->coin_cmd, + coin->coin_idx, + &age_commitment_proof)) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return GNUNET_NO; + } + + GNUNET_CRYPTO_eddsa_key_get_public (&coin_spent_priv->eddsa_priv, + &coin_spent_pub.eddsa_pub); + + { + struct TALER_TESTING_Trait traits[] = { + /* First two traits are only available if + ds->traits is #GNUNET_YES */ + TALER_TESTING_make_trait_exchange_pub (0, + &ds->exchange_pub), + TALER_TESTING_make_trait_exchange_sig (0, + &ds->exchange_sig), + /* These traits are always available */ + TALER_TESTING_make_trait_wire_details (ds->wire_details), + TALER_TESTING_make_trait_contract_terms (ds->contract_terms), + TALER_TESTING_make_trait_merchant_priv (&ds->merchant_priv), + TALER_TESTING_make_trait_age_commitment_proof (index, + age_commitment_proof), + TALER_TESTING_make_trait_coin_history (index, + &coin->che), + TALER_TESTING_make_trait_coin_pub (index, + &coin_spent_pub), + TALER_TESTING_make_trait_denom_pub (index, + coin->denom_pub), + TALER_TESTING_make_trait_coin_priv (index, + coin_spent_priv), + TALER_TESTING_make_trait_coin_sig (index, + &coin->coin_sig), + TALER_TESTING_make_trait_deposit_amount (index, + &coin->amount), + TALER_TESTING_make_trait_deposit_fee_amount (index, + &coin->deposit_fee), + TALER_TESTING_make_trait_timestamp (index, + &ds->exchange_timestamp), + TALER_TESTING_make_trait_wire_deadline (index, + &ds->wire_deadline), + TALER_TESTING_make_trait_refund_deadline (index, + &ds->refund_deadline), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait ((ds->deposit_succeeded) + ? traits + : &traits[2], + ret, + trait, + index); + } +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_batch_deposit (const char *label, + const char *target_account_payto, + const char *contract_terms, + struct GNUNET_TIME_Relative refund_deadline, + unsigned int expected_response_code, + ...) +{ + struct BatchDepositState *ds; + va_list ap; + unsigned int num_coins = 0; + const char *ref; + + va_start (ap, + expected_response_code); + while (NULL != (ref = va_arg (ap, + const char *))) + { + GNUNET_assert (NULL != va_arg (ap, + const char *)); + num_coins++; + } + va_end (ap); + + ds = GNUNET_new (struct BatchDepositState); + ds->num_coins = num_coins; + ds->coins = GNUNET_new_array (num_coins, + struct Coin); + num_coins = 0; + va_start (ap, + expected_response_code); + while (NULL != (ref = va_arg (ap, + const char *))) + { + struct Coin *coin = &ds->coins[num_coins++]; + const char *amount = va_arg (ap, + const char *); + + GNUNET_assert (GNUNET_OK == + TALER_TESTING_parse_coin_reference (ref, + &coin->coin_reference, + &coin->coin_idx)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (amount, + &coin->amount)); + } + va_end (ap); + + ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto); + GNUNET_assert (NULL != ds->wire_details); + ds->contract_terms = json_loads (contract_terms, + JSON_REJECT_DUPLICATES, + NULL); + GNUNET_CRYPTO_eddsa_key_create (&ds->merchant_priv.eddsa_priv); + if (NULL == ds->contract_terms) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse contract terms `%s' for CMD `%s'\n", + contract_terms, + label); + GNUNET_assert (0); + } + ds->wallet_timestamp = GNUNET_TIME_timestamp_get (); + GNUNET_assert (0 == + json_object_set_new (ds->contract_terms, + "timestamp", + GNUNET_JSON_from_timestamp ( + ds->wallet_timestamp))); + if (! GNUNET_TIME_relative_is_zero (refund_deadline)) + { + ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline); + GNUNET_assert (0 == + json_object_set_new (ds->contract_terms, + "refund_deadline", + GNUNET_JSON_from_timestamp ( + ds->refund_deadline))); + } + ds->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &batch_deposit_run, + .cleanup = &batch_deposit_cleanup, + .traits = &batch_deposit_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_batch_deposit.c */ diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c new file mode 100644 index 000000000..1b056bdbb --- /dev/null +++ b/src/testing/testing_api_cmd_batch_withdraw.c @@ -0,0 +1,557 @@ +/* + This file is part of TALER + Copyright (C) 2018-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_batch_withdraw.c + * @brief implements the batch withdraw command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <microhttpd.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_extensions.h" +#include "taler_testing_lib.h" + +/** + * Information we track per withdrawn coin. + */ +struct CoinState +{ + + /** + * String describing the denomination value we should withdraw. + * A corresponding denomination key must exist in the exchange's + * offerings. Can be NULL if @e pk is set instead. + */ + struct TALER_Amount amount; + + /** + * If @e amount is NULL, this specifies the denomination key to + * use. Otherwise, this will be set (by the interpreter) to the + * denomination PK matching @e amount. + */ + struct TALER_EXCHANGE_DenomPublicKey *pk; + + /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** + * Public key of the coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Blinding key used during the operation. + */ + union GNUNET_CRYPTO_BlindingSecretP bks; + + /** + * Values contributed from the exchange during the + * withdraw protocol. + */ + struct TALER_ExchangeWithdrawValues exchange_vals; + + /** + * Set (by the interpreter) to the exchange's signature over the + * coin's public key. + */ + struct TALER_DenominationSignature sig; + + /** + * Private key material of the coin, set by the interpreter. + */ + struct TALER_PlanchetMasterSecretP ps; + + /** + * If age > 0, put here the corresponding age commitment with its proof and + * its hash, respectively. + */ + struct TALER_AgeCommitmentProof age_commitment_proof; + struct TALER_AgeCommitmentHash h_age_commitment; + + /** + * Reserve history entry that corresponds to this coin. + * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL. + */ + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; + + +}; + + +/** + * State for a "batch withdraw" CMD. + */ +struct BatchWithdrawState +{ + + /** + * Which reserve should we withdraw from? + */ + const char *reserve_reference; + + /** + * Exchange base URL. Only used as offered trait. + */ + char *exchange_url; + + /** + * URI if the reserve we are withdrawing from. + */ + char *reserve_payto_uri; + + /** + * Private key of the reserve we are withdrawing from. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Public key of the reserve we are withdrawing from. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Interpreter state (during command). + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Withdraw handle (while operation is running). + */ + struct TALER_EXCHANGE_BatchWithdrawHandle *wsh; + + /** + * Array of coin states. + */ + struct CoinState *coins; + + /** + * Set to the KYC requirement payto hash *if* the exchange replied with a + * request for KYC. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC. + */ + uint64_t requirement_row; + + /** + * Length of the @e coins array. + */ + unsigned int num_coins; + + /** + * Expected HTTP response code to the request. + */ + unsigned int expected_response_code; + + /** + * An age > 0 signifies age restriction is required. + * Same for all coins in the batch. + */ + uint8_t age; + + /** + * Force a conflict: + */ + bool force_conflict; +}; + + +/** + * "batch withdraw" operation callback; checks that the + * response code is expected and store the exchange signature + * in the state. + * + * @param cls closure. + * @param wr withdraw response details + */ +static void +reserve_batch_withdraw_cb (void *cls, + const struct + TALER_EXCHANGE_BatchWithdrawResponse *wr) +{ + struct BatchWithdrawState *ws = cls; + struct TALER_TESTING_Interpreter *is = ws->is; + + ws->wsh = NULL; + if (ws->expected_response_code != wr->hr.http_status) + { + TALER_TESTING_unexpected_status_with_body (is, + wr->hr.http_status, + ws->expected_response_code, + wr->hr.reply); + return; + } + switch (wr->hr.http_status) + { + case MHD_HTTP_OK: + for (unsigned int i = 0; i<ws->num_coins; i++) + { + struct CoinState *cs = &ws->coins[i]; + const struct TALER_EXCHANGE_PrivateCoinDetails *pcd + = &wr->details.ok.coins[i]; + + TALER_denom_sig_copy (&cs->sig, + &pcd->sig); + cs->coin_priv = pcd->coin_priv; + GNUNET_CRYPTO_eddsa_key_get_public (&cs->coin_priv.eddsa_priv, + &cs->coin_pub.eddsa_pub); + + cs->bks = pcd->bks; + TALER_denom_ewv_copy (&cs->exchange_vals, + &pcd->exchange_vals); + } + break; + case MHD_HTTP_FORBIDDEN: + /* nothing to check */ + break; + case MHD_HTTP_NOT_FOUND: + /* nothing to check */ + break; + case MHD_HTTP_CONFLICT: + /* TODO[oec]: Check if age-requirement is the reason */ + break; + case MHD_HTTP_GONE: + /* theoretically could check that the key was actually */ + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* nothing to check */ + ws->requirement_row + = wr->details.unavailable_for_legal_reasons.requirement_row; + ws->h_payto + = wr->details.unavailable_for_legal_reasons.h_payto; + break; + default: + /* Unsupported status code (by test harness) */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Batch withdraw test command does not support status code %u\n", + wr->hr.http_status); + GNUNET_break (0); + break; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + */ +static void +batch_withdraw_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct BatchWithdrawState *ws = cls; + const struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is); + const struct TALER_ReservePrivateKeyP *rp; + const struct TALER_TESTING_Command *create_reserve; + const struct TALER_EXCHANGE_DenomPublicKey *dpk; + struct TALER_EXCHANGE_WithdrawCoinInput wcis[ws->num_coins]; + struct TALER_PlanchetMasterSecretP conflict_ps = {0}; + struct TALER_AgeMask mask = {0}; + + (void) cmd; + ws->is = is; + create_reserve + = TALER_TESTING_interpreter_lookup_command ( + is, + ws->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &rp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL == ws->exchange_url) + ws->exchange_url + = GNUNET_strdup (TALER_TESTING_get_exchange_url (is)); + ws->reserve_priv = *rp; + GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv, + &ws->reserve_pub.eddsa_pub); + ws->reserve_payto_uri + = TALER_reserve_make_payto (ws->exchange_url, + &ws->reserve_pub); + + if (0 < ws->age) + mask = TALER_extensions_get_age_restriction_mask (); + + if (ws->force_conflict) + TALER_planchet_master_setup_random (&conflict_ps); + + for (unsigned int i = 0; i<ws->num_coins; i++) + { + struct CoinState *cs = &ws->coins[i]; + struct TALER_EXCHANGE_WithdrawCoinInput *wci = &wcis[i]; + + if (ws->force_conflict) + cs->ps = conflict_ps; + else + TALER_planchet_master_setup_random (&cs->ps); + + if (0 < ws->age) + { + struct GNUNET_HashCode seed = {0}; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &seed, + sizeof(seed)); + TALER_age_restriction_commit (&mask, + ws->age, + &seed, + &cs->age_commitment_proof); + TALER_age_commitment_hash (&cs->age_commitment_proof.commitment, + &cs->h_age_commitment); + } + + + dpk = TALER_TESTING_find_pk (keys, + &cs->amount, + ws->age > 0); + if (NULL == dpk) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to determine denomination key at %s\n", + (NULL != cmd) ? cmd->label : "<retried command>"); + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + /* We copy the denomination key, as re-querying /keys + * would free the old one. */ + cs->pk = TALER_EXCHANGE_copy_denomination_key (dpk); + cs->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL; + GNUNET_assert (0 <= + TALER_amount_add (&cs->reserve_history.amount, + &cs->amount, + &cs->pk->fees.withdraw)); + cs->reserve_history.details.withdraw.fee = cs->pk->fees.withdraw; + + wci->pk = cs->pk; + wci->ps = &cs->ps; + wci->ach = &cs->h_age_commitment; + } + ws->wsh = TALER_EXCHANGE_batch_withdraw ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + keys, + rp, + ws->num_coins, + wcis, + &reserve_batch_withdraw_cb, + ws); + if (NULL == ws->wsh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "withdraw" CMD, and possibly cancel + * a pending operation thereof. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +batch_withdraw_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct BatchWithdrawState *ws = cls; + + if (NULL != ws->wsh) + { + TALER_TESTING_command_incomplete (ws->is, + cmd->label); + TALER_EXCHANGE_batch_withdraw_cancel (ws->wsh); + ws->wsh = NULL; + } + for (unsigned int i = 0; i<ws->num_coins; i++) + { + struct CoinState *cs = &ws->coins[i]; + + TALER_denom_ewv_free (&cs->exchange_vals); + TALER_denom_sig_free (&cs->sig); + if (NULL != cs->pk) + { + TALER_EXCHANGE_destroy_denomination_key (cs->pk); + cs->pk = NULL; + } + if (0 < ws->age) + TALER_age_commitment_proof_free (&cs->age_commitment_proof); + } + GNUNET_free (ws->coins); + GNUNET_free (ws->exchange_url); + GNUNET_free (ws->reserve_payto_uri); + GNUNET_free (ws); +} + + +/** + * Offer internal data to a "withdraw" CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +batch_withdraw_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct BatchWithdrawState *ws = cls; + struct CoinState *cs = &ws->coins[index]; + struct TALER_TESTING_Trait traits[] = { + /* history entry MUST be first due to response code logic below! */ + TALER_TESTING_make_trait_reserve_history (index, + &cs->reserve_history), + TALER_TESTING_make_trait_coin_priv (index, + &cs->coin_priv), + TALER_TESTING_make_trait_coin_pub (index, + &cs->coin_pub), + TALER_TESTING_make_trait_planchet_secrets (index, + &cs->ps), + TALER_TESTING_make_trait_blinding_key (index, + &cs->bks), + TALER_TESTING_make_trait_exchange_wd_value (index, + &cs->exchange_vals), + TALER_TESTING_make_trait_denom_pub (index, + cs->pk), + TALER_TESTING_make_trait_denom_sig (index, + &cs->sig), + TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub), + TALER_TESTING_make_trait_amounts (index, + &cs->amount), + TALER_TESTING_make_trait_legi_requirement_row (&ws->requirement_row), + TALER_TESTING_make_trait_h_payto (&ws->h_payto), + TALER_TESTING_make_trait_payto_uri (ws->reserve_payto_uri), + TALER_TESTING_make_trait_exchange_url (ws->exchange_url), + TALER_TESTING_make_trait_age_commitment_proof (index, + ws->age > 0 ? + &cs->age_commitment_proof: + NULL), + TALER_TESTING_make_trait_h_age_commitment (index, + ws->age > 0 ? + &cs->h_age_commitment : + NULL), + TALER_TESTING_trait_end () + }; + + if (index >= ws->num_coins) + return GNUNET_NO; + return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK) + ? &traits[0] /* we have reserve history */ + : &traits[1], /* skip reserve history */ + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_batch_withdraw_with_conflict ( + const char *label, + const char *reserve_reference, + bool conflict, + uint8_t age, + unsigned int expected_response_code, + const char *amount, + ...) +{ + struct BatchWithdrawState *ws; + unsigned int cnt; + va_list ap; + + ws = GNUNET_new (struct BatchWithdrawState); + ws->age = age; + ws->reserve_reference = reserve_reference; + ws->expected_response_code = expected_response_code; + ws->force_conflict = conflict; + + cnt = 1; + va_start (ap, + amount); + while (NULL != (va_arg (ap, + const char *))) + cnt++; + ws->num_coins = cnt; + ws->coins = GNUNET_new_array (cnt, + struct CoinState); + va_end (ap); + va_start (ap, + amount); + for (unsigned int i = 0; i<ws->num_coins; i++) + { + struct CoinState *cs = &ws->coins[i]; + + if (GNUNET_OK != + TALER_string_to_amount (amount, + &cs->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + amount, + label); + GNUNET_assert (0); + } + /* move on to next vararg! */ + amount = va_arg (ap, + const char *); + } + GNUNET_assert (NULL == amount); + va_end (ap); + + { + struct TALER_TESTING_Command cmd = { + .cls = ws, + .label = label, + .run = &batch_withdraw_run, + .cleanup = &batch_withdraw_cleanup, + .traits = &batch_withdraw_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_batch_withdraw.c */ diff --git a/src/testing/testing_api_cmd_change_auth.c b/src/testing/testing_api_cmd_change_auth.c deleted file mode 100644 index fdf5d3591..000000000 --- a/src/testing/testing_api_cmd_change_auth.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - This file is part of TALER - (C) 2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 3, or - (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_cmd_change_auth.c - * @brief command(s) to change CURL context authorization header - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_testing_lib.h" - - -/** - * State for a "authchange" CMD. - */ -struct AuthchangeState -{ - - /** - * What is the new authorization token to send? - */ - const char *auth_token; -}; - - -/** - * No traits to offer, just provide a stub to be called when - * some CMDs iterates through the list of all the commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the trait to return. - * @return #GNUNET_OK on success. - */ -static int -authchange_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - (void) cls; - (void) ret; - (void) trait; - (void) index; - return GNUNET_NO; -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -authchange_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct AuthchangeState *ss = cls; - - if (NULL != is->ctx) - { - GNUNET_CURL_fini (is->ctx); - is->ctx = NULL; - } - if (NULL != is->rc) - { - GNUNET_CURL_gnunet_rc_destroy (is->rc); - is->rc = NULL; - } - is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, - &is->rc); - GNUNET_CURL_enable_async_scope_header (is->ctx, - "Taler-Correlation-Id"); - GNUNET_assert (NULL != is->ctx); - is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); - if (NULL != ss->auth_token) - { - char *authorization; - - GNUNET_asprintf (&authorization, - "%s: %s", - MHD_HTTP_HEADER_AUTHORIZATION, - ss->auth_token); - GNUNET_assert (GNUNET_OK == - GNUNET_CURL_append_header (is->ctx, - authorization)); - GNUNET_free (authorization); - } - TALER_TESTING_interpreter_next (is); -} - - -/** - * Cleanup the state from a "authchange" CMD. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -authchange_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct AuthchangeState *ss = cls; - - (void) cmd; - GNUNET_free (ss); -} - - -struct TALER_TESTING_Command -TALER_TESTING_cmd_set_authorization (const char *label, - const char *auth_token) -{ - struct AuthchangeState *ss; - - ss = GNUNET_new (struct AuthchangeState); - ss->auth_token = auth_token; - - { - struct TALER_TESTING_Command cmd = { - .cls = ss, - .label = label, - .run = &authchange_run, - .cleanup = &authchange_cleanup, - .traits = &authchange_traits - }; - - return cmd; - } -} - - -/* end of testing_api_cmd_change_auth.c */ diff --git a/src/testing/testing_api_cmd_check_aml_decision.c b/src/testing/testing_api_cmd_check_aml_decision.c new file mode 100644 index 000000000..fa0981e0d --- /dev/null +++ b/src/testing/testing_api_cmd_check_aml_decision.c @@ -0,0 +1,270 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_check_aml_decision.c + * @brief command for testing /management/XXX + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "check_aml_decision" CMD. + */ +struct AmlCheckState +{ + + /** + * Handle while operation is running. + */ + struct TALER_EXCHANGE_LookupAmlDecision *dh; + + /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to command to previous set officer. + */ + const char *ref_officer; + + /** + * Reference to command to the previous set AML status operation. + */ + const char *ref_operation; + + /** + * Expected HTTP status. + */ + unsigned int expected_http_status; + +}; + + +/** + * Callback to analyze the /aml/$OFFICER_PUB/$decision/$H_PAYTO response, just used to check + * if the response code is acceptable. + * + * @param cls closure. + * @param adr response details + */ +static void +check_aml_decision_cb (void *cls, + const struct TALER_EXCHANGE_AmlDecisionResponse *adr) +{ + struct AmlCheckState *ds = cls; + + ds->dh = NULL; + if (ds->expected_http_status != adr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + adr->hr.http_status, + ds->expected_http_status); + return; + } + if (MHD_HTTP_OK == adr->hr.http_status) + { + const struct TALER_TESTING_Command *ref; + const char *justification; + enum TALER_AmlDecisionState *new_state; + const struct TALER_Amount *amount; + const struct TALER_EXCHANGE_AmlDecisionDetail *oldest = NULL; + + ref = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->ref_operation); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_aml_justification (ref, + &justification)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_aml_decision (ref, + &new_state)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_amount (ref, + &amount)); + for (unsigned int i = 0; i<adr->details.ok.aml_history_length; i++) + { + const struct TALER_EXCHANGE_AmlDecisionDetail *aml_history + = &adr->details.ok.aml_history[i]; + + if ( (NULL == oldest) || + (0 != + TALER_amount_cmp (amount, + &oldest->new_threshold)) || + (GNUNET_TIME_timestamp_cmp (oldest->decision_time, + >, + aml_history->decision_time)) ) + oldest = aml_history; + } + if (NULL == oldest) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + if ( (oldest->new_state != *new_state) || + (0 != strcmp (oldest->justification, + justification) ) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +check_aml_decision_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AmlCheckState *ds = cls; + const struct TALER_PaytoHashP *h_payto; + const struct TALER_AmlOfficerPrivateKeyP *officer_priv; + const struct TALER_TESTING_Command *ref; + const char *exchange_url; + + (void) cmd; + ds->is = is; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } + + ref = TALER_TESTING_interpreter_lookup_command (is, + ds->ref_operation); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_h_payto (ref, + &h_payto)); + ref = TALER_TESTING_interpreter_lookup_command (is, + ds->ref_officer); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_officer_priv (ref, + &officer_priv)); + ds->dh = TALER_EXCHANGE_lookup_aml_decision ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + h_payto, + officer_priv, + true, /* history */ + &check_aml_decision_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "check_aml_decision" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct AmlCheckState`. + * @param cmd the command which is being cleaned up. + */ +static void +check_aml_decision_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AmlCheckState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_lookup_aml_decision_cancel (ds->dh); + ds->dh = NULL; + } + GNUNET_free (ds); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_aml_decision ( + const char *label, + const char *ref_officer, + const char *ref_operation, + unsigned int expected_http_status) +{ + struct AmlCheckState *ds; + + ds = GNUNET_new (struct AmlCheckState); + ds->ref_officer = ref_officer; + ds->ref_operation = ref_operation; + ds->expected_http_status = expected_http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &check_aml_decision_run, + .cleanup = &check_aml_decision_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_check_aml_decision.c */ diff --git a/src/testing/testing_api_cmd_check_aml_decisions.c b/src/testing/testing_api_cmd_check_aml_decisions.c new file mode 100644 index 000000000..c8c2ec3f5 --- /dev/null +++ b/src/testing/testing_api_cmd_check_aml_decisions.c @@ -0,0 +1,204 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_check_aml_decisions.c + * @brief command for testing /aml/$OFFICER/decisions/$FILTER + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "check_aml_decision" CMD. + */ +struct AmlCheckState +{ + + /** + * Handle while operation is running. + */ + struct TALER_EXCHANGE_LookupAmlDecisions *dh; + + /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to command to previous set officer. + */ + const char *ref_officer; + + /** + * Which states to filter by. + */ + enum TALER_AmlDecisionState filter; + + /** + * Expected HTTP status. + */ + unsigned int expected_http_status; + +}; + + +/** + * Callback to analyze the /aml/$OFFICER_PUB/$decisions/$FILTER response, just used to check + * if the response code is acceptable. + * + * @param cls closure. + * @param adr response details + */ +static void +check_aml_decisions_cb (void *cls, + const struct TALER_EXCHANGE_AmlDecisionsResponse *adr) +{ + struct AmlCheckState *ds = cls; + + ds->dh = NULL; + if (ds->expected_http_status != adr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + adr->hr.http_status, + ds->expected_http_status); + return; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +check_aml_decisions_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AmlCheckState *ds = cls; + const struct TALER_AmlOfficerPrivateKeyP *officer_priv; + const struct TALER_TESTING_Command *ref; + const char *exchange_url; + + (void) cmd; + ds->is = is; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } + ref = TALER_TESTING_interpreter_lookup_command (is, + ds->ref_officer); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_officer_priv (ref, + &officer_priv)); + ds->dh = TALER_EXCHANGE_lookup_aml_decisions ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + INT64_MAX, + -1, /* return last one for testing */ + ds->filter, + officer_priv, + &check_aml_decisions_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "check_aml_decisions" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct AmlCheckState`. + * @param cmd the command which is being cleaned up. + */ +static void +check_aml_decisions_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AmlCheckState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_lookup_aml_decisions_cancel (ds->dh); + ds->dh = NULL; + } + GNUNET_free (ds); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_aml_decisions ( + const char *label, + const char *ref_officer, + enum TALER_AmlDecisionState filter, + unsigned int expected_http_status) +{ + struct AmlCheckState *ds; + + ds = GNUNET_new (struct AmlCheckState); + ds->ref_officer = ref_officer; + ds->filter = filter; + ds->expected_http_status = expected_http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &check_aml_decisions_run, + .cleanup = &check_aml_decisions_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_check_aml_decisions.c */ diff --git a/src/testing/testing_api_cmd_check_keys.c b/src/testing/testing_api_cmd_check_keys.c deleted file mode 100644 index 5645bb9be..000000000 --- a/src/testing/testing_api_cmd_check_keys.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - This file is part of TALER - (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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_cmd_check_keys.c - * @brief Implementation of "check keys" test command. - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_testing_lib.h" - - -/** - * State for a "check keys" CMD. - */ -struct CheckKeysState -{ - /** - * This number will instruct the CMD interpreter to - * make sure that /keys was downloaded `generation` times - * _before_ running the very CMD logic. - */ - unsigned int generation; - - /** - * If this value is true, then the "cherry - * picking" facility is turned off; whole /keys is - * downloaded. - */ - bool pull_all_keys; - - /** - * Label of a command to use to derive the "last_denom_issue" date to use. - */ - const char *last_denom_date_ref; - - /** - * Last denomination date we received when doing this request. - */ - struct GNUNET_TIME_Absolute my_denom_date; -}; - - -/** - * Run the "check keys" command. - * - * @param cls closure. - * @param cmd the command currently being executed. - * @param is the interpreter state. - */ -static void -check_keys_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct CheckKeysState *cks = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "cmd `%s' (ip: %u), key generation: %d\n", - cmd->label, - is->ip, - is->key_generation); - if (is->key_generation < cks->generation) - { - struct GNUNET_TIME_Absolute rdate; - - is->working = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Triggering GET /keys, cmd `%s'\n", - cmd->label); - if (NULL != cks->last_denom_date_ref) - { - if (0 == strcmp ("zero", - cks->last_denom_date_ref)) - { - TALER_LOG_DEBUG ("Forcing last_denom_date URL argument set to zero\n"); - TALER_EXCHANGE_set_last_denom (is->exchange, - GNUNET_TIME_UNIT_ZERO_ABS); - } - else - { - const struct GNUNET_TIME_Absolute *last_denom_date; - const struct TALER_TESTING_Command *ref; - - ref = TALER_TESTING_interpreter_lookup_command (is, - cks->last_denom_date_ref); - if (NULL == ref) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - if (GNUNET_OK != - TALER_TESTING_get_trait_absolute_time (ref, - 0, - &last_denom_date)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - TALER_LOG_DEBUG ("Forcing last_denom_date URL argument\n"); - TALER_EXCHANGE_set_last_denom (is->exchange, - *last_denom_date); - } - } - - rdate = TALER_EXCHANGE_check_keys_current ( - is->exchange, - cks->pull_all_keys - ? TALER_EXCHANGE_CKF_FORCE_ALL_NOW - : TALER_EXCHANGE_CKF_FORCE_DOWNLOAD); - /* Redownload /keys. */ - GNUNET_break (0 == - rdate.abs_value_us); - return; - } - { - const struct TALER_EXCHANGE_Keys *keys; - - keys = TALER_EXCHANGE_get_keys (is->exchange); - if (NULL == keys) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - cks->my_denom_date = keys->last_denom_issue_date; - } - TALER_TESTING_interpreter_next (is); -} - - -/** - * Cleanup the state. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -check_keys_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct CheckKeysState *cks = cls; - - (void) cmd; - GNUNET_free (cks); -} - - -/** - * Offer internal data to a "check_keys" CMD state to other - * commands. - * - * @param cls closure - * @param[out] ret result (could be anything) - * @param trait name of the trait - * @param index index number of the object to offer. - * @return #GNUNET_OK on success - */ -static int -check_keys_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct CheckKeysState *cks = cls; - struct TALER_TESTING_Trait traits[] = { - /* history entry MUST be first due to response code logic below! */ - TALER_TESTING_make_trait_absolute_time (0, - &cks->my_denom_date), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys (const char *label, - unsigned int generation) -{ - struct CheckKeysState *cks; - - cks = GNUNET_new (struct CheckKeysState); - cks->generation = generation; - { - struct TALER_TESTING_Command cmd = { - .cls = cks, - .label = label, - .run = &check_keys_run, - .cleanup = &check_keys_cleanup, - .traits = &check_keys_traits - }; - - return cmd; - } -} - - -struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label, - unsigned int generation) -{ - struct TALER_TESTING_Command cmd - = TALER_TESTING_cmd_check_keys (label, - generation); - struct CheckKeysState *cks = cmd.cls; - - cks->pull_all_keys = true; - return cmd; -} - - -struct TALER_TESTING_Command -TALER_TESTING_cmd_check_keys_with_last_denom ( - const char *label, - unsigned int generation, - const char *last_denom_date_ref) -{ - struct TALER_TESTING_Command cmd - = TALER_TESTING_cmd_check_keys (label, - generation); - struct CheckKeysState *cks = cmd.cls; - cks->last_denom_date_ref = last_denom_date_ref; - return cmd; -} - - -/* end of testing_api_cmd_check_keys.c */ diff --git a/src/testing/testing_api_cmd_coin_history.c b/src/testing/testing_api_cmd_coin_history.c new file mode 100644 index 000000000..a1345f67f --- /dev/null +++ b/src/testing/testing_api_cmd_coin_history.c @@ -0,0 +1,609 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_coin_history.c + * @brief Implement the /coins/$COIN_PUB/history test command. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "history" CMD. + */ +struct HistoryState +{ + + /** + * Public key of the coin being analyzed. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Label to the command which created the coin to check, + * needed to resort the coin key. + */ + const char *coin_reference; + + /** + * Handle to the "coin history" operation. + */ + struct TALER_EXCHANGE_CoinsHistoryHandle *rsh; + + /** + * Expected coin balance. + */ + const char *expected_balance; + + /** + * Private key of the coin being analyzed. + */ + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + +}; + + +/** + * Closure for analysis_cb(). + */ +struct AnalysisContext +{ + /** + * Coin public key we are looking at. + */ + const struct TALER_CoinSpendPublicKeyP *coin_pub; + + /** + * Length of the @e history array. + */ + unsigned int history_length; + + /** + * Array of history items to match. + */ + const struct TALER_EXCHANGE_CoinHistoryEntry *history; + + /** + * Array of @e history_length of matched entries. + */ + bool *found; + + /** + * Set to true if an entry could not be found. + */ + bool failure; +}; + + +/** + * Compare @a h1 and @a h2. + * + * @param h1 a history entry + * @param h2 a history entry + * @return 0 if @a h1 and @a h2 are equal + */ +static int +history_entry_cmp ( + const struct TALER_EXCHANGE_CoinHistoryEntry *h1, + const struct TALER_EXCHANGE_CoinHistoryEntry *h2) +{ + if (h1->type != h2->type) + return 1; + if (0 != TALER_amount_cmp (&h1->amount, + &h2->amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Amount mismatch (%s)\n", + TALER_amount2s (&h1->amount)); + return 1; + } + switch (h1->type) + { + case TALER_EXCHANGE_CTT_NONE: + GNUNET_break (0); + break; + case TALER_EXCHANGE_CTT_DEPOSIT: + if (0 != GNUNET_memcmp (&h1->details.deposit.h_contract_terms, + &h2->details.deposit.h_contract_terms)) + return 1; + if (0 != GNUNET_memcmp (&h1->details.deposit.merchant_pub, + &h2->details.deposit.merchant_pub)) + return 1; + if (0 != GNUNET_memcmp (&h1->details.deposit.h_wire, + &h2->details.deposit.h_wire)) + return 1; + if (0 != GNUNET_memcmp (&h1->details.deposit.sig, + &h2->details.deposit.sig)) + return 1; + return 0; + case TALER_EXCHANGE_CTT_MELT: + if (0 != GNUNET_memcmp (&h1->details.melt.h_age_commitment, + &h2->details.melt.h_age_commitment)) + return 1; + /* Note: most other fields are not initialized + in the trait as they are hard to extract from + the API */ + return 0; + case TALER_EXCHANGE_CTT_REFUND: + if (0 != GNUNET_memcmp (&h1->details.refund.sig, + &h2->details.refund.sig)) + return 1; + return 0; + case TALER_EXCHANGE_CTT_RECOUP: + if (0 != GNUNET_memcmp (&h1->details.recoup.coin_sig, + &h2->details.recoup.coin_sig)) + return 1; + /* Note: exchange_sig, exchange_pub and timestamp are + fundamentally not available in the initiating command */ + return 0; + case TALER_EXCHANGE_CTT_RECOUP_REFRESH: + if (0 != GNUNET_memcmp (&h1->details.recoup_refresh.coin_sig, + &h2->details.recoup_refresh.coin_sig)) + return 1; + /* Note: exchange_sig, exchange_pub and timestamp are + fundamentally not available in the initiating command */ + return 0; + case TALER_EXCHANGE_CTT_OLD_COIN_RECOUP: + if (0 != GNUNET_memcmp (&h1->details.old_coin_recoup.new_coin_pub, + &h2->details.old_coin_recoup.new_coin_pub)) + return 1; + /* Note: exchange_sig, exchange_pub and timestamp are + fundamentally not available in the initiating command */ + return 0; + case TALER_EXCHANGE_CTT_PURSE_DEPOSIT: + /* coin_sig is not initialized */ + if (0 != GNUNET_memcmp (&h1->details.purse_deposit.purse_pub, + &h2->details.purse_deposit.purse_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Purse public key mismatch\n"); + return 1; + } + if (0 != strcmp (h1->details.purse_deposit.exchange_base_url, + h2->details.purse_deposit.exchange_base_url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Exchange base URL mismatch (%s/%s)\n", + h1->details.purse_deposit.exchange_base_url, + h2->details.purse_deposit.exchange_base_url); + GNUNET_break (0); + return 1; + } + return 0; + case TALER_EXCHANGE_CTT_PURSE_REFUND: + /* NOTE: not supported yet (trait not returned) */ + return 0; + case TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT: + /* NOTE: not supported yet (trait not returned) */ + if (0 != GNUNET_memcmp (&h1->details.reserve_open_deposit.coin_sig, + &h2->details.reserve_open_deposit.coin_sig)) + return 1; + return 0; + } + GNUNET_assert (0); + return -1; +} + + +/** + * Check if @a cmd changed the coin, if so, find the + * entry in our history and set the respective index in found + * to true. If the entry is not found, set failure. + * + * @param cls our `struct AnalysisContext *` + * @param cmd command to analyze for impact on history + */ +static void +analyze_command (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AnalysisContext *ac = cls; + const struct TALER_CoinSpendPublicKeyP *coin_pub = ac->coin_pub; + const struct TALER_EXCHANGE_CoinHistoryEntry *history = ac->history; + unsigned int history_length = ac->history_length; + bool *found = ac->found; + + if (TALER_TESTING_cmd_is_batch (cmd)) + { + struct TALER_TESTING_Command *cur; + struct TALER_TESTING_Command *bcmd; + + cur = TALER_TESTING_cmd_batch_get_current (cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_batch_cmds (cmd, + &bcmd)) + { + GNUNET_break (0); + ac->failure = true; + return; + } + for (unsigned int i = 0; NULL != bcmd[i].label; i++) + { + struct TALER_TESTING_Command *step = &bcmd[i]; + + analyze_command (ac, + step); + if (ac->failure) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Entry for batch step `%s' missing in history\n", + step->label); + return; + } + if (step == cur) + break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */ + } + return; + } + + for (unsigned int j = 0; true; j++) + { + const struct TALER_CoinSpendPublicKeyP *rp; + const struct TALER_EXCHANGE_CoinHistoryEntry *he; + bool matched = false; + + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_pub (cmd, + j, + &rp)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Command `%s#%u' has no public key for a coin\n", + cmd->label, + j); + break; /* command does nothing for coins */ + } + if (0 != + GNUNET_memcmp (rp, + coin_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Command `%s#%u' is about another coin\n", + cmd->label, + j); + continue; /* command affects some _other_ coin */ + } + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_history (cmd, + j, + &he)) + { + /* NOTE: only for debugging... */ + if (0 == j) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Command `%s' has the coin_pub, but lacks coin history trait\n", + cmd->label); + return; /* command does nothing for coins */ + } + for (unsigned int i = 0; i<history_length; i++) + { + if (found[i]) + continue; /* already found, skip */ + if (0 == + history_entry_cmp (he, + &history[i])) + { + found[i] = true; + matched = true; + break; + } + } + if (! matched) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command `%s' coin history entry #%u not found\n", + cmd->label, + j); + ac->failure = true; + return; + } + } +} + + +/** + * Check that the coin balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +coin_history_cb (void *cls, + const struct TALER_EXCHANGE_CoinHistory *rs) +{ + struct HistoryState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + struct TALER_Amount eb; + unsigned int hlen; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + TALER_TESTING_unexpected_status (ss->is, + rs->hr.http_status, + ss->expected_response_code); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ss->expected_balance, + &eb)); + + if (0 != TALER_amount_cmp (&eb, + &rs->details.ok.balance)) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected balance for coin: %s\n", + TALER_amount_to_string (&rs->details.ok.balance)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected balance of: %s\n", + TALER_amount_to_string (&eb)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + hlen = json_array_size (rs->details.ok.history); + { + bool found[GNUNET_NZL (hlen)]; + struct TALER_EXCHANGE_CoinHistoryEntry rhist[GNUNET_NZL (hlen)]; + struct AnalysisContext ac = { + .coin_pub = &ss->coin_pub, + .history = rhist, + .history_length = hlen, + .found = found + }; + const struct TALER_EXCHANGE_DenomPublicKey *dk; + struct TALER_Amount total_in; + struct TALER_Amount total_out; + struct TALER_Amount hbal; + + dk = TALER_EXCHANGE_get_denomination_key_by_hash ( + TALER_TESTING_get_keys (is), + &rs->details.ok.h_denom_pub); + memset (found, + 0, + sizeof (found)); + memset (rhist, + 0, + sizeof (rhist)); + if (GNUNET_OK != + TALER_EXCHANGE_parse_coin_history ( + TALER_TESTING_get_keys (is), + dk, + rs->details.ok.history, + &ss->coin_pub, + &total_in, + &total_out, + hlen, + rhist)) + { + GNUNET_break (0); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (0 > + TALER_amount_subtract (&hbal, + &total_in, + &total_out)) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Coin credits: %s\n", + TALER_amount2s (&total_in)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Coin debits: %s\n", + TALER_amount2s (&total_out)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (0 != TALER_amount_cmp (&hbal, + &rs->details.ok.balance)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + (void) ac; + TALER_TESTING_iterate (is, + true, + &analyze_command, + &ac); + if (ac.failure) + { + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } +#if 1 + for (unsigned int i = 0; i<hlen; i++) + { + if (found[i]) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "History entry at index %u of type %d not justified by command history\n", + i, + rs->details.ok.history[i].type); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } +#endif + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +history_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct HistoryState *ss = cls; + const struct TALER_TESTING_Command *create_coin; + char *cref; + unsigned int idx; + + ss->is = is; + GNUNET_assert ( + GNUNET_OK == + TALER_TESTING_parse_coin_reference ( + ss->coin_reference, + &cref, + &idx)); + create_coin + = TALER_TESTING_interpreter_lookup_command (is, + cref); + if (NULL == create_coin) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (create_coin, + idx, + &ss->coin_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find coin_priv for history query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->coin_priv->eddsa_priv, + &ss->coin_pub.eddsa_pub); + ss->rsh = TALER_EXCHANGE_coins_history ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + ss->coin_priv, + 0, + &coin_history_cb, + ss); +} + + +/** + * Offer internal data from a "history" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +history_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct HistoryState *hs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_coin_pub (0, + &hs->coin_pub), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Cleanup the state from a "coin 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 *ss = cls; + + if (NULL != ss->rsh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_coins_history_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_coin_history (const char *label, + const char *coin_reference, + const char *expected_balance, + unsigned int expected_response_code) +{ + struct HistoryState *ss; + + GNUNET_assert (NULL != coin_reference); + ss = GNUNET_new (struct HistoryState); + ss->coin_reference = coin_reference; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &history_run, + .cleanup = &history_cleanup, + .traits = &history_traits + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_common.c b/src/testing/testing_api_cmd_common.c new file mode 100644 index 000000000..2d828a2b0 --- /dev/null +++ b/src/testing/testing_api_cmd_common.c @@ -0,0 +1,64 @@ +/* + This file is part of TALER + Copyright (C) 2018-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_common.c + * @brief common functions for commands + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_testing_lib.h" + + +enum GNUNET_GenericReturnValue +TALER_TESTING_parse_coin_reference ( + const char *coin_reference, + char **cref, + unsigned int *idx) +{ + const char *index; + char dummy; + + /* We allow command references of the form "$LABEL#$INDEX" or + just "$LABEL", which implies the index is 0. Figure out + which one it is. */ + index = strchr (coin_reference, '#'); + if (NULL == index) + { + *idx = 0; + *cref = GNUNET_strdup (coin_reference); + return GNUNET_OK; + } + *cref = GNUNET_strndup (coin_reference, + index - coin_reference); + if (1 != sscanf (index + 1, + "%u%c", + idx, + &dummy)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Numeric index (not `%s') required after `#' in command reference of command in %s:%u\n", + index, + __FILE__, + __LINE__); + GNUNET_free (*cref); + *cref = NULL; + return GNUNET_SYSERR; + } + return GNUNET_OK; +} diff --git a/src/testing/testing_api_cmd_contract_get.c b/src/testing/testing_api_cmd_contract_get.c new file mode 100644 index 000000000..fa83d83f7 --- /dev/null +++ b/src/testing/testing_api_cmd_contract_get.c @@ -0,0 +1,316 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_contract_get.c + * @brief command for testing GET /contracts/$CPUB + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "contract get" CMD. + */ +struct ContractGetState +{ + + /** + * JSON string describing the resulting contract. + */ + json_t *contract_terms; + + /** + * Private key to decrypt the contract. + */ + struct TALER_ContractDiffiePrivateP contract_priv; + + /** + * Set to the returned merge key. + */ + struct TALER_PurseMergePrivateKeyP merge_priv; + + /** + * Public key of the purse. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * Reference to the command that uploaded the contract. + */ + const char *contract_ref; + + /** + * ContractGet handle while operation is running. + */ + struct TALER_EXCHANGE_ContractsGetHandle *dh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * True if this is for a 'merge' operation, + * 'false' if this is for a 'deposit' operation. + */ + bool merge; + +}; + + +/** + * Callback to analyze the /contracts/$CPUB response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param dr get response details + */ +static void +get_cb (void *cls, + const struct TALER_EXCHANGE_ContractGetResponse *dr) +{ + struct ContractGetState *ds = cls; + const struct TALER_TESTING_Command *ref; + + ds->dh = NULL; + if (ds->expected_response_code != dr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + dr->hr.http_status, + ds->expected_response_code); + return; + } + ref = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->contract_ref); + GNUNET_assert (NULL != ref); + if (MHD_HTTP_OK == dr->hr.http_status) + { + const struct TALER_PurseMergePrivateKeyP *mp; + const json_t *ct; + + ds->purse_pub = dr->details.ok.purse_pub; + if (ds->merge) + { + if (GNUNET_OK != + TALER_TESTING_get_trait_merge_priv (ref, + &mp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->contract_terms = + TALER_CRYPTO_contract_decrypt_for_merge ( + &ds->contract_priv, + &ds->purse_pub, + dr->details.ok.econtract, + dr->details.ok.econtract_size, + &ds->merge_priv); + if (0 != + GNUNET_memcmp (mp, + &ds->merge_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + } + else + { + ds->contract_terms = + TALER_CRYPTO_contract_decrypt_for_deposit ( + &ds->contract_priv, + dr->details.ok.econtract, + dr->details.ok.econtract_size); + } + if (NULL == ds->contract_terms) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_contract_terms (ref, + &ct)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + if (1 != /* 1: equal, 0: not equal */ + json_equal (ct, + ds->contract_terms)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +get_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct ContractGetState *ds = cls; + const struct TALER_ContractDiffiePrivateP *contract_priv; + const struct TALER_TESTING_Command *ref; + const char *exchange_url; + + (void) cmd; + ds->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + ref = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->contract_ref); + GNUNET_assert (NULL != ref); + if (GNUNET_OK != + TALER_TESTING_get_trait_contract_priv (ref, + &contract_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->contract_priv = *contract_priv; + ds->dh = TALER_EXCHANGE_contract_get ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + contract_priv, + &get_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not GET contract\n"); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "get" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct ContractGetState`. + * @param cmd the command which is being cleaned up. + */ +static void +get_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct ContractGetState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_contract_get_cancel (ds->dh); + ds->dh = NULL; + } + json_decref (ds->contract_terms); + GNUNET_free (ds); +} + + +/** + * Offer internal data from a "get" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +get_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct ContractGetState *ds = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_merge_priv (&ds->merge_priv), + TALER_TESTING_make_trait_purse_pub (&ds->purse_pub), + TALER_TESTING_make_trait_contract_terms (ds->contract_terms), + TALER_TESTING_trait_end () + }; + + /* skip 'merge_priv' if we are in 'merge' mode */ + return TALER_TESTING_get_trait (&traits[ds->merge ? 0 : 1], + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_contract_get ( + const char *label, + unsigned int expected_http_status, + bool for_merge, + const char *contract_ref) +{ + struct ContractGetState *ds; + + ds = GNUNET_new (struct ContractGetState); + ds->expected_response_code = expected_http_status; + ds->contract_ref = contract_ref; + ds->merge = for_merge; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &get_run, + .cleanup = &get_cleanup, + .traits = &get_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_contract_get.c */ diff --git a/src/testing/testing_api_cmd_deposit.c b/src/testing/testing_api_cmd_deposit.c index 58322b1d1..849c78c70 100644 --- a/src/testing/testing_api_cmd_deposit.c +++ b/src/testing/testing_api_cmd_deposit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2021 Taler Systems SA + Copyright (C) 2018-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -69,6 +69,11 @@ struct DepositState unsigned int coin_index; /** + * Our coin signature. + */ + struct TALER_CoinSpendSignatureP coin_sig; + + /** * Wire details of who is depositing -- this would be merchant * wire details in a normal scenario. */ @@ -82,7 +87,12 @@ struct DepositState /** * Refund deadline. Zero for no refunds. */ - struct GNUNET_TIME_Absolute refund_deadline; + struct GNUNET_TIME_Timestamp refund_deadline; + + /** + * Wire deadline. + */ + struct GNUNET_TIME_Timestamp wire_deadline; /** * Set (by the interpreter) to a fresh private key. This @@ -93,12 +103,17 @@ struct DepositState /** * Deposit handle while operation is running. */ - struct TALER_EXCHANGE_DepositHandle *dh; + struct TALER_EXCHANGE_BatchDepositHandle *dh; + + /** + * Denomination public key of the deposited coin. + */ + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; /** * Timestamp of the /deposit operation in the wallet (contract signing time). */ - struct GNUNET_TIME_Absolute wallet_timestamp; + struct GNUNET_TIME_Timestamp wallet_timestamp; /** * Interpreter state. @@ -126,15 +141,21 @@ struct DepositState unsigned int do_retry; /** - * Set to #GNUNET_YES if the /deposit succeeded + * Set to true if the /deposit succeeded * and we now can provide the resulting traits. */ - int deposit_succeeded; + bool deposit_succeeded; + + /** + * Expected entry in the coin history created by this + * operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che; /** * When did the exchange receive the deposit? */ - struct GNUNET_TIME_Absolute exchange_timestamp; + struct GNUNET_TIME_Timestamp exchange_timestamp; /** * Signing key used by the exchange to sign the @@ -160,7 +181,7 @@ struct DepositState * When we're referencing another deposit operation, * this will only be set after the command has been started. */ - int command_initialized; + bool command_initialized; /** * Reference to fetch the merchant private key from. @@ -194,8 +215,7 @@ do_retry (void *cls) struct DepositState *ds = cls; ds->retry_task = NULL; - ds->is->commands[ds->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (ds->is); deposit_run (ds, NULL, ds->is); @@ -211,7 +231,7 @@ do_retry (void *cls) */ static void deposit_cb (void *cls, - const struct TALER_EXCHANGE_DepositResult *dr) + const struct TALER_EXCHANGE_BatchDepositResult *dr) { struct DepositState *ds = cls; @@ -235,7 +255,8 @@ deposit_cb (void *cls, else ds->backoff = GNUNET_TIME_randomized_backoff (ds->backoff, MAX_BACKOFF); - ds->is->commands[ds->is->ip].num_tries++; + TALER_TESTING_inc_tries (ds->is); + GNUNET_assert (NULL == ds->retry_task); ds->retry_task = GNUNET_SCHEDULER_add_delayed (ds->backoff, &do_retry, @@ -243,24 +264,20 @@ deposit_cb (void *cls, return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - dr->hr.http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (dr->hr.reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status_with_body ( + ds->is, + dr->hr.http_status, + ds->expected_response_code, + dr->hr.reply); + return; } if (MHD_HTTP_OK == dr->hr.http_status) { - ds->deposit_succeeded = GNUNET_YES; - ds->exchange_timestamp = dr->details.success.deposit_timestamp; - ds->exchange_pub = *dr->details.success.exchange_pub; - ds->exchange_sig = *dr->details.success.exchange_sig; + ds->deposit_succeeded = true; + ds->exchange_timestamp = dr->details.ok.deposit_timestamp; + ds->exchange_pub = *dr->details.ok.exchange_pub; + ds->exchange_sig = *dr->details.ok.exchange_sig; } TALER_TESTING_interpreter_next (ds->is); } @@ -282,16 +299,46 @@ deposit_run (void *cls, const struct TALER_TESTING_Command *coin_cmd; const struct TALER_CoinSpendPrivateKeyP *coin_priv; struct TALER_CoinSpendPublicKeyP coin_pub; - const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + const struct TALER_AgeCommitmentHash *phac; const struct TALER_DenominationSignature *denom_pub_sig; - struct TALER_CoinSpendSignatureP coin_sig; - struct GNUNET_TIME_Absolute wire_deadline; struct TALER_MerchantPublicKeyP merchant_pub; - struct GNUNET_HashCode h_contract_terms; + struct TALER_PrivateContractHashP h_contract_terms; enum TALER_ErrorCode ec; + struct TALER_WireSaltP wire_salt; + const char *payto_uri; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &payto_uri), + GNUNET_JSON_spec_fixed_auto ("salt", + &wire_salt), + GNUNET_JSON_spec_end () + }; + const char *exchange_url + = TALER_TESTING_get_exchange_url (is); (void) cmd; + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } ds->is = is; + if (! GNUNET_TIME_absolute_is_zero (ds->refund_deadline.abs_time)) + { + struct GNUNET_TIME_Relative refund_deadline; + + refund_deadline + = GNUNET_TIME_absolute_get_remaining (ds->refund_deadline.abs_time); + ds->wire_deadline + = GNUNET_TIME_relative_to_timestamp ( + GNUNET_TIME_relative_multiply (refund_deadline, + 2)); + } + else + { + ds->refund_deadline = ds->wallet_timestamp; + ds->wire_deadline = GNUNET_TIME_timestamp_get (); + } if (NULL != ds->deposit_reference) { /* We're copying another deposit operation, initialize here. */ @@ -310,12 +357,14 @@ deposit_run (void *cls, ds->coin_reference = ods->coin_reference; ds->coin_index = ods->coin_index; ds->wire_details = json_incref (ods->wire_details); + GNUNET_assert (NULL != ds->wire_details); ds->contract_terms = json_incref (ods->contract_terms); ds->wallet_timestamp = ods->wallet_timestamp; ds->refund_deadline = ods->refund_deadline; + ds->wire_deadline = ods->wire_deadline; ds->amount = ods->amount; ds->merchant_priv = ods->merchant_priv; - ds->command_initialized = GNUNET_YES; + ds->command_initialized = true; } else if (NULL != ds->merchant_priv_reference) { @@ -333,7 +382,6 @@ deposit_run (void *cls, } if ( (GNUNET_OK != TALER_TESTING_get_trait_merchant_priv (cmd, - 0, &merchant_priv)) ) { GNUNET_break (0); @@ -342,6 +390,19 @@ deposit_run (void *cls, } ds->merchant_priv = *merchant_priv; } + GNUNET_assert (NULL != ds->wire_details); + if (GNUNET_OK != + GNUNET_JSON_parse (ds->wire_details, + spec, + NULL, NULL)) + { + json_dumpf (ds->wire_details, + stderr, + JSON_INDENT (2)); + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } GNUNET_assert (ds->coin_reference); coin_cmd = TALER_TESTING_interpreter_lookup_command (is, ds->coin_reference); @@ -357,9 +418,13 @@ deposit_run (void *cls, ds->coin_index, &coin_priv)) || (GNUNET_OK != + TALER_TESTING_get_trait_h_age_commitment (coin_cmd, + ds->coin_index, + &phac)) || + (GNUNET_OK != TALER_TESTING_get_trait_denom_pub (coin_cmd, ds->coin_index, - &denom_pub)) || + &ds->denom_pub)) || (GNUNET_OK != TALER_TESTING_get_trait_denom_sig (coin_cmd, ds->coin_index, @@ -372,58 +437,79 @@ deposit_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - ds->deposit_fee = denom_pub->fee_deposit; + + ds->deposit_fee = ds->denom_pub->fees.deposit; GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); - if (0 != ds->refund_deadline.abs_value_us) - { - struct GNUNET_TIME_Relative refund_deadline; - - refund_deadline = GNUNET_TIME_absolute_get_remaining (ds->refund_deadline); - wire_deadline = GNUNET_TIME_relative_to_absolute - (GNUNET_TIME_relative_multiply (refund_deadline, 2)); - } - else - { - ds->refund_deadline = ds->wallet_timestamp; - wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_ZERO); - } GNUNET_CRYPTO_eddsa_key_get_public (&ds->merchant_priv.eddsa_priv, &merchant_pub.eddsa_pub); - (void) GNUNET_TIME_round_abs (&wire_deadline); { - struct GNUNET_HashCode h_wire; + struct TALER_MerchantWireHashP h_wire; GNUNET_assert (GNUNET_OK == TALER_JSON_merchant_wire_signature_hash (ds->wire_details, &h_wire)); - TALER_EXCHANGE_deposit_permission_sign (&ds->amount, - &denom_pub->fee_deposit, - &h_wire, - &h_contract_terms, - &denom_pub->h_key, - coin_priv, - ds->wallet_timestamp, - &merchant_pub, - ds->refund_deadline, - &coin_sig); + TALER_wallet_deposit_sign (&ds->amount, + &ds->denom_pub->fees.deposit, + &h_wire, + &h_contract_terms, + NULL, /* wallet data hash */ + phac, + NULL, /* hash of extensions */ + &ds->denom_pub->h_key, + ds->wallet_timestamp, + &merchant_pub, + ds->refund_deadline, + coin_priv, + &ds->coin_sig); + ds->che.type = TALER_EXCHANGE_CTT_DEPOSIT; + ds->che.amount = ds->amount; + ds->che.details.deposit.h_wire = h_wire; + ds->che.details.deposit.h_contract_terms = h_contract_terms; + ds->che.details.deposit.no_h_policy = true; + ds->che.details.deposit.no_wallet_data_hash = true; + ds->che.details.deposit.wallet_timestamp = ds->wallet_timestamp; + ds->che.details.deposit.merchant_pub = merchant_pub; + ds->che.details.deposit.refund_deadline = ds->refund_deadline; + ds->che.details.deposit.sig = ds->coin_sig; + ds->che.details.deposit.no_hac = true; + ds->che.details.deposit.deposit_fee = ds->denom_pub->fees.deposit; + } + GNUNET_assert (NULL == ds->dh); + { + struct TALER_EXCHANGE_CoinDepositDetail cdd = { + .amount = ds->amount, + .coin_pub = coin_pub, + .coin_sig = ds->coin_sig, + .denom_sig = *denom_pub_sig, + .h_denom_pub = ds->denom_pub->h_key, + .h_age_commitment = {{{0}}}, + }; + struct TALER_EXCHANGE_DepositContractDetail dcd = { + .wire_deadline = ds->wire_deadline, + .merchant_payto_uri = payto_uri, + .wire_salt = wire_salt, + .h_contract_terms = h_contract_terms, + .wallet_timestamp = ds->wallet_timestamp, + .merchant_pub = merchant_pub, + .refund_deadline = ds->refund_deadline + }; + + if (NULL != phac) + cdd.h_age_commitment = *phac; + + ds->dh = TALER_EXCHANGE_batch_deposit ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + TALER_TESTING_get_keys (is), + &dcd, + 1, + &cdd, + &deposit_cb, + ds, + &ec); } - ds->dh = TALER_EXCHANGE_deposit (is->exchange, - &ds->amount, - wire_deadline, - ds->wire_details, - &h_contract_terms, - &coin_pub, - denom_pub_sig, - &denom_pub->key, - ds->wallet_timestamp, - &merchant_pub, - ds->refund_deadline, - &coin_sig, - &deposit_cb, - ds, - &ec); if (NULL == ds->dh) { GNUNET_break (0); @@ -451,11 +537,9 @@ deposit_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); - TALER_EXCHANGE_deposit_cancel (ds->dh); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_batch_deposit_cancel (ds->dh); ds->dh = NULL; } if (NULL != ds->retry_task) @@ -476,10 +560,9 @@ deposit_cleanup (void *cls, * @param[out] ret result. * @param trait name of the trait. * @param index index number of the object to offer. - * * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue deposit_traits (void *cls, const void **ret, const char *trait, @@ -489,8 +572,11 @@ deposit_traits (void *cls, const struct TALER_TESTING_Command *coin_cmd; /* Will point to coin cmd internals. */ const struct TALER_CoinSpendPrivateKeyP *coin_spent_priv; + struct TALER_CoinSpendPublicKeyP coin_spent_pub; + const struct TALER_AgeCommitmentProof *age_commitment_proof; + const struct TALER_AgeCommitmentHash *h_age_commitment; - if (GNUNET_YES != ds->command_initialized) + if (! ds->command_initialized) { /* No access to traits yet. */ GNUNET_break (0); @@ -506,40 +592,63 @@ deposit_traits (void *cls, TALER_TESTING_interpreter_fail (ds->is); return GNUNET_NO; } - if (GNUNET_OK != - TALER_TESTING_get_trait_coin_priv (coin_cmd, - ds->coin_index, - &coin_spent_priv)) + if ( (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_cmd, + ds->coin_index, + &coin_spent_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (coin_cmd, + ds->coin_index, + &age_commitment_proof)) || + (GNUNET_OK != + TALER_TESTING_get_trait_h_age_commitment (coin_cmd, + ds->coin_index, + &h_age_commitment)) ) { GNUNET_break (0); TALER_TESTING_interpreter_fail (ds->is); return GNUNET_NO; } + + GNUNET_CRYPTO_eddsa_key_get_public (&coin_spent_priv->eddsa_priv, + &coin_spent_pub.eddsa_pub); + { struct TALER_TESTING_Trait traits[] = { /* First two traits are only available if - ds->traits is #GNUNET_YES */ + ds->traits is true */ TALER_TESTING_make_trait_exchange_pub (0, &ds->exchange_pub), TALER_TESTING_make_trait_exchange_sig (0, &ds->exchange_sig), /* These traits are always available */ + TALER_TESTING_make_trait_coin_history (0, + &ds->che), TALER_TESTING_make_trait_coin_priv (0, coin_spent_priv), - TALER_TESTING_make_trait_wire_details (0, - ds->wire_details), - TALER_TESTING_make_trait_contract_terms (0, - ds->contract_terms), - TALER_TESTING_make_trait_merchant_priv (0, - &ds->merchant_priv), - TALER_TESTING_make_trait_amount_obj ( - TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_VALUE, - &ds->amount), - TALER_TESTING_make_trait_amount_obj ( - TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_FEE, - &ds->deposit_fee), - TALER_TESTING_make_trait_absolute_time (0, - &ds->exchange_timestamp), + TALER_TESTING_make_trait_coin_pub (0, + &coin_spent_pub), + TALER_TESTING_make_trait_denom_pub (0, + ds->denom_pub), + TALER_TESTING_make_trait_coin_sig (0, + &ds->coin_sig), + TALER_TESTING_make_trait_age_commitment_proof (0, + age_commitment_proof), + TALER_TESTING_make_trait_h_age_commitment (0, + h_age_commitment), + TALER_TESTING_make_trait_wire_details (ds->wire_details), + TALER_TESTING_make_trait_contract_terms (ds->contract_terms), + TALER_TESTING_make_trait_merchant_priv (&ds->merchant_priv), + TALER_TESTING_make_trait_deposit_amount (0, + &ds->amount), + TALER_TESTING_make_trait_deposit_fee_amount (0, + &ds->deposit_fee), + TALER_TESTING_make_trait_timestamp (0, + &ds->exchange_timestamp), + TALER_TESTING_make_trait_wire_deadline (0, + &ds->wire_deadline), + TALER_TESTING_make_trait_refund_deadline (0, + &ds->refund_deadline), TALER_TESTING_trait_end () }; @@ -553,46 +662,24 @@ deposit_traits (void *cls, } -/** - * Create a "deposit" command. - * - * @param label command label. - * @param coin_reference reference to any operation that can - * provide a coin. - * @param coin_index if @a withdraw_reference offers an array of - * coins, this parameter selects which one in that array. - * This value is currently ignored, as only one-coin - * withdrawals are implemented. - * @param target_account_payto target account for the "deposit" - * request. - * @param contract_terms contract terms to be signed over by the - * coin. - * @param refund_deadline refund deadline, zero means 'no refunds'. - * Note, if time were absolute, then it would have come - * one day and disrupt tests meaning. - * @param amount how much is going to be deposited. - * @param expected_response_code expected HTTP response code. - * - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_deposit (const char *label, - const char *coin_reference, - unsigned int coin_index, - const char *target_account_payto, - const char *contract_terms, - struct GNUNET_TIME_Relative refund_deadline, - const char *amount, - unsigned int expected_response_code) +TALER_TESTING_cmd_deposit ( + const char *label, + const char *coin_reference, + unsigned int coin_index, + const char *target_account_payto, + const char *contract_terms, + struct GNUNET_TIME_Relative refund_deadline, + const char *amount, + unsigned int expected_response_code) { struct DepositState *ds; - json_t *wire_details; - wire_details = TALER_TESTING_make_wire_details (target_account_payto); ds = GNUNET_new (struct DepositState); ds->coin_reference = coin_reference; ds->coin_index = coin_index; - ds->wire_details = wire_details; + ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto); + GNUNET_assert (NULL != ds->wire_details); ds->contract_terms = json_loads (contract_terms, JSON_REJECT_DUPLICATES, NULL); @@ -605,25 +692,26 @@ TALER_TESTING_cmd_deposit (const char *label, label); GNUNET_assert (0); } - ds->wallet_timestamp = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&ds->wallet_timestamp); - - json_object_set_new (ds->contract_terms, - "timestamp", - GNUNET_JSON_from_time_abs (ds->wallet_timestamp)); - if (0 != refund_deadline.rel_value_us) + ds->wallet_timestamp = GNUNET_TIME_timestamp_get (); + GNUNET_assert (0 == + json_object_set_new (ds->contract_terms, + "timestamp", + GNUNET_JSON_from_timestamp ( + ds->wallet_timestamp))); + if (! GNUNET_TIME_relative_is_zero (refund_deadline)) { - ds->refund_deadline = GNUNET_TIME_relative_to_absolute (refund_deadline); - (void) GNUNET_TIME_round_abs (&ds->refund_deadline); - json_object_set_new (ds->contract_terms, - "refund_deadline", - GNUNET_JSON_from_time_abs (ds->refund_deadline)); + ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline); + GNUNET_assert (0 == + json_object_set_new (ds->contract_terms, + "refund_deadline", + GNUNET_JSON_from_timestamp ( + ds->refund_deadline))); } GNUNET_assert (GNUNET_OK == TALER_string_to_amount (amount, &ds->amount)); ds->expected_response_code = expected_response_code; - ds->command_initialized = GNUNET_YES; + ds->command_initialized = true; { struct TALER_TESTING_Command cmd = { .cls = ds, @@ -638,50 +726,26 @@ TALER_TESTING_cmd_deposit (const char *label, } -/** - * Create a "deposit" command that references an existing merchant key. - * - * @param label command label. - * @param coin_reference reference to any operation that can - * provide a coin. - * @param coin_index if @a withdraw_reference offers an array of - * coins, this parameter selects which one in that array. - * This value is currently ignored, as only one-coin - * withdrawals are implemented. - * @param target_account_payto target account for the "deposit" - * request. - * @param contract_terms contract terms to be signed over by the - * coin. - * @param refund_deadline refund deadline, zero means 'no refunds'. - * Note, if time were absolute, then it would have come - * one day and disrupt tests meaning. - * @param amount how much is going to be deposited. - * @param expected_response_code expected HTTP response code. - * @param merchant_priv_reference reference to another operation - * that has a merchant private key trait - * - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_deposit_with_ref (const char *label, - const char *coin_reference, - unsigned int coin_index, - const char *target_account_payto, - const char *contract_terms, - struct GNUNET_TIME_Relative refund_deadline, - const char *amount, - unsigned int expected_response_code, - const char *merchant_priv_reference) +TALER_TESTING_cmd_deposit_with_ref ( + const char *label, + const char *coin_reference, + unsigned int coin_index, + const char *target_account_payto, + const char *contract_terms, + struct GNUNET_TIME_Relative refund_deadline, + const char *amount, + unsigned int expected_response_code, + const char *merchant_priv_reference) { struct DepositState *ds; - json_t *wire_details; - wire_details = TALER_TESTING_make_wire_details (target_account_payto); ds = GNUNET_new (struct DepositState); ds->merchant_priv_reference = merchant_priv_reference; ds->coin_reference = coin_reference; ds->coin_index = coin_index; - ds->wire_details = wire_details; + ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto); + GNUNET_assert (NULL != ds->wire_details); ds->contract_terms = json_loads (contract_terms, JSON_REJECT_DUPLICATES, NULL); @@ -693,25 +757,26 @@ TALER_TESTING_cmd_deposit_with_ref (const char *label, label); GNUNET_assert (0); } - ds->wallet_timestamp = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&ds->wallet_timestamp); - - json_object_set_new (ds->contract_terms, - "timestamp", - GNUNET_JSON_from_time_abs (ds->wallet_timestamp)); + ds->wallet_timestamp = GNUNET_TIME_timestamp_get (); + GNUNET_assert (0 == + json_object_set_new (ds->contract_terms, + "timestamp", + GNUNET_JSON_from_timestamp ( + ds->wallet_timestamp))); if (0 != refund_deadline.rel_value_us) { - ds->refund_deadline = GNUNET_TIME_relative_to_absolute (refund_deadline); - (void) GNUNET_TIME_round_abs (&ds->refund_deadline); - json_object_set_new (ds->contract_terms, - "refund_deadline", - GNUNET_JSON_from_time_abs (ds->refund_deadline)); + ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline); + GNUNET_assert (0 == + json_object_set_new (ds->contract_terms, + "refund_deadline", + GNUNET_JSON_from_timestamp ( + ds->refund_deadline))); } GNUNET_assert (GNUNET_OK == TALER_string_to_amount (amount, &ds->amount)); ds->expected_response_code = expected_response_code; - ds->command_initialized = GNUNET_YES; + ds->command_initialized = true; { struct TALER_TESTING_Command cmd = { .cls = ds, @@ -726,19 +791,11 @@ TALER_TESTING_cmd_deposit_with_ref (const char *label, } -/** - * Create a "deposit" command that repeats an existing - * deposit command. - * - * @param label command label. - * @param deposit_reference which deposit command should we repeat - * @param expected_response_code expected HTTP response code. - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_deposit_replay (const char *label, - const char *deposit_reference, - unsigned int expected_response_code) +TALER_TESTING_cmd_deposit_replay ( + const char *label, + const char *deposit_reference, + unsigned int expected_response_code) { struct DepositState *ds; @@ -759,13 +816,6 @@ TALER_TESTING_cmd_deposit_replay (const char *label, } -/** - * Modify a deposit command to enable retries when we get transient - * errors from the exchange. - * - * @param cmd a deposit command - * @return the command with retries enabled - */ struct TALER_TESTING_Command TALER_TESTING_cmd_deposit_with_retry (struct TALER_TESTING_Command cmd) { diff --git a/src/testing/testing_api_cmd_deposits_get.c b/src/testing/testing_api_cmd_deposits_get.c index 61358291b..5d4436e2a 100644 --- a/src/testing/testing_api_cmd_deposits_get.c +++ b/src/testing/testing_api_cmd_deposits_get.c @@ -40,6 +40,11 @@ struct TrackTransactionState const char *bank_transfer_reference; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * The WTID associated by the transaction being tracked. */ struct TALER_WireTransferIdentifierRawP wtid; @@ -50,12 +55,31 @@ struct TrackTransactionState unsigned int expected_response_code; /** + * Set to the KYC requirement payto hash *if* the exchange replied with a + * request for KYC (#MHD_HTTP_ACCEPTED). + * Note: set based on our @e merchant_payto_uri, as + * the exchange does not respond with the payto hash. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC (#MHD_HTTP_ACCEPTED). + */ + uint64_t requirement_row; + + /** * Reference to any operation that can provide a transaction. * Will be the transaction to track. */ const char *transaction_reference; /** + * Payto URI of the merchant receiving the deposit. + */ + char *merchant_payto_uri; + + /** * Index of the coin involved in the transaction. Recall: * at the exchange, the tracking is done _per coin_. */ @@ -88,28 +112,19 @@ deposit_wtid_cb (void *cls, { struct TrackTransactionState *tts = cls; struct TALER_TESTING_Interpreter *is = tts->is; - struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; tts->tth = NULL; if (tts->expected_response_code != dr->hr.http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - dr->hr.http_status, - (int) dr->hr.ec, - cmd->label, - __FILE__, - __LINE__); - json_dumpf (dr->hr.reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (is); + TALER_TESTING_unexpected_status (is, + dr->hr.http_status, + tts->expected_response_code); return; } switch (dr->hr.http_status) { case MHD_HTTP_OK: - tts->wtid = dr->details.success.wtid; + tts->wtid = dr->details.ok.wtid; if (NULL != tts->bank_transfer_reference) { const struct TALER_TESTING_Command *bank_transfer_cmd; @@ -128,7 +143,6 @@ deposit_wtid_cb (void *cls, if (GNUNET_OK != TALER_TESTING_get_trait_wtid (bank_transfer_cmd, - 0, &wtid_want)) { GNUNET_break (0); @@ -137,7 +151,7 @@ deposit_wtid_cb (void *cls, } /* Compare that expected and gotten subjects match. */ - if (0 != GNUNET_memcmp (&dr->details.success.wtid, + if (0 != GNUNET_memcmp (&dr->details.ok.wtid, wtid_want)) { GNUNET_break (0); @@ -148,6 +162,10 @@ deposit_wtid_cb (void *cls, break; case MHD_HTTP_ACCEPTED: /* allowed, nothing to check here */ + TALER_payto_hash (tts->merchant_payto_uri, + &tts->h_payto); + tts->requirement_row + = dr->details.accepted.requirement_row; break; case MHD_HTTP_NOT_FOUND: /* allowed, nothing to check here */ @@ -178,11 +196,11 @@ track_transaction_run (void *cls, struct TALER_CoinSpendPublicKeyP coin_pub; const json_t *contract_terms; const json_t *wire_details; - struct GNUNET_HashCode h_wire_details; - struct GNUNET_HashCode h_contract_terms; + struct TALER_MerchantWireHashP h_wire_details; + struct TALER_PrivateContractHashP h_contract_terms; const struct TALER_MerchantPrivateKeyP *merchant_priv; - (void) cmd; + tts->cmd = cmd; tts->is = is; transaction_cmd = TALER_TESTING_interpreter_lookup_command (tts->is, @@ -210,17 +228,17 @@ track_transaction_run (void *cls, /* Get the strings.. */ if (GNUNET_OK != TALER_TESTING_get_trait_wire_details (transaction_cmd, - 0, &wire_details)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); return; } - + tts->merchant_payto_uri + = GNUNET_strdup (json_string_value (json_object_get (wire_details, + "payto_uri"))); if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms (transaction_cmd, - 0, &contract_terms)) { GNUNET_break (0); @@ -247,7 +265,6 @@ track_transaction_run (void *cls, if (GNUNET_OK != TALER_TESTING_get_trait_merchant_priv (transaction_cmd, - 0, &merchant_priv)) { GNUNET_break (0); @@ -255,13 +272,17 @@ track_transaction_run (void *cls, return; } - tts->tth = TALER_EXCHANGE_deposits_get (is->exchange, - merchant_priv, - &h_wire_details, - &h_contract_terms, - &coin_pub, - &deposit_wtid_cb, - tts); + tts->tth = TALER_EXCHANGE_deposits_get ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + merchant_priv, + &h_wire_details, + &h_contract_terms, + &coin_pub, + GNUNET_TIME_UNIT_ZERO, + &deposit_wtid_cb, + tts); GNUNET_assert (NULL != tts->tth); } @@ -281,13 +302,12 @@ track_transaction_cleanup (void *cls, if (NULL != tts->tth) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - tts->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (tts->is, + cmd->label); TALER_EXCHANGE_deposits_get_cancel (tts->tth); tts->tth = NULL; } + GNUNET_free (tts->merchant_payto_uri); GNUNET_free (tts); } @@ -301,7 +321,7 @@ track_transaction_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue track_transaction_traits (void *cls, const void **ret, const char *trait, @@ -309,7 +329,11 @@ track_transaction_traits (void *cls, { struct TrackTransactionState *tts = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_wtid (0, &tts->wtid), + TALER_TESTING_make_trait_wtid (&tts->wtid), + TALER_TESTING_make_trait_legi_requirement_row ( + &tts->requirement_row), + TALER_TESTING_make_trait_h_payto (&tts->h_payto), + TALER_TESTING_make_trait_payto_uri (tts->merchant_payto_uri), TALER_TESTING_trait_end () }; @@ -320,19 +344,6 @@ track_transaction_traits (void *cls, } -/** - * Create a "track transaction" command. - * - * @param label the command label. - * @param transaction_reference reference to a deposit operation, - * will be used to get the input data for the track. - * @param coin_index index of the coin involved in the transaction. - * @param expected_response_code expected HTTP response code. - * @param bank_transfer_reference reference to a command that - * can offer a WTID so as to check that against what WTID - * the tracked operation has. Set as NULL if not needed. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_track_transaction (const char *label, const char *transaction_reference, diff --git a/src/testing/testing_api_cmd_exec_aggregator.c b/src/testing/testing_api_cmd_exec_aggregator.c index 6ec56c175..1f05576ff 100644 --- a/src/testing/testing_api_cmd_exec_aggregator.c +++ b/src/testing/testing_api_cmd_exec_aggregator.c @@ -43,6 +43,11 @@ struct AggregatorState * Configuration file used by the aggregator. */ const char *config_filename; + + /** + * Run with KYC restrictions on. + */ + bool kyc_on; }; @@ -68,6 +73,9 @@ aggregator_run (void *cls, "taler-exchange-aggregator", "-c", as->config_filename, "-t", /* exit when done */ + (as->kyc_on) + ? NULL + : "-y", /* skip KYC */ NULL); if (NULL == as->aggregator_proc) { @@ -115,7 +123,7 @@ aggregator_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue aggregator_traits (void *cls, const void **ret, const char *trait, @@ -123,7 +131,7 @@ aggregator_traits (void *cls, { struct AggregatorState *as = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &as->aggregator_proc), + TALER_TESTING_make_trait_process (&as->aggregator_proc), TALER_TESTING_trait_end () }; @@ -134,14 +142,6 @@ aggregator_traits (void *cls, } -/** - * Make a "aggregator" CMD. - * - * @param label command label. - * @param config_filename configuration file for the - * aggregator to use. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_exec_aggregator (const char *label, const char *config_filename) @@ -164,4 +164,27 @@ TALER_TESTING_cmd_exec_aggregator (const char *label, } +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_aggregator_with_kyc (const char *label, + const char *config_filename) +{ + struct AggregatorState *as; + + as = GNUNET_new (struct AggregatorState); + as->config_filename = config_filename; + as->kyc_on = true; + { + struct TALER_TESTING_Command cmd = { + .cls = as, + .label = label, + .run = &aggregator_run, + .cleanup = &aggregator_cleanup, + .traits = &aggregator_traits + }; + + return cmd; + } +} + + /* end of testing_api_cmd_exec_aggregator.c */ diff --git a/src/testing/testing_api_cmd_exec_auditor-offline.c b/src/testing/testing_api_cmd_exec_auditor-offline.c new file mode 100644 index 000000000..1899d5c06 --- /dev/null +++ b/src/testing/testing_api_cmd_exec_auditor-offline.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 <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_exec_auditor-offline.c + * @brief run the taler-exchange-auditor-offline command + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "auditor-offline" CMD. + */ +struct AuditorOfflineState +{ + + /** + * AuditorOffline process. + */ + struct GNUNET_OS_Process *auditor_offline_proc; + + /** + * Configuration file used by the auditor-offline. + */ + const char *config_filename; + +}; + + +/** + * Run the command. Use the `taler-exchange-auditor-offline' program. + * + * @param cls closure. + * @param cmd command being run. + * @param is interpreter state. + */ +static void +auditor_offline_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AuditorOfflineState *as = cls; + + (void) cmd; + as->auditor_offline_proc + = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-auditor-offline", + "taler-auditor-offline", + "-c", as->config_filename, + "-L", "INFO", + "download", + "sign", + "upload", + NULL); + if (NULL == as->auditor_offline_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "auditor-offline" CMD, and possibly kill its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +auditor_offline_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AuditorOfflineState *as = cls; + + (void) cmd; + if (NULL != as->auditor_offline_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (as->auditor_offline_proc, + SIGKILL)); + GNUNET_OS_process_wait (as->auditor_offline_proc); + GNUNET_OS_process_destroy (as->auditor_offline_proc); + as->auditor_offline_proc = NULL; + } + GNUNET_free (as); +} + + +/** + * Offer "auditor-offline" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +auditor_offline_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct AuditorOfflineState *as = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&as->auditor_offline_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_auditor_offline (const char *label, + const char *config_filename) +{ + struct AuditorOfflineState *as; + + as = GNUNET_new (struct AuditorOfflineState); + as->config_filename = config_filename; + { + struct TALER_TESTING_Command cmd = { + .cls = as, + .label = label, + .run = &auditor_offline_run, + .cleanup = &auditor_offline_cleanup, + .traits = &auditor_offline_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_auditor-offline.c */ diff --git a/src/testing/testing_api_cmd_exec_closer.c b/src/testing/testing_api_cmd_exec_closer.c index c1153babb..2501b39a6 100644 --- a/src/testing/testing_api_cmd_exec_closer.c +++ b/src/testing/testing_api_cmd_exec_closer.c @@ -49,7 +49,7 @@ struct CloserState * expect_close is true. Will be of type * #TALER_EXCHANGE_RTT_RESERVE_CLOSED. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; /** * If the closer filled a reserve (@e expect_close is set), this is set to @@ -65,7 +65,7 @@ struct CloserState /** * Do we expect the command to actually close a reserve? */ - int expect_close; + bool expect_close; }; @@ -91,9 +91,9 @@ closer_run (void *cls, rcmd = TALER_TESTING_interpreter_lookup_command (is, as->reserve_ref); + GNUNET_assert (NULL != rcmd); if (GNUNET_OK != TALER_TESTING_get_trait_reserve_pub (rcmd, - 0, &reserve_pubp)) { GNUNET_break (0); @@ -156,7 +156,7 @@ closer_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue closer_traits (void *cls, const void **ret, const char *trait, @@ -164,13 +164,12 @@ closer_traits (void *cls, { struct CloserState *as = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &as->closer_proc), + TALER_TESTING_make_trait_process (&as->closer_proc), TALER_TESTING_trait_end () }; struct TALER_TESTING_Trait xtraits[] = { - TALER_TESTING_make_trait_process (0, &as->closer_proc), - TALER_TESTING_make_trait_reserve_pub (0, - &as->reserve_pub), + TALER_TESTING_make_trait_process (&as->closer_proc), + TALER_TESTING_make_trait_reserve_pub (&as->reserve_pub), TALER_TESTING_make_trait_reserve_history (0, &as->reserve_history), TALER_TESTING_trait_end () @@ -185,22 +184,6 @@ closer_traits (void *cls, } -/** - * Make a "closer" CMD. Note that it is right now not supported to run the - * closer to close multiple reserves in combination with a subsequent reserve - * status call, as we cannot generate the traits necessary for multiple closed - * reserves. You can work around this by using multiple closer commands, one - * per reserve that is being closed. - * - * @param label command label. - * @param config_filename configuration file for the - * closer to use. - * @param expected_amount amount we expect to see wired from a @a expected_reserve_ref - * @param expected_fee closing fee we expect to see - * @param expected_reserve_ref reference to a reserve we expect the closer to drain; - * NULL if we do not expect the closer to do anything - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_exec_closer (const char *label, const char *config_filename, @@ -214,7 +197,7 @@ TALER_TESTING_cmd_exec_closer (const char *label, as->config_filename = config_filename; if (NULL != expected_reserve_ref) { - as->expect_close = GNUNET_YES; + as->expect_close = true; as->reserve_ref = expected_reserve_ref; if (GNUNET_OK != TALER_string_to_amount (expected_amount, @@ -239,11 +222,11 @@ TALER_TESTING_cmd_exec_closer (const char *label, /* expected amount includes fee, while our argument gives the amount _without_ the fee. So add the fee. */ GNUNET_assert (0 <= - TALER_amount_add (&as->reserve_history.amount, - &as->reserve_history.amount, - &as->reserve_history.details.close_details. - fee)); - as->reserve_history.type = TALER_EXCHANGE_RTT_CLOSE; + TALER_amount_add ( + &as->reserve_history.amount, + &as->reserve_history.amount, + &as->reserve_history.details.close_details.fee)); + as->reserve_history.type = TALER_EXCHANGE_RTT_CLOSING; } { struct TALER_TESTING_Command cmd = { diff --git a/src/testing/testing_api_cmd_exec_expire.c b/src/testing/testing_api_cmd_exec_expire.c new file mode 100644 index 000000000..aabf37361 --- /dev/null +++ b/src/testing/testing_api_cmd_exec_expire.c @@ -0,0 +1,162 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_exec_expire.c + * @brief run the taler-exchange-expire command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "expire" CMD. + */ +struct ExpireState +{ + + /** + * Process for the expireer. + */ + struct GNUNET_OS_Process *expire_proc; + + /** + * Configuration file used by the expireer. + */ + const char *config_filename; +}; + + +/** + * Run the command; use the `taler-exchange-expire' program. + * + * @param cls closure. + * @param cmd command currently being executed. + * @param is interpreter state. + */ +static void +expire_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct ExpireState *ws = cls; + + (void) cmd; + ws->expire_proc + = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-expire", + "taler-exchange-expire", + "-L", "INFO", + "-c", ws->config_filename, + "-t", /* exit when done */ + NULL); + if (NULL == ws->expire_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "expire" CMD, and possibly + * kills its process if it did not terminate regularly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +expire_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct ExpireState *ws = cls; + + (void) cmd; + if (NULL != ws->expire_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ws->expire_proc, + SIGKILL)); + GNUNET_OS_process_wait (ws->expire_proc); + GNUNET_OS_process_destroy (ws->expire_proc); + ws->expire_proc = NULL; + } + GNUNET_free (ws); +} + + +/** + * Offer "expire" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +expire_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct ExpireState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ws->expire_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_expire (const char *label, + const char *config_filename) +{ + struct ExpireState *ws; + + ws = GNUNET_new (struct ExpireState); + ws->config_filename = config_filename; + + { + struct TALER_TESTING_Command cmd = { + .cls = ws, + .label = label, + .run = &expire_run, + .cleanup = &expire_cleanup, + .traits = &expire_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_expire.c */ diff --git a/src/testing/testing_api_cmd_exec_router.c b/src/testing/testing_api_cmd_exec_router.c new file mode 100644 index 000000000..d7f3fe265 --- /dev/null +++ b/src/testing/testing_api_cmd_exec_router.c @@ -0,0 +1,161 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_exec_router.c + * @brief run the taler-exchange-router command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "router" CMD. + */ +struct RouterState +{ + + /** + * Process for the routerer. + */ + struct GNUNET_OS_Process *router_proc; + + /** + * Configuration file used by the routerer. + */ + const char *config_filename; +}; + + +/** + * Run the command; use the `taler-exchange-router' program. + * + * @param cls closure. + * @param cmd command currently being executed. + * @param is interpreter state. + */ +static void +router_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RouterState *ws = cls; + + (void) cmd; + ws->router_proc + = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-router", + "taler-exchange-router", + "-c", ws->config_filename, + "-t", /* exit when done */ + NULL); + if (NULL == ws->router_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "router" CMD, and possibly + * kills its process if it did not terminate regularly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +router_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct RouterState *ws = cls; + + (void) cmd; + if (NULL != ws->router_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ws->router_proc, + SIGKILL)); + GNUNET_OS_process_wait (ws->router_proc); + GNUNET_OS_process_destroy (ws->router_proc); + ws->router_proc = NULL; + } + GNUNET_free (ws); +} + + +/** + * Offer "router" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +router_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct RouterState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ws->router_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_router (const char *label, + const char *config_filename) +{ + struct RouterState *ws; + + ws = GNUNET_new (struct RouterState); + ws->config_filename = config_filename; + + { + struct TALER_TESTING_Command cmd = { + .cls = ws, + .label = label, + .run = &router_run, + .cleanup = &router_cleanup, + .traits = &router_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_router.c */ diff --git a/src/testing/testing_api_cmd_exec_transfer.c b/src/testing/testing_api_cmd_exec_transfer.c index 796f32d07..300413b4b 100644 --- a/src/testing/testing_api_cmd_exec_transfer.c +++ b/src/testing/testing_api_cmd_exec_transfer.c @@ -60,13 +60,13 @@ transfer_run (void *cls, { struct TransferState *as = cls; + (void) cmd; as->transfer_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, "taler-exchange-transfer", "taler-exchange-transfer", "-c", as->config_filename, - "-L", "INFO", "-S", "1", "-w", "0", "-t", /* exit when done */ @@ -94,6 +94,7 @@ transfer_cleanup (void *cls, { struct TransferState *as = cls; + (void) cmd; if (NULL != as->transfer_proc) { GNUNET_break (0 == @@ -116,7 +117,7 @@ transfer_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue transfer_traits (void *cls, const void **ret, const char *trait, @@ -124,7 +125,7 @@ transfer_traits (void *cls, { struct TransferState *as = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &as->transfer_proc), + TALER_TESTING_make_trait_process (&as->transfer_proc), TALER_TESTING_trait_end () }; @@ -135,14 +136,6 @@ transfer_traits (void *cls, } -/** - * Make a "transfer" CMD. - * - * @param label command label. - * @param config_filename configuration file for the - * transfer to use. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_exec_transfer (const char *label, const char *config_filename) diff --git a/src/testing/testing_api_cmd_exec_wget.c b/src/testing/testing_api_cmd_exec_wget.c new file mode 100644 index 000000000..67aceca0a --- /dev/null +++ b/src/testing/testing_api_cmd_exec_wget.c @@ -0,0 +1,158 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_exec_wget.c + * @brief run a wget command + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "wget" CMD. + */ +struct WgetState +{ + /** + * Process for the wgeter. + */ + struct GNUNET_OS_Process *wget_proc; + + /** + * URL to used by the wget. + */ + const char *url; +}; + + +/** + * Run the command; use the `wget' program. + * + * @param cls closure. + * @param cmd command currently being executed. + * @param is interpreter state. + */ +static void +wget_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct WgetState *ws = cls; + + (void) cmd; + ws->wget_proc + = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "wget", + "wget", + ws->url, + NULL); + if (NULL == ws->wget_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "wget" CMD, and possibly + * kills its process if it did not terminate regularly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +wget_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct WgetState *ws = cls; + + (void) cmd; + if (NULL != ws->wget_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ws->wget_proc, + SIGKILL)); + GNUNET_OS_process_wait (ws->wget_proc); + GNUNET_OS_process_destroy (ws->wget_proc); + ws->wget_proc = NULL; + } + GNUNET_free (ws); +} + + +/** + * Offer "wget" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +wget_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct WgetState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ws->wget_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_wget (const char *label, + const char *url) +{ + struct WgetState *ws; + + ws = GNUNET_new (struct WgetState); + ws->url = url; + + { + struct TALER_TESTING_Command cmd = { + .cls = ws, + .label = label, + .run = &wget_run, + .cleanup = &wget_cleanup, + .traits = &wget_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_wget.c */ diff --git a/src/testing/testing_api_cmd_exec_wirewatch.c b/src/testing/testing_api_cmd_exec_wirewatch.c index 2c50b4f4a..b6ed4f0f1 100644 --- a/src/testing/testing_api_cmd_exec_wirewatch.c +++ b/src/testing/testing_api_cmd_exec_wirewatch.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018 Taler Systems SA + Copyright (C) 2018, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -34,7 +34,6 @@ */ struct WirewatchState { - /** * Process for the wirewatcher. */ @@ -44,8 +43,14 @@ struct WirewatchState * Configuration file used by the wirewatcher. */ const char *config_filename; + + /** + * Account section to be used by the wirewatcher. + */ + const char *account_section; }; + /** * Run the command; use the `taler-exchange-wirewatch' program. * @@ -60,6 +65,7 @@ wirewatch_run (void *cls, { struct WirewatchState *ws = cls; + (void) cmd; ws->wirewatch_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, @@ -69,6 +75,10 @@ wirewatch_run (void *cls, "-S", "1", "-w", "0", "-t", /* exit when done */ + (NULL == ws->account_section) + ? NULL + : "-a", + ws->account_section, NULL); if (NULL == ws->wirewatch_proc) { @@ -93,6 +103,7 @@ wirewatch_cleanup (void *cls, { struct WirewatchState *ws = cls; + (void) cmd; if (NULL != ws->wirewatch_proc) { GNUNET_break (0 == @@ -115,7 +126,7 @@ wirewatch_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue wirewatch_traits (void *cls, const void **ret, const char *trait, @@ -123,8 +134,7 @@ wirewatch_traits (void *cls, { struct WirewatchState *ws = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, - &ws->wirewatch_proc), + TALER_TESTING_make_trait_process (&ws->wirewatch_proc), TALER_TESTING_trait_end () }; @@ -135,22 +145,16 @@ wirewatch_traits (void *cls, } -/** - * Make a "wirewatch" CMD. - * - * @param label command label. - * @param config_filename configuration filename. - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_exec_wirewatch (const char *label, - const char *config_filename) +TALER_TESTING_cmd_exec_wirewatch2 (const char *label, + const char *config_filename, + const char *account_section) { struct WirewatchState *ws; ws = GNUNET_new (struct WirewatchState); ws->config_filename = config_filename; - + ws->account_section = account_section; { struct TALER_TESTING_Command cmd = { .cls = ws, @@ -165,4 +169,14 @@ TALER_TESTING_cmd_exec_wirewatch (const char *label, } +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_wirewatch (const char *label, + const char *config_filename) +{ + return TALER_TESTING_cmd_exec_wirewatch2 (label, + config_filename, + NULL); +} + + /* end of testing_api_cmd_exec_wirewatch.c */ diff --git a/src/testing/testing_api_cmd_get_auditor.c b/src/testing/testing_api_cmd_get_auditor.c new file mode 100644 index 000000000..ab4e32665 --- /dev/null +++ b/src/testing/testing_api_cmd_get_auditor.c @@ -0,0 +1,286 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_get_auditor.c + * @brief Command to get an auditor handle + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "get auditor" CMD. + */ +struct GetAuditorState +{ + + /** + * Private key of the auditor. + */ + struct TALER_AuditorPrivateKeyP auditor_priv; + + /** + * Public key of the auditor. + */ + struct TALER_AuditorPublicKeyP auditor_pub; + + /** + * Our interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Auditor handle used to get the configuration. + */ + struct TALER_AUDITOR_GetConfigHandle *auditor; + + /** + * URL of the auditor. + */ + char *auditor_url; + + /** + * Filename of the master private key of the auditor. + */ + char *priv_file; + +}; + + +/** + * Function called with information about the auditor. + * + * @param cls closure + * @param vr response data + */ +static void +version_cb ( + void *cls, + const struct TALER_AUDITOR_ConfigResponse *vr) +{ + struct GetAuditorState *gas = cls; + + gas->auditor = NULL; + if (MHD_HTTP_OK != vr->hr.http_status) + { + TALER_TESTING_unexpected_status (gas->is, + vr->hr.http_status, + MHD_HTTP_OK); + return; + } + if ( (NULL != gas->priv_file) && + (0 != GNUNET_memcmp (&gas->auditor_pub, + &vr->details.ok.vi.auditor_pub)) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (gas->is); + return; + } + TALER_TESTING_interpreter_next (gas->is); +} + + +/** + * Run the "get_auditor" command. + * + * @param cls closure. + * @param cmd the command currently being executed. + * @param is the interpreter state. + */ +static void +get_auditor_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetAuditorState *gas = cls; + + (void) cmd; + if (NULL == gas->auditor_url) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL != gas->priv_file) + { + if (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_key_from_file (gas->priv_file, + GNUNET_YES, + &gas->auditor_priv.eddsa_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&gas->auditor_priv.eddsa_priv, + &gas->auditor_pub.eddsa_pub); + } + gas->is = is; + gas->auditor + = TALER_AUDITOR_get_config (TALER_TESTING_interpreter_get_context (is), + gas->auditor_url, + &version_cb, + gas); + if (NULL == gas->auditor) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Cleanup the state. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +get_auditor_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetAuditorState *gas = cls; + + if (NULL != gas->auditor) + { + GNUNET_break (0); + TALER_AUDITOR_get_config_cancel (gas->auditor); + gas->auditor = NULL; + } + GNUNET_free (gas->priv_file); + GNUNET_free (gas->auditor_url); + GNUNET_free (gas); +} + + +/** + * Offer internal data to a "get_auditor" CMD state to other commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +get_auditor_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct GetAuditorState *gas = cls; + unsigned int off = (NULL == gas->priv_file) ? 2 : 0; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_auditor_priv (&gas->auditor_priv), + TALER_TESTING_make_trait_auditor_pub (&gas->auditor_pub), + TALER_TESTING_make_trait_auditor_url (gas->auditor_url), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (&traits[off], + ret, + trait, + index); +} + + +/** + * Get the base URL of the auditor from @a cfg. + * + * @param cfg configuration to evaluate + * @return base URL of the auditor according to @a cfg + */ +static char * +get_auditor_base_url ( + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *auditor_url; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "auditor", + "BASE_URL", + &auditor_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "BASE_URL"); + return NULL; + } + return auditor_url; +} + + +/** + * Get the file name of the master private key file of the auditor from @a + * cfg. + * + * @param cfg configuration to evaluate + * @return base URL of the auditor according to @a cfg + */ +static char * +get_auditor_priv_file ( + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *fn; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "auditor", + "AUDITOR_PRIV_FILE", + &fn)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "AUDITOR_PRIV_FILE"); + return NULL; + } + return fn; +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_get_auditor ( + const char *label, + const struct GNUNET_CONFIGURATION_Handle *cfg, + bool load_auditor_keys) +{ + struct GetAuditorState *gas; + + gas = GNUNET_new (struct GetAuditorState); + gas->auditor_url = get_auditor_base_url (cfg); + if (load_auditor_keys) + gas->priv_file = get_auditor_priv_file (cfg); + { + struct TALER_TESTING_Command cmd = { + .cls = gas, + .label = label, + .run = &get_auditor_run, + .cleanup = &get_auditor_cleanup, + .traits = &get_auditor_traits, + .name = "auditor" + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_get_exchange.c b/src/testing/testing_api_cmd_get_exchange.c new file mode 100644 index 000000000..69a6e82b0 --- /dev/null +++ b/src/testing/testing_api_cmd_get_exchange.c @@ -0,0 +1,411 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_get_exchange.c + * @brief Command to get an exchange handle + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "get exchange" CMD. + */ +struct GetExchangeState +{ + + /** + * Master private key of the exchange. + */ + struct TALER_MasterPrivateKeyP master_priv; + + /** + * Our interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Exchange handle we produced. + */ + struct TALER_EXCHANGE_GetKeysHandle *exchange; + + /** + * Keys of the exchange. + */ + struct TALER_EXCHANGE_Keys *keys; + + /** + * URL of the exchange. + */ + char *exchange_url; + + /** + * Filename of the master private key of the exchange. + */ + char *master_priv_file; + + /** + * Label of a command to use to obtain existing + * keys. + */ + const char *last_keys_ref; + + /** + * Last denomination date we received when doing this request. + */ + struct GNUNET_TIME_Timestamp my_denom_date; + + /** + * Are we waiting for /keys before continuing? + */ + bool wait_for_keys; +}; + + +/** + * Function called with information about who is auditing + * a particular exchange and what keys the exchange is using. + * + * @param cls closure + * @param kr response from /keys + * @param[in] keys the keys of the exchange + */ +static void +cert_cb (void *cls, + const struct TALER_EXCHANGE_KeysResponse *kr, + struct TALER_EXCHANGE_Keys *keys) +{ + struct GetExchangeState *ges = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr; + struct TALER_TESTING_Interpreter *is = ges->is; + + ges->exchange = NULL; + ges->keys = keys; + switch (hr->http_status) + { + case MHD_HTTP_OK: + if (ges->wait_for_keys) + { + ges->wait_for_keys = false; + TALER_TESTING_interpreter_next (is); + return; + } + ges->my_denom_date = kr->details.ok.keys->last_denom_issue_date; + return; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "/keys responded with HTTP status %u\n", + hr->http_status); + if (ges->wait_for_keys) + { + ges->wait_for_keys = false; + TALER_TESTING_interpreter_fail (is); + return; + } + return; + } +} + + +/** + * Run the "get_exchange" command. + * + * @param cls closure. + * @param cmd the command currently being executed. + * @param is the interpreter state. + */ +static void +get_exchange_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetExchangeState *ges = cls; + struct TALER_EXCHANGE_Keys *xkeys = NULL; + + (void) cmd; + if (NULL == ges->exchange_url) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL != ges->last_keys_ref) + { + const struct TALER_TESTING_Command *state_cmd; + struct TALER_EXCHANGE_Keys *old_keys; + const char *exchange_url; + json_t *s_keys; + + state_cmd + = TALER_TESTING_interpreter_lookup_command (is, + ges->last_keys_ref); + if (NULL == state_cmd) + { + /* Command providing serialized keys not found. */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_keys (state_cmd, + &old_keys)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL == old_keys) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_exchange_url (state_cmd, + &exchange_url)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (0 != strcmp (exchange_url, + ges->exchange_url)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + s_keys = TALER_EXCHANGE_keys_to_json (old_keys); + if (NULL == s_keys) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + xkeys = TALER_EXCHANGE_keys_from_json (s_keys); + if (NULL == xkeys) + { + GNUNET_break (0); + json_dumpf (s_keys, + stderr, + JSON_INDENT (2)); + json_decref (s_keys); + TALER_TESTING_interpreter_fail (is); + return; + } + json_decref (s_keys); + } + if (NULL != ges->master_priv_file) + { + if (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_key_from_file (ges->master_priv_file, + GNUNET_YES, + &ges->master_priv.eddsa_priv)) + { + GNUNET_break (0); + TALER_EXCHANGE_keys_decref (xkeys); + TALER_TESTING_interpreter_fail (is); + return; + } + } + ges->is = is; + ges->exchange + = TALER_EXCHANGE_get_keys (TALER_TESTING_interpreter_get_context (is), + ges->exchange_url, + xkeys, + &cert_cb, + ges); + TALER_EXCHANGE_keys_decref (xkeys); + if (NULL == ges->exchange) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (! ges->wait_for_keys) + TALER_TESTING_interpreter_next (is); +} + + +/** + * Cleanup the state. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +get_exchange_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetExchangeState *ges = cls; + + if (NULL != ges->exchange) + { + TALER_EXCHANGE_get_keys_cancel (ges->exchange); + ges->exchange = NULL; + } + TALER_EXCHANGE_keys_decref (ges->keys); + ges->keys = NULL; + GNUNET_free (ges->master_priv_file); + GNUNET_free (ges->exchange_url); + GNUNET_free (ges); +} + + +/** + * Offer internal data to a "get_exchange" CMD state to other commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +get_exchange_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct GetExchangeState *ges = cls; + unsigned int off = (NULL == ges->master_priv_file) ? 1 : 0; + + if (NULL != ges->keys) + { + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_master_priv (&ges->master_priv), + TALER_TESTING_make_trait_master_pub (&ges->keys->master_pub), + TALER_TESTING_make_trait_keys (ges->keys), + TALER_TESTING_make_trait_exchange_url (ges->exchange_url), + TALER_TESTING_make_trait_timestamp (0, + &ges->my_denom_date), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (&traits[off], + ret, + trait, + index); + } + else + { + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_master_priv (&ges->master_priv), + TALER_TESTING_make_trait_exchange_url (ges->exchange_url), + TALER_TESTING_make_trait_timestamp (0, + &ges->my_denom_date), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (&traits[off], + ret, + trait, + index); + } +} + + +/** + * Get the base URL of the exchange from @a cfg. + * + * @param cfg configuration to evaluate + * @return base URL of the exchange according to @a cfg + */ +static char * +get_exchange_base_url ( + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *exchange_url; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "BASE_URL", + &exchange_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL"); + return NULL; + } + return exchange_url; +} + + +/** + * Get the file name of the master private key file of the exchange from @a + * cfg. + * + * @param cfg configuration to evaluate + * @return base URL of the exchange according to @a cfg + */ +static char * +get_exchange_master_priv_file ( + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *fn; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "exchange-offline", + "MASTER_PRIV_FILE", + &fn)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange-offline", + "MASTER_PRIV_FILE"); + return NULL; + } + return fn; +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_get_exchange ( + const char *label, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *last_keys_ref, + bool wait_for_keys, + bool load_private_key) +{ + struct GetExchangeState *ges; + + ges = GNUNET_new (struct GetExchangeState); + ges->exchange_url = get_exchange_base_url (cfg); + ges->last_keys_ref = last_keys_ref; + if (load_private_key) + ges->master_priv_file = get_exchange_master_priv_file (cfg); + ges->wait_for_keys = wait_for_keys; + { + struct TALER_TESTING_Command cmd = { + .cls = ges, + .label = label, + .run = &get_exchange_run, + .cleanup = &get_exchange_cleanup, + .traits = &get_exchange_traits, + .name = "exchange" + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_insert_deposit.c b/src/testing/testing_api_cmd_insert_deposit.c index 0a0d5db2f..03e704c72 100644 --- a/src/testing/testing_api_cmd_insert_deposit.c +++ b/src/testing/testing_api_cmd_insert_deposit.c @@ -29,6 +29,7 @@ #include "taler_signatures.h" #include "taler_testing_lib.h" #include "taler_exchangedb_plugin.h" +#include "taler_exchangedb_lib.h" /** @@ -37,9 +38,9 @@ struct InsertDepositState { /** - * Configuration file used by the command. + * Database connection we use. */ - const struct TALER_TESTING_DatabaseConnection *dbc; + struct TALER_EXCHANGEDB_Plugin *plugin; /** * Human-readable name of the shop. @@ -60,7 +61,7 @@ struct InsertDepositState /** * When did the exchange receive the deposit? */ - struct GNUNET_TIME_Absolute exchange_timestamp; + struct GNUNET_TIME_Timestamp exchange_timestamp; /** * Amount to deposit, inclusive of deposit fee. @@ -71,6 +72,11 @@ struct InsertDepositState * Deposit fee. */ const char *deposit_fee; + + /** + * Do we used a cached @e plugin? + */ + bool cached; }; /** @@ -79,44 +85,37 @@ struct InsertDepositState * @param[out] issue information to initialize with "valid" data */ static void -fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue) +fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) { - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp now; memset (issue, 0, - sizeof (struct TALER_EXCHANGEDB_DenominationKeyInformationP)); - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); - issue->properties.start - = GNUNET_TIME_absolute_hton (now); - issue->properties.expire_withdraw - = GNUNET_TIME_absolute_hton ( - GNUNET_TIME_absolute_add (now, - GNUNET_TIME_UNIT_MINUTES)); - issue->properties.expire_deposit - = GNUNET_TIME_absolute_hton ( - GNUNET_TIME_absolute_add (now, - GNUNET_TIME_UNIT_HOURS)); - issue->properties.expire_legal - = GNUNET_TIME_absolute_hton ( - GNUNET_TIME_absolute_add (now, - GNUNET_TIME_UNIT_DAYS)); + sizeof (*issue)); + now = GNUNET_TIME_timestamp_get (); + issue->start + = now; + issue->expire_withdraw + = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MINUTES); + issue->expire_deposit + = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS); + issue->expire_legal + = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_DAYS); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount_nbo ("EUR:1", - &issue->properties.value)); + TALER_string_to_amount ("EUR:1", + &issue->value)); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount_nbo ("EUR:0.1", - &issue->properties.fee_withdraw)); + TALER_string_to_amount ("EUR:0.1", + &issue->fees.withdraw)); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount_nbo ("EUR:0.1", - &issue->properties.fee_deposit)); + TALER_string_to_amount ("EUR:0.1", + &issue->fees.deposit)); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount_nbo ("EUR:0.1", - &issue->properties.fee_refresh)); + TALER_string_to_amount ("EUR:0.1", + &issue->fees.refresh)); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount_nbo ("EUR:0.1", - &issue->properties.fee_refund)); + TALER_string_to_amount ("EUR:0.1", + &issue->fees.refund)); } @@ -133,31 +132,50 @@ insert_deposit_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct InsertDepositState *ids = cls; - struct TALER_EXCHANGEDB_Deposit deposit; + struct TALER_EXCHANGEDB_CoinDepositInformation deposit; + struct TALER_EXCHANGEDB_BatchDeposit bd; struct TALER_MerchantPrivateKeyP merchant_priv; - struct TALER_EXCHANGEDB_DenominationKeyInformationP issue; + struct TALER_EXCHANGEDB_DenominationKeyInformation issue; struct TALER_DenominationPublicKey dpk; - struct GNUNET_CRYPTO_RsaPrivateKey *denom_priv; - struct GNUNET_HashCode hc; + struct TALER_DenominationPrivateKey denom_priv; + char *receiver_wire_account; - // prepare and store issue first. + (void) cmd; + if (NULL == ids->plugin) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + ids->plugin->preflight (ids->plugin->cls)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } fake_issue (&issue); - denom_priv = GNUNET_CRYPTO_rsa_private_key_create (1024); - dpk.rsa_public_key = GNUNET_CRYPTO_rsa_private_key_get_public (denom_priv); - GNUNET_CRYPTO_rsa_public_key_hash (dpk.rsa_public_key, - &issue.properties.denom_hash); + GNUNET_assert (GNUNET_OK == + TALER_denom_priv_create (&denom_priv, + &dpk, + GNUNET_CRYPTO_BSA_RSA, + 1024)); + TALER_denom_pub_hash (&dpk, + &issue.denom_hash); if ( (GNUNET_OK != - ids->dbc->plugin->start (ids->dbc->plugin->cls, - "talertestinglib: denomination insertion")) || + ids->plugin->start (ids->plugin->cls, + "talertestinglib: denomination insertion")) || (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - ids->dbc->plugin->insert_denomination_info (ids->dbc->plugin->cls, - &dpk, - &issue)) || + ids->plugin->insert_denomination_info (ids->plugin->cls, + &dpk, + &issue)) || (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - ids->dbc->plugin->commit (ids->dbc->plugin->cls)) ) + ids->plugin->commit (ids->plugin->cls)) ) { TALER_TESTING_interpreter_fail (is); + TALER_denom_pub_free (&dpk); + TALER_denom_priv_free (&denom_priv); return; } @@ -165,91 +183,132 @@ insert_deposit_run (void *cls, memset (&deposit, 0, sizeof (deposit)); - - GNUNET_CRYPTO_kdf (&merchant_priv, - sizeof (struct TALER_MerchantPrivateKeyP), - "merchant-priv", - strlen ("merchant-priv"), - ids->merchant_name, - strlen (ids->merchant_name), - NULL, - 0); + memset (&bd, + 0, + sizeof (bd)); + bd.cdis = &deposit; + bd.num_cdis = 1; + + GNUNET_assert ( + GNUNET_YES == + GNUNET_CRYPTO_kdf (&merchant_priv, + sizeof (struct TALER_MerchantPrivateKeyP), + "merchant-priv", + strlen ("merchant-priv"), + ids->merchant_name, + strlen (ids->merchant_name), + NULL, + 0)); GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv, - &deposit.merchant_pub.eddsa_pub); + &bd.merchant_pub.eddsa_pub); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, - &deposit.h_contract_terms); - if ( (GNUNET_OK != - TALER_string_to_amount (ids->amount_with_fee, - &deposit.amount_with_fee)) || - (GNUNET_OK != - TALER_string_to_amount (ids->deposit_fee, - &deposit.deposit_fee)) ) + &bd.h_contract_terms.hash); + if (GNUNET_OK != + TALER_string_to_amount (ids->amount_with_fee, + &deposit.amount_with_fee)) { TALER_TESTING_interpreter_fail (is); + TALER_denom_pub_free (&dpk); + TALER_denom_priv_free (&denom_priv); return; } - GNUNET_CRYPTO_rsa_public_key_hash (dpk.rsa_public_key, - &deposit.coin.denom_pub_hash); + TALER_denom_pub_hash (&dpk, + &deposit.coin.denom_pub_hash); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &deposit.coin.coin_pub, sizeof (deposit.coin.coin_pub)); - GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, - &hc); - deposit.coin.denom_sig.rsa_signature = GNUNET_CRYPTO_rsa_sign_fdh (denom_priv, - &hc); { - char *str; - struct TALER_WireSalt salt; - - GNUNET_asprintf (&str, - "payto://x-taler-bank/localhost/%s", - ids->merchant_account); - memset (&salt, - 46, - sizeof (salt)); - deposit.receiver_wire_account - = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("salt", - &salt), - GNUNET_JSON_pack_string ("payto_uri", - str)); - GNUNET_free (str); + struct TALER_CoinPubHashP c_hash; + struct TALER_PlanchetDetail pd; + struct TALER_BlindedDenominationSignature bds; + struct TALER_PlanchetMasterSecretP ps; + union GNUNET_CRYPTO_BlindingSecretP bks; + const struct TALER_ExchangeWithdrawValues *alg_values; + + alg_values = TALER_denom_ewv_rsa_singleton (); + TALER_planchet_blinding_secret_create (&ps, + alg_values, + &bks); + GNUNET_assert (GNUNET_OK == + TALER_denom_blind (&dpk, + &bks, + NULL, /* no age restriction active */ + NULL, /* no nonce needed */ + &deposit.coin.coin_pub, + alg_values, + &c_hash, + &pd.blinded_planchet)); + GNUNET_assert (GNUNET_OK == + TALER_denom_sign_blinded (&bds, + &denom_priv, + false, + &pd.blinded_planchet)); + TALER_blinded_planchet_free (&pd.blinded_planchet); + GNUNET_assert (GNUNET_OK == + TALER_denom_sig_unblind (&deposit.coin.denom_sig, + &bds, + &bks, + &c_hash, + alg_values, + &dpk)); + TALER_blinded_denom_sig_free (&bds); } - - GNUNET_assert (GNUNET_OK == - TALER_JSON_merchant_wire_signature_hash ( - deposit.receiver_wire_account, - &deposit.h_wire)); - deposit.timestamp = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&deposit.timestamp); - deposit.wire_deadline = GNUNET_TIME_relative_to_absolute (ids->wire_deadline); - (void) GNUNET_TIME_round_abs (&deposit.wire_deadline); - + GNUNET_asprintf (&receiver_wire_account, + "payto://x-taler-bank/localhost/%s?receiver-name=%s", + ids->merchant_account, + ids->merchant_account); + bd.receiver_wire_account = receiver_wire_account; + TALER_payto_hash (bd.receiver_wire_account, + &bd.wire_target_h_payto); + memset (&bd.wire_salt, + 46, + sizeof (bd.wire_salt)); + bd.wallet_timestamp = GNUNET_TIME_timestamp_get (); + bd.wire_deadline = GNUNET_TIME_relative_to_timestamp ( + ids->wire_deadline); /* finally, actually perform the DB operation */ - if ( (GNUNET_OK != - ids->dbc->plugin->start (ids->dbc->plugin->cls, - "libtalertesting: insert deposit")) || - (0 > - ids->dbc->plugin->ensure_coin_known (ids->dbc->plugin->cls, - &deposit.coin)) || - (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - ids->dbc->plugin->insert_deposit (ids->dbc->plugin->cls, - ids->exchange_timestamp, - &deposit)) || - (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - ids->dbc->plugin->commit (ids->dbc->plugin->cls)) ) { - GNUNET_break (0); - ids->dbc->plugin->rollback (ids->dbc->plugin->cls); - TALER_TESTING_interpreter_fail (is); + uint64_t known_coin_id; + struct TALER_DenominationHashP dph; + struct TALER_AgeCommitmentHash agh; + bool balance_ok; + uint32_t bad_index; + bool ctr_conflict; + + if ( (GNUNET_OK != + ids->plugin->start (ids->plugin->cls, + "libtalertesting: insert deposit")) || + (0 > + ids->plugin->ensure_coin_known (ids->plugin->cls, + &deposit.coin, + &known_coin_id, + &dph, + &agh)) || + (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + ids->plugin->do_deposit (ids->plugin->cls, + &bd, + &ids->exchange_timestamp, + &balance_ok, + &bad_index, + &ctr_conflict)) || + (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + ids->plugin->commit (ids->plugin->cls)) ) + { + GNUNET_break (0); + ids->plugin->rollback (ids->plugin->cls); + GNUNET_free (receiver_wire_account); + TALER_denom_pub_free (&dpk); + TALER_denom_priv_free (&denom_priv); + TALER_TESTING_interpreter_fail (is); + return; + } } - GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig.rsa_signature); - GNUNET_CRYPTO_rsa_public_key_free (dpk.rsa_public_key); - GNUNET_CRYPTO_rsa_private_key_free (denom_priv); - json_decref (deposit.receiver_wire_account); - + TALER_denom_sig_free (&deposit.coin.denom_sig); + TALER_denom_pub_free (&dpk); + TALER_denom_priv_free (&denom_priv); + GNUNET_free (receiver_wire_account); TALER_TESTING_interpreter_next (is); } @@ -267,63 +326,46 @@ insert_deposit_cleanup (void *cls, { struct InsertDepositState *ids = cls; + (void) cmd; + if ( (NULL != ids->plugin) && + (! ids->cached) ) + { + // FIXME: historically, we also did: + // ids->plugin->drop_tables (ids->plugin->cls); + TALER_EXCHANGEDB_plugin_unload (ids->plugin); + ids->plugin = NULL; + } GNUNET_free (ids); } -/** - * Offer "insert-deposit" CMD internal data to other commands. - * - * @param cls closure. - * @param[out] ret result - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -insert_deposit_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - (void) cls; - (void) ret; - (void) trait; - (void) index; - return GNUNET_NO; -} - - -/** - * Make the "insert-deposit" CMD. - * - * @param label command label. - * @param dbc collects database plugin - * @param merchant_name Human-readable name of the merchant. - * @param merchant_account merchant's account name (NOT a payto:// URI) - * @param exchange_timestamp when did the exchange receive the deposit - * @param wire_deadline point in time where the aggregator should have - * wired money to the merchant. - * @param amount_with_fee amount to deposit (inclusive of deposit fee) - * @param deposit_fee deposit fee - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_insert_deposit ( const char *label, - const struct TALER_TESTING_DatabaseConnection *dbc, + const struct GNUNET_CONFIGURATION_Handle *db_cfg, const char *merchant_name, const char *merchant_account, - struct GNUNET_TIME_Absolute exchange_timestamp, + struct GNUNET_TIME_Timestamp exchange_timestamp, struct GNUNET_TIME_Relative wire_deadline, const char *amount_with_fee, const char *deposit_fee) { + static struct TALER_EXCHANGEDB_Plugin *pluginc; + static const struct GNUNET_CONFIGURATION_Handle *db_cfgc; struct InsertDepositState *ids; - GNUNET_TIME_round_abs (&exchange_timestamp); ids = GNUNET_new (struct InsertDepositState); - ids->dbc = dbc; + if (db_cfgc == db_cfg) + { + ids->plugin = pluginc; + ids->cached = true; + } + else + { + ids->plugin = TALER_EXCHANGEDB_plugin_load (db_cfg); + pluginc = ids->plugin; + db_cfgc = db_cfg; + } ids->merchant_name = merchant_name; ids->merchant_account = merchant_account; ids->exchange_timestamp = exchange_timestamp; @@ -336,8 +378,7 @@ TALER_TESTING_cmd_insert_deposit ( .cls = ids, .label = label, .run = &insert_deposit_run, - .cleanup = &insert_deposit_cleanup, - .traits = &insert_deposit_traits + .cleanup = &insert_deposit_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_kyc_check_get.c b/src/testing/testing_api_cmd_kyc_check_get.c new file mode 100644 index 000000000..25c7e98b8 --- /dev/null +++ b/src/testing/testing_api_cmd_kyc_check_get.c @@ -0,0 +1,243 @@ +/* + This file is part of TALER + Copyright (C) 2021-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file testing/testing_api_cmd_kyc_check_get.c + * @brief Implement the testing CMDs for the /kyc_check/ GET operations. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + +/** + * State for a "track transaction" CMD. + */ +struct KycCheckGetState +{ + + /** + * Command to get a reserve private key from. + */ + const char *payment_target_reference; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Set to the KYC URL *if* the exchange replied with + * a request for KYC (#MHD_HTTP_ACCEPTED). + */ + char *kyc_url; + + /** + * Handle to the "track transaction" pending operation. + */ + struct TALER_EXCHANGE_KycCheckHandle *kwh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Handle response to the command. + * + * @param cls closure. + * @param ks GET KYC status response details + */ +static void +check_kyc_cb (void *cls, + const struct TALER_EXCHANGE_KycStatus *ks) +{ + struct KycCheckGetState *kcg = cls; + struct TALER_TESTING_Interpreter *is = kcg->is; + + kcg->kwh = NULL; + if (kcg->expected_response_code != ks->http_status) + { + TALER_TESTING_unexpected_status (is, + ks->http_status, + kcg->expected_response_code); + return; + } + switch (ks->http_status) + { + case MHD_HTTP_OK: + break; + case MHD_HTTP_ACCEPTED: + kcg->kyc_url = GNUNET_strdup (ks->details.accepted.kyc_url); + break; + case MHD_HTTP_NO_CONTENT: + break; + default: + GNUNET_break (0); + break; + } + TALER_TESTING_interpreter_next (kcg->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +check_kyc_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct KycCheckGetState *kcg = cls; + const struct TALER_TESTING_Command *res_cmd; + const uint64_t *requirement_row; + const struct TALER_PaytoHashP *h_payto; + + (void) cmd; + kcg->is = is; + res_cmd = TALER_TESTING_interpreter_lookup_command ( + kcg->is, + kcg->payment_target_reference); + if (NULL == res_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kcg->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_legi_requirement_row (res_cmd, + &requirement_row)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kcg->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_h_payto (res_cmd, + &h_payto)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kcg->is); + return; + } + if (0 == *requirement_row) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kcg->is); + return; + } + kcg->kwh = TALER_EXCHANGE_kyc_check ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + *requirement_row, + h_payto, + TALER_KYCLOGIC_KYC_UT_INDIVIDUAL, + GNUNET_TIME_UNIT_SECONDS, + &check_kyc_cb, + kcg); + GNUNET_assert (NULL != kcg->kwh); +} + + +/** + * Cleanup the state from a "track transaction" CMD, and possibly + * cancel a operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +check_kyc_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct KycCheckGetState *kcg = cls; + + if (NULL != kcg->kwh) + { + TALER_TESTING_command_incomplete (kcg->is, + cmd->label); + TALER_EXCHANGE_kyc_check_cancel (kcg->kwh); + kcg->kwh = NULL; + } + GNUNET_free (kcg->kyc_url); + GNUNET_free (kcg); +} + + +/** + * Offer internal data from a "check KYC" CMD. + * + * @param cls closure. + * @param[out] ret result (could be anything). + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +check_kyc_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct KycCheckGetState *kcg = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_kyc_url (kcg->kyc_url), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_kyc_get (const char *label, + const char *payment_target_reference, + unsigned int expected_response_code) +{ + struct KycCheckGetState *kcg; + + kcg = GNUNET_new (struct KycCheckGetState); + kcg->payment_target_reference = payment_target_reference; + kcg->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = kcg, + .label = label, + .run = &check_kyc_run, + .cleanup = &check_kyc_cleanup, + .traits = &check_kyc_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_kyc_check_get.c */ diff --git a/src/testing/testing_api_cmd_kyc_proof.c b/src/testing/testing_api_cmd_kyc_proof.c new file mode 100644 index 000000000..b079fffce --- /dev/null +++ b/src/testing/testing_api_cmd_kyc_proof.c @@ -0,0 +1,259 @@ +/* + This file is part of TALER + Copyright (C) 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file testing/testing_api_cmd_kyc_proof.c + * @brief Implement the testing CMDs for the /kyc-proof/ operation. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + +/** + * State for a "track transaction" CMD. + */ +struct KycProofGetState +{ + + /** + * Command to get a reserve private key from. + */ + const char *payment_target_reference; + + /** + * Code to pass. + */ + const char *code; + + /** + * Logic section name to pass to `/kyc-proof/` handler. + */ + const char *logic; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Set to the KYC REDIRECT *if* the exchange replied with + * success (#MHD_HTTP_OK). + */ + char *redirect_url; + + /** + * Handle to the "track transaction" pending operation. + */ + struct TALER_EXCHANGE_KycProofHandle *kph; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Handle response to the command. + * + * @param cls closure. + * @param kpr KYC proof response details + */ +static void +proof_kyc_cb (void *cls, + const struct TALER_EXCHANGE_KycProofResponse *kpr) +{ + struct KycProofGetState *kcg = cls; + struct TALER_TESTING_Interpreter *is = kcg->is; + + kcg->kph = NULL; + if (kcg->expected_response_code != kpr->http_status) + { + TALER_TESTING_unexpected_status (is, + kpr->http_status, + kcg->expected_response_code); + return; + } + switch (kpr->http_status) + { + case MHD_HTTP_SEE_OTHER: + kcg->redirect_url = GNUNET_strdup (kpr->details.found.redirect_url); + break; + case MHD_HTTP_FORBIDDEN: + break; + case MHD_HTTP_BAD_GATEWAY: + break; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u to /kyc-proof\n", + kpr->http_status); + break; + } + TALER_TESTING_interpreter_next (kcg->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +proof_kyc_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct KycProofGetState *kps = cls; + const struct TALER_TESTING_Command *res_cmd; + const struct TALER_PaytoHashP *h_payto; + char *uargs; + const char *exchange_url; + + (void) cmd; + kps->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + res_cmd = TALER_TESTING_interpreter_lookup_command ( + kps->is, + kps->payment_target_reference); + if (NULL == res_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kps->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_h_payto (res_cmd, + &h_payto)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kps->is); + return; + } + if (NULL == kps->code) + uargs = NULL; + else + GNUNET_asprintf (&uargs, + "&code=%s", + kps->code); + kps->kph = TALER_EXCHANGE_kyc_proof ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + h_payto, + kps->logic, + uargs, + &proof_kyc_cb, + kps); + GNUNET_free (uargs); + GNUNET_assert (NULL != kps->kph); +} + + +/** + * Cleanup the state from a "track transaction" CMD, and possibly + * cancel a operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +proof_kyc_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct KycProofGetState *kps = cls; + + if (NULL != kps->kph) + { + TALER_TESTING_command_incomplete (kps->is, + cmd->label); + TALER_EXCHANGE_kyc_proof_cancel (kps->kph); + kps->kph = NULL; + } + GNUNET_free (kps->redirect_url); + GNUNET_free (kps); +} + + +/** + * Offer internal data from a "proof KYC" CMD. + * + * @param cls closure. + * @param[out] ret result (could be anything). + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +proof_kyc_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct KycProofGetState *kps = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_web_url (kps->redirect_url), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_proof_kyc_oauth2 ( + const char *label, + const char *payment_target_reference, + const char *logic_section, + const char *code, + unsigned int expected_response_code) +{ + struct KycProofGetState *kps; + + kps = GNUNET_new (struct KycProofGetState); + kps->code = code; + kps->logic = logic_section; + kps->payment_target_reference = payment_target_reference; + kps->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = kps, + .label = label, + .run = &proof_kyc_run, + .cleanup = &proof_kyc_cleanup, + .traits = &proof_kyc_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_kyc_proof.c */ diff --git a/src/testing/testing_api_cmd_kyc_wallet_get.c b/src/testing/testing_api_cmd_kyc_wallet_get.c new file mode 100644 index 000000000..ffb143ffb --- /dev/null +++ b/src/testing/testing_api_cmd_kyc_wallet_get.c @@ -0,0 +1,290 @@ +/* + This file is part of TALER + Copyright (C) 2021 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file testing/testing_api_cmd_kyc_wallet_get.c + * @brief Implement the testing CMDs for the /kyc_wallet/ GET operations. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + +/** + * State for a "/kyc-wallet" GET CMD. + */ +struct KycWalletGetState +{ + + /** + * Private key of the reserve (account). + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Public key of the reserve (account). + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Payto URI of the reserve of the wallet. + */ + char *reserve_payto_uri; + + /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** + * Command to get a reserve private key from. + */ + const char *reserve_reference; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Set to the KYC requirement payto hash *if* the exchange replied with a + * request for KYC (#MHD_HTTP_OK). + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC (#MHD_HTTP_OK). + */ + uint64_t requirement_row; + + /** + * Handle to the "track transaction" pending operation. + */ + struct TALER_EXCHANGE_KycWalletHandle *kwh; + + /** + * Balance to pass to the exchange. + */ + struct TALER_Amount balance; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Handle response to the command. + * + * @param cls closure. + * @param wkr GET deposit response details + */ +static void +wallet_kyc_cb (void *cls, + const struct TALER_EXCHANGE_WalletKycResponse *wkr) +{ + struct KycWalletGetState *kwg = cls; + struct TALER_TESTING_Interpreter *is = kwg->is; + + kwg->kwh = NULL; + if (kwg->expected_response_code != wkr->http_status) + { + TALER_TESTING_unexpected_status (is, + wkr->http_status, + kwg->expected_response_code); + return; + } + switch (wkr->http_status) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_FORBIDDEN: + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + kwg->requirement_row + = wkr->details.unavailable_for_legal_reasons.requirement_row; + kwg->h_payto + = wkr->details.unavailable_for_legal_reasons.h_payto; + break; + default: + GNUNET_break (0); + break; + } + TALER_TESTING_interpreter_next (kwg->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +wallet_kyc_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct KycWalletGetState *kwg = cls; + const char *exchange_url; + + kwg->cmd = cmd; + kwg->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + if (NULL != kwg->reserve_reference) + { + const struct TALER_TESTING_Command *res_cmd; + const struct TALER_ReservePrivateKeyP *reserve_priv; + + res_cmd = TALER_TESTING_interpreter_lookup_command (kwg->is, + kwg->reserve_reference); + if (NULL == res_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kwg->is); + return; + } + + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (res_cmd, + &reserve_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (kwg->is); + return; + } + kwg->reserve_priv = *reserve_priv; + } + else + { + GNUNET_CRYPTO_eddsa_key_create (&kwg->reserve_priv.eddsa_priv); + } + GNUNET_CRYPTO_eddsa_key_get_public (&kwg->reserve_priv.eddsa_priv, + &kwg->reserve_pub.eddsa_pub); + kwg->reserve_payto_uri + = TALER_reserve_make_payto (exchange_url, + &kwg->reserve_pub); + kwg->kwh = TALER_EXCHANGE_kyc_wallet ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + &kwg->reserve_priv, + &kwg->balance, + &wallet_kyc_cb, + kwg); + GNUNET_assert (NULL != kwg->kwh); +} + + +/** + * Cleanup the state from a "track transaction" CMD, and possibly + * cancel a operation thereof. + * + * @param cls closure with our `struct KycWalletGetState` + * @param cmd the command which is being cleaned up. + */ +static void +wallet_kyc_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct KycWalletGetState *kwg = cls; + + if (NULL != kwg->kwh) + { + TALER_TESTING_command_incomplete (kwg->is, + cmd->label); + TALER_EXCHANGE_kyc_wallet_cancel (kwg->kwh); + kwg->kwh = NULL; + } + GNUNET_free (kwg->reserve_payto_uri); + GNUNET_free (kwg); +} + + +/** + * Offer internal data from a "wallet KYC" CMD. + * + * @param cls closure. + * @param[out] ret result (could be anything). + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +wallet_kyc_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct KycWalletGetState *kwg = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_reserve_priv (&kwg->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (&kwg->reserve_pub), + TALER_TESTING_make_trait_legi_requirement_row (&kwg->requirement_row), + TALER_TESTING_make_trait_h_payto (&kwg->h_payto), + TALER_TESTING_make_trait_payto_uri (kwg->reserve_payto_uri), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_wallet_kyc_get (const char *label, + const char *reserve_reference, + const char *threshold_balance, + unsigned int expected_response_code) +{ + struct KycWalletGetState *kwg; + + kwg = GNUNET_new (struct KycWalletGetState); + kwg->reserve_reference = reserve_reference; + kwg->expected_response_code = expected_response_code; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (threshold_balance, + &kwg->balance)); + { + struct TALER_TESTING_Command cmd = { + .cls = kwg, + .label = label, + .run = &wallet_kyc_run, + .cleanup = &wallet_kyc_cleanup, + .traits = &wallet_kyc_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_kyc_wallet_get.c */ diff --git a/src/testing/testing_api_cmd_oauth.c b/src/testing/testing_api_cmd_oauth.c new file mode 100644 index 000000000..80d38e4c8 --- /dev/null +++ b/src/testing/testing_api_cmd_oauth.c @@ -0,0 +1,412 @@ +/* + This file is part of TALER + Copyright (C) 2021-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file testing/testing_api_cmd_oauth.c + * @brief Implement a CMD to run an OAuth service for faking the legitimation service + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_mhd_lib.h" + +/** + * State for the oauth CMD. + */ +struct OAuthState +{ + + /** + * Handle to the "oauth" service. + */ + struct MHD_Daemon *mhd; + + /** + * Birthdate that the oauth server should return in a response, may be NULL + */ + const char *birthdate; + + /** + * Port to listen on. + */ + uint16_t port; +}; + + +struct RequestCtx +{ + struct MHD_PostProcessor *pp; + char *code; + char *client_id; + char *redirect_uri; + char *client_secret; +}; + + +static void +append (char **target, + const char *data, + size_t size) +{ + char *tmp; + + if (NULL == *target) + { + *target = GNUNET_strndup (data, + size); + return; + } + GNUNET_asprintf (&tmp, + "%s%.*s", + *target, + (int) size, + data); + GNUNET_free (*target); + *target = tmp; +} + + +static MHD_RESULT +handle_post (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, + uint64_t off, + size_t size) +{ + struct RequestCtx *rc = cls; + + (void) kind; + (void) filename; + (void) content_type; + (void) transfer_encoding; + (void) off; + if (0 == strcmp (key, + "code")) + append (&rc->code, + data, + size); + if (0 == strcmp (key, + "client_id")) + append (&rc->client_id, + data, + size); + if (0 == strcmp (key, + "redirect_uri")) + append (&rc->redirect_uri, + data, + size); + if (0 == strcmp (key, + "client_secret")) + append (&rc->client_secret, + data, + size); + return MHD_YES; +} + + +/** + * A client has requested the given url using the given method + * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, + * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback + * must call MHD callbacks to provide content to give back to the + * client and return an HTTP status code (i.e. #MHD_HTTP_OK, + * #MHD_HTTP_NOT_FOUND, etc.). + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param connection the connection being handled + * @param url the requested url + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param version the HTTP version string (i.e. + * MHD_HTTP_VERSION_1_1) + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of MHD_get_connection_values(); very large POST + * data *will* be made available incrementally in + * @a upload_data) + * @param[in,out] upload_data_size set initially to the size of the + * @a upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param[in,out] con_cls pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global MHD_RequestCompletedCallback (which + * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). + * Initially, `*con_cls` will be NULL. + * @return #MHD_YES if the connection was handled successfully, + * #MHD_NO if the socket must be closed due to a serious + * error while handling the request + */ +static MHD_RESULT +handler_cb (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **con_cls) +{ + struct RequestCtx *rc = *con_cls; + struct OAuthState *oas = cls; + unsigned int hc; + json_t *body; + + (void) version; + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_GET)) + { + json_t *data = + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("id", + "XXXID12345678"), + GNUNET_JSON_pack_string ("first_name", + "Bob"), + GNUNET_JSON_pack_string ("last_name", + "Builder")); + + if (NULL != oas->birthdate) + GNUNET_assert (0 == + json_object_set_new (data, + "birthdate", + json_string_nocheck ( + oas->birthdate))); + + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ( + "status", + "success"), + GNUNET_JSON_pack_object_steal ( + "data", data)); + return TALER_MHD_reply_json_steal (connection, + body, + MHD_HTTP_OK); + } + if (0 != strcasecmp (method, + MHD_HTTP_METHOD_POST)) + { + GNUNET_break (0); + return MHD_NO; + } + if (NULL == rc) + { + rc = GNUNET_new (struct RequestCtx); + *con_cls = rc; + rc->pp = MHD_create_post_processor (connection, + 4092, + &handle_post, + rc); + return MHD_YES; + } + if (0 != *upload_data_size) + { + MHD_RESULT ret; + + ret = MHD_post_process (rc->pp, + upload_data, + *upload_data_size); + *upload_data_size = 0; + return ret; + } + + + /* NOTE: In the future, we MAY want to distinguish between + the different URLs and possibly return more information. + For now, just do the minimum: implement the main handler + that checks the code. */ + if ( (NULL == rc->code) || + (NULL == rc->client_id) || + (NULL == rc->redirect_uri) || + (NULL == rc->client_secret) ) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Bad request to Oauth faker: `%s' with %s/%s/%s/%s\n", + url, + rc->code, + rc->client_id, + rc->redirect_uri, + rc->client_secret); + return MHD_NO; + } + if (0 != strcmp (rc->client_id, + "taler-exchange")) + { + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("error", + "unknown_client"), + GNUNET_JSON_pack_string ("error_description", + "only 'taler-exchange' is allowed")); + hc = MHD_HTTP_NOT_FOUND; + } + else if (0 != strcmp (rc->client_secret, + "exchange-secret")) + { + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("error", + "invalid_client_secret"), + GNUNET_JSON_pack_string ("error_description", + "only 'exchange-secret' is valid")); + hc = MHD_HTTP_FORBIDDEN; + } + else + { + if (0 != strcmp (rc->code, + "pass")) + { + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("error", + "invalid_grant"), + GNUNET_JSON_pack_string ("error_description", + "only 'pass' shall pass")); + hc = MHD_HTTP_FORBIDDEN; + } + else + { + body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("access_token", + "good"), + GNUNET_JSON_pack_string ("token_type", + "bearer"), + GNUNET_JSON_pack_uint64 ("expires_in", + 3600), + GNUNET_JSON_pack_string ("refresh_token", + "better")); + hc = MHD_HTTP_OK; + } + } + return TALER_MHD_reply_json_steal (connection, + body, + hc); +} + + +static void +cleanup (void *cls, + struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + struct RequestCtx *rc = *con_cls; + + (void) cls; + (void) connection; + (void) toe; + if (NULL == rc) + return; + MHD_destroy_post_processor (rc->pp); + GNUNET_free (rc->code); + GNUNET_free (rc->client_id); + GNUNET_free (rc->redirect_uri); + GNUNET_free (rc->client_secret); + GNUNET_free (rc); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +oauth_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct OAuthState *oas = cls; + + (void) cmd; + oas->mhd = MHD_start_daemon (MHD_USE_AUTO_INTERNAL_THREAD | MHD_USE_DEBUG, + oas->port, + NULL, NULL, + &handler_cb, oas, + MHD_OPTION_NOTIFY_COMPLETED, &cleanup, NULL, + NULL); + if (NULL == oas->mhd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Cleanup the state from a "oauth" CMD, and possibly cancel a operation + * thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +oauth_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct OAuthState *oas = cls; + + (void) cmd; + if (NULL != oas->mhd) + { + MHD_stop_daemon (oas->mhd); + oas->mhd = NULL; + } + GNUNET_free (oas); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_oauth_with_birthdate (const char *label, + const char *birthdate, + uint16_t port) +{ + struct OAuthState *oas; + + oas = GNUNET_new (struct OAuthState); + oas->port = port; + oas->birthdate = birthdate; + { + struct TALER_TESTING_Command cmd = { + .cls = oas, + .label = label, + .run = &oauth_run, + .cleanup = &oauth_cleanup, + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_oauth.c */ diff --git a/src/testing/testing_api_cmd_offline_sign_extensions.c b/src/testing/testing_api_cmd_offline_sign_extensions.c new file mode 100644 index 000000000..f39679f97 --- /dev/null +++ b/src/testing/testing_api_cmd_offline_sign_extensions.c @@ -0,0 +1,164 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file testing/testing_api_cmd_offline_sign_extensions.c + * @brief run the taler-exchange-offline command to sign extensions (and therefore activate them) + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "extensionssign" CMD. + */ +struct ExtensionsSignState +{ + + /** + * Process for the "extensionssign" command. + */ + struct GNUNET_OS_Process *extensionssign_proc; + + /** + * Configuration file used by the command. + */ + const char *config_filename; + +}; + + +/** + * Run the command; calls the `taler-exchange-offline' program. + * + * @param cls closure. + * @param cmd the commaind being run. + * @param is interpreter state. + */ +static void +extensionssign_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct ExtensionsSignState *ks = cls; + + ks->extensionssign_proc + = GNUNET_OS_start_process ( + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-offline", + "taler-exchange-offline", + "-c", ks->config_filename, + "-L", "INFO", + "extensions", + "sign", + "upload", + NULL); + if (NULL == ks->extensionssign_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "extensionssign" CMD, and possibly kills its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +extensionssign_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct ExtensionsSignState *ks = cls; + + (void) cmd; + if (NULL != ks->extensionssign_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ks->extensionssign_proc, + SIGKILL)); + GNUNET_OS_process_wait (ks->extensionssign_proc); + GNUNET_OS_process_destroy (ks->extensionssign_proc); + ks->extensionssign_proc = NULL; + } + GNUNET_free (ks); +} + + +/** + * Offer "extensionssign" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +extensionssign_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct ExtensionsSignState *ks = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ks->extensionssign_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_offline_sign_extensions (const char *label, + const char *config_filename) +{ + struct ExtensionsSignState *ks; + + ks = GNUNET_new (struct ExtensionsSignState); + ks->config_filename = config_filename; + { + struct TALER_TESTING_Command cmd = { + .cls = ks, + .label = label, + .run = &extensionssign_run, + .cleanup = &extensionssign_cleanup, + .traits = &extensionssign_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_offline_sign_extensions.c */ diff --git a/src/testing/testing_api_cmd_offline_sign_global_fees.c b/src/testing/testing_api_cmd_offline_sign_global_fees.c new file mode 100644 index 000000000..db3916258 --- /dev/null +++ b/src/testing/testing_api_cmd_offline_sign_global_fees.c @@ -0,0 +1,230 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file testing/testing_api_cmd_offline_sign_global_fees.c + * @brief run the taler-exchange-offline command to download, sign and upload global fees + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "offlinesign" CMD. + */ +struct OfflineSignState +{ + + /** + * Process for the "offlinesign" command. + */ + struct GNUNET_OS_Process *offlinesign_proc; + + /** + * Configuration file used by the command. + */ + const char *config_filename; + + /** + * The history fee to sign. + */ + const char *history_fee_s; + + /** + * The account fee to sign. + */ + const char *account_fee_s; + + /** + * The purse fee to sign. + */ + const char *purse_fee_s; + + /** + * When MUST purses time out? + */ + struct GNUNET_TIME_Relative purse_timeout; + + /** + * How long do we keep the history? + */ + struct GNUNET_TIME_Relative history_expiration; + + /** + * Number of (free) purses per account. + */ + unsigned int num_purses; +}; + + +/** + * Run the command; calls the `taler-exchange-offline' program. + * + * @param cls closure. + * @param cmd the commaind being run. + * @param is interpreter state. + */ +static void +offlinesign_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct OfflineSignState *ks = cls; + char num_purses[12]; + char history_expiration[32]; + char purse_timeout[32]; + + GNUNET_snprintf (num_purses, + sizeof (num_purses), + "%u", + ks->num_purses); + GNUNET_snprintf (history_expiration, + sizeof (history_expiration), + "%s", + GNUNET_TIME_relative2s (ks->history_expiration, + false)); + GNUNET_snprintf (purse_timeout, + sizeof (purse_timeout), + "%s", + GNUNET_TIME_relative2s (ks->purse_timeout, + false)); + ks->offlinesign_proc + = GNUNET_OS_start_process ( + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-exchange-offline", + "taler-exchange-offline", + "-c", ks->config_filename, + "-L", "INFO", + "global-fee", + "now", + ks->history_fee_s, + ks->account_fee_s, + ks->purse_fee_s, + purse_timeout, + history_expiration, + num_purses, + "upload", + NULL); + if (NULL == ks->offlinesign_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Free the state of a "offlinesign" CMD, and possibly kills its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +offlinesign_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct OfflineSignState *ks = cls; + + (void) cmd; + if (NULL != ks->offlinesign_proc) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ks->offlinesign_proc, + SIGKILL)); + GNUNET_OS_process_wait (ks->offlinesign_proc); + GNUNET_OS_process_destroy (ks->offlinesign_proc); + ks->offlinesign_proc = NULL; + } + GNUNET_free (ks); +} + + +/** + * Offer "offlinesign" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +offlinesign_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct OfflineSignState *ks = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&ks->offlinesign_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_offline_sign_global_fees ( + const char *label, + const char *config_filename, + const char *history_fee, + const char *account_fee, + const char *purse_fee, + struct GNUNET_TIME_Relative purse_timeout, + struct GNUNET_TIME_Relative history_expiration, + unsigned int num_purses) +{ + struct OfflineSignState *ks; + + ks = GNUNET_new (struct OfflineSignState); + ks->config_filename = config_filename; + ks->history_fee_s = history_fee; + ks->account_fee_s = account_fee; + ks->purse_fee_s = purse_fee; + ks->purse_timeout = purse_timeout; + ks->history_expiration = history_expiration; + ks->num_purses = num_purses; + { + struct TALER_TESTING_Command cmd = { + .cls = ks, + .label = label, + .run = &offlinesign_run, + .cleanup = &offlinesign_cleanup, + .traits = &offlinesign_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_exec_offline_sign_global_fees.c */ diff --git a/src/testing/testing_api_cmd_offline_sign_keys.c b/src/testing/testing_api_cmd_offline_sign_keys.c index dd6170d90..2c99219b6 100644 --- a/src/testing/testing_api_cmd_offline_sign_keys.c +++ b/src/testing/testing_api_cmd_offline_sign_keys.c @@ -121,7 +121,7 @@ offlinesign_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue offlinesign_traits (void *cls, const void **ret, const char *trait, @@ -129,8 +129,7 @@ offlinesign_traits (void *cls, { struct OfflineSignState *ks = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, - &ks->offlinesign_proc), + TALER_TESTING_make_trait_process (&ks->offlinesign_proc), TALER_TESTING_trait_end () }; diff --git a/src/testing/testing_api_cmd_offline_sign_fees.c b/src/testing/testing_api_cmd_offline_sign_wire_fees.c index 500953202..0fccbcd0a 100644 --- a/src/testing/testing_api_cmd_offline_sign_fees.c +++ b/src/testing/testing_api_cmd_offline_sign_wire_fees.c @@ -18,7 +18,7 @@ */ /** - * @file testing/testing_api_cmd_offline_sign_fees.c + * @file testing/testing_api_cmd_offline_sign_wire_fees.c * @brief run the taler-exchange-offline command to download, sign and upload wire fees * @author Marcello Stanisci * @author Christian Grothoff @@ -134,7 +134,7 @@ offlinesign_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue offlinesign_traits (void *cls, const void **ret, const char *trait, @@ -142,8 +142,7 @@ offlinesign_traits (void *cls, { struct OfflineSignState *ks = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, - &ks->offlinesign_proc), + TALER_TESTING_make_trait_process (&ks->offlinesign_proc), TALER_TESTING_trait_end () }; @@ -154,15 +153,6 @@ offlinesign_traits (void *cls, } -/** - * Sign a wire fee. - * - * @param label command label. - * @param config_filename configuration filename. - * @param wire_fee the wire fee to affirm (for the current year) - * @param closing_fee the closing fee to affirm (for the current year) - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_exec_offline_sign_fees (const char *label, const char *config_filename, diff --git a/src/testing/testing_api_cmd_purse_create_deposit.c b/src/testing/testing_api_cmd_purse_create_deposit.c new file mode 100644 index 000000000..4740f9801 --- /dev/null +++ b/src/testing/testing_api_cmd_purse_create_deposit.c @@ -0,0 +1,450 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_purse_create_deposit.c + * @brief command for testing /purses/$PID/create + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + +/** + * Information we keep per deposited coin. + */ +struct Coin +{ + /** + * Reference to the respective command. + */ + char *command_ref; + + /** + * index of the specific coin in the traits of @e command_ref. + */ + unsigned int coin_index; + + /** + * Public key of the deposited coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Amount to deposit (with fee). + */ + struct TALER_Amount deposit_with_fee; + + /** + * Entry in the coin's history generated by this operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che; + +}; + + +/** + * State for a "purse create deposit" CMD. + */ +struct PurseCreateDepositState +{ + + /** + * Total purse target amount without fees. + */ + struct TALER_Amount target_amount; + + /** + * Reference to any command that is able to provide a coin. + */ + struct Coin *coin_references; + + /** + * JSON string describing what a proposal is about. + */ + json_t *contract_terms; + + /** + * Purse expiration time. + */ + struct GNUNET_TIME_Timestamp purse_expiration; + + /** + * Relative purse expiration time. + */ + struct GNUNET_TIME_Relative rel_expiration; + + /** + * Set (by the interpreter) to a fresh private key. This + * key will be used to create the purse. + */ + struct TALER_PurseContractPrivateKeyP purse_priv; + + /** + * Set (by the interpreter) to a fresh private key. This + * key will be used to merge the purse. + */ + struct TALER_PurseMergePrivateKeyP merge_priv; + + /** + * Set (by the interpreter) to a fresh private key. This + * key will be used to decrypt the contract. + */ + struct TALER_ContractDiffiePrivateP contract_priv; + + /** + * Signing key used by the exchange to sign the + * deposit confirmation. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * Signature from the exchange on the + * deposit confirmation. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Set (by the interpreter) to a public key corresponding + * to @e purse_priv. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * PurseCreateDeposit handle while operation is running. + */ + struct TALER_EXCHANGE_PurseCreateDepositHandle *dh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Length of the @e coin_references array. + */ + unsigned int num_coin_references; + + /** + * Should we upload the contract? + */ + bool upload_contract; + +}; + + +/** + * Callback to analyze the /purses/$PID/create response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param dr deposit response details + */ +static void +deposit_cb (void *cls, + const struct TALER_EXCHANGE_PurseCreateDepositResponse *dr) +{ + struct PurseCreateDepositState *ds = cls; + + ds->dh = NULL; + if (ds->expected_response_code != dr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + dr->hr.http_status, + ds->expected_response_code); + return; + } + if (MHD_HTTP_OK == dr->hr.http_status) + { + ds->exchange_pub = dr->details.ok.exchange_pub; + ds->exchange_sig = dr->details.ok.exchange_sig; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +deposit_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PurseCreateDepositState *ds = cls; + struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references]; + + (void) cmd; + ds->is = is; + GNUNET_CRYPTO_eddsa_key_create (&ds->purse_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_create (&ds->merge_priv.eddsa_priv); + GNUNET_CRYPTO_ecdhe_key_create (&ds->contract_priv.ecdhe_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&ds->purse_priv.eddsa_priv, + &ds->purse_pub.eddsa_pub); + + for (unsigned int i = 0; i<ds->num_coin_references; i++) + { + struct Coin *cr = &ds->coin_references[i]; + struct TALER_EXCHANGE_PurseDeposit *pd = &deposits[i]; + const struct TALER_TESTING_Command *coin_cmd; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + const struct TALER_DenominationSignature *denom_pub_sig; + + coin_cmd = TALER_TESTING_interpreter_lookup_command (is, + cr->command_ref); + if (NULL == coin_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + + if ( (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_cmd, + cr->coin_index, + &coin_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (coin_cmd, + cr->coin_index, + &age_commitment_proof)) + || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (coin_cmd, + cr->coin_index, + &denom_pub)) || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_sig (coin_cmd, + cr->coin_index, + &denom_pub_sig)) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + pd->age_commitment_proof = age_commitment_proof; + pd->denom_sig = *denom_pub_sig; + pd->coin_priv = *coin_priv; + pd->amount = cr->deposit_with_fee; + pd->h_denom_pub = denom_pub->h_key; + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &cr->coin_pub.eddsa_pub); + cr->che.type = TALER_EXCHANGE_CTT_PURSE_DEPOSIT; + cr->che.amount = cr->deposit_with_fee; + GNUNET_CRYPTO_eddsa_key_get_public ( + &ds->purse_priv.eddsa_priv, + &cr->che.details.purse_deposit.purse_pub.eddsa_pub); + cr->che.details.purse_deposit.exchange_base_url + = TALER_TESTING_get_exchange_url (is); + TALER_age_commitment_hash ( + &age_commitment_proof->commitment, + &cr->che.details.purse_deposit.phac); + } + + ds->purse_expiration = + GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_relative_to_absolute (ds->rel_expiration)); + GNUNET_assert (0 == + json_object_set_new ( + ds->contract_terms, + "pay_deadline", + GNUNET_JSON_from_timestamp (ds->purse_expiration))); + ds->dh = TALER_EXCHANGE_purse_create_with_deposit ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + &ds->purse_priv, + &ds->merge_priv, + &ds->contract_priv, + ds->contract_terms, + ds->num_coin_references, + deposits, + ds->upload_contract, + &deposit_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not create purse with deposit\n"); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "deposit" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct PurseCreateDepositState`. + * @param cmd the command which is being cleaned up. + */ +static void +deposit_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PurseCreateDepositState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_purse_create_with_deposit_cancel (ds->dh); + ds->dh = NULL; + } + for (unsigned int i = 0; i<ds->num_coin_references; i++) + GNUNET_free (ds->coin_references[i].command_ref); + json_decref (ds->contract_terms); + GNUNET_free (ds->coin_references); + GNUNET_free (ds); +} + + +/** + * Offer internal data from a "deposit" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +deposit_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct PurseCreateDepositState *ds = cls; + if (index >= ds->num_coin_references) + return GNUNET_NO; + + { + const struct Coin *co = &ds->coin_references[index]; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_merge_priv (&ds->merge_priv), + TALER_TESTING_make_trait_contract_priv (&ds->contract_priv), + TALER_TESTING_make_trait_coin_history (index, + &co->che), + TALER_TESTING_make_trait_coin_pub (index, + &co->coin_pub), + TALER_TESTING_make_trait_purse_priv (&ds->purse_priv), + TALER_TESTING_make_trait_purse_pub (&ds->purse_pub), + TALER_TESTING_make_trait_contract_terms (ds->contract_terms), + TALER_TESTING_make_trait_deposit_amount (0, + &ds->target_amount), + TALER_TESTING_make_trait_timestamp (index, + &ds->purse_expiration), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); + } +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_create_with_deposit ( + const char *label, + unsigned int expected_http_status, + const char *contract_terms, + bool upload_contract, + struct GNUNET_TIME_Relative purse_expiration, + ...) +{ + struct PurseCreateDepositState *ds; + + ds = GNUNET_new (struct PurseCreateDepositState); + ds->rel_expiration = purse_expiration; + ds->upload_contract = upload_contract; + ds->expected_response_code = expected_http_status; + ds->contract_terms = json_loads (contract_terms, + JSON_REJECT_DUPLICATES, + NULL); + if (NULL == ds->contract_terms) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse contract terms `%s' for CMD `%s'\n", + contract_terms, + label); + GNUNET_assert (0); + } + { + va_list ap; + unsigned int i; + const char *ref; + const char *val; + + va_start (ap, purse_expiration); + while (NULL != (va_arg (ap, const char *))) + ds->num_coin_references++; + va_end (ap); + GNUNET_assert (0 == (ds->num_coin_references % 2)); + ds->num_coin_references /= 2; + ds->coin_references = GNUNET_new_array (ds->num_coin_references, + struct Coin); + i = 0; + va_start (ap, purse_expiration); + while (NULL != (ref = va_arg (ap, const char *))) + { + struct Coin *c = &ds->coin_references[i++]; + + GNUNET_assert (NULL != (val = va_arg (ap, const char *))); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_parse_coin_reference ( + ref, + &c->command_ref, + &c->coin_index)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (val, + &c->deposit_with_fee)); + } + va_end (ap); + } + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &deposit_run, + .cleanup = &deposit_cleanup, + .traits = &deposit_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_purse_create_deposit.c */ diff --git a/src/testing/testing_api_cmd_purse_delete.c b/src/testing/testing_api_cmd_purse_delete.c new file mode 100644 index 000000000..26037359e --- /dev/null +++ b/src/testing/testing_api_cmd_purse_delete.c @@ -0,0 +1,189 @@ +/* + This file is part of TALER + Copyright (C) 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 + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_purse_delete.c + * @brief command for testing /management/purse/disable. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "purse_delete" CMD. + */ +struct PurseDeleteState +{ + + /** + * Purse delete handle while operation is running. + */ + struct TALER_EXCHANGE_PurseDeleteHandle *pdh; + + /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Command that created the purse we now want to + * delete. + */ + const char *purse_cmd; +}; + + +/** + * Callback to analyze the DELETE /purses/$PID response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param pdr HTTP response details + */ +static void +purse_delete_cb (void *cls, + const struct TALER_EXCHANGE_PurseDeleteResponse *pdr) +{ + struct PurseDeleteState *pds = cls; + + pds->pdh = NULL; + if (pds->expected_response_code != pdr->hr.http_status) + { + TALER_TESTING_unexpected_status (pds->is, + pdr->hr.http_status, + pds->expected_response_code); + return; + } + TALER_TESTING_interpreter_next (pds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +purse_delete_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PurseDeleteState *pds = cls; + const struct TALER_PurseContractPrivateKeyP *purse_priv; + const struct TALER_TESTING_Command *ref; + const char *exchange_url; + + (void) cmd; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + ref = TALER_TESTING_interpreter_lookup_command (is, + pds->purse_cmd); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_purse_priv (ref, + &purse_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + pds->is = is; + pds->pdh = TALER_EXCHANGE_purse_delete ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + purse_priv, + &purse_delete_cb, + pds); + if (NULL == pds->pdh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "purse_delete" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct PurseDeleteState`. + * @param cmd the command which is being cleaned up. + */ +static void +purse_delete_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PurseDeleteState *pds = cls; + + if (NULL != pds->pdh) + { + TALER_TESTING_command_incomplete (pds->is, + cmd->label); + TALER_EXCHANGE_purse_delete_cancel (pds->pdh); + pds->pdh = NULL; + } + GNUNET_free (pds); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_delete (const char *label, + unsigned int expected_http_status, + const char *purse_cmd) +{ + struct PurseDeleteState *ds; + + ds = GNUNET_new (struct PurseDeleteState); + ds->expected_response_code = expected_http_status; + ds->purse_cmd = purse_cmd; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &purse_delete_run, + .cleanup = &purse_delete_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_purse_delete.c */ diff --git a/src/testing/testing_api_cmd_purse_deposit.c b/src/testing/testing_api_cmd_purse_deposit.c new file mode 100644 index 000000000..048c6d736 --- /dev/null +++ b/src/testing/testing_api_cmd_purse_deposit.c @@ -0,0 +1,491 @@ +/* + This file is part of TALER + Copyright (C) 2022, 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_purse_deposit.c + * @brief command for testing /purses/$PID/create + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + +/** + * Information we keep per deposited coin. + */ +struct Coin +{ + /** + * Reference to the respective command. + */ + char *command_ref; + + /** + * Entry in the coin's history generated by this operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che; + + /** + * Public key of the deposited coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * index of the specific coin in the traits of @e command_ref. + */ + unsigned int coin_index; + + /** + * Amount to deposit (with fee). + */ + struct TALER_Amount deposit_with_fee; + +}; + + +/** + * State for a "purse deposit" CMD. + */ +struct PurseDepositState +{ + + /** + * Total purse target amount without fees. + */ + struct TALER_Amount target_amount; + + /** + * Reference to any command that is able to provide a coin. + */ + struct Coin *coin_references; + + /** + * The purse's public key. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * The reserve we are being deposited into. + * Set as a trait once we know the reserve. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * PurseDeposit handle while operation is running. + */ + struct TALER_EXCHANGE_PurseDepositHandle *dh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to the command that established the purse. + */ + const char *purse_ref; + + /** + * Reserve history entry that corresponds to this operation. + * Will be of type #TALER_EXCHANGE_RTT_MERGE. + * Only valid if @e purse_complete is true. + */ + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Length of the @e coin_references array. + */ + unsigned int num_coin_references; + + /** + * Minimum age to apply to all deposits. + */ + uint8_t min_age; + + /** + * Set to true if this deposit filled the purse. + */ + bool purse_complete; +}; + + +/** + * Callback to analyze the /purses/$PID/deposit response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param dr deposit response details + */ +static void +deposit_cb (void *cls, + const struct TALER_EXCHANGE_PurseDepositResponse *dr) +{ + struct PurseDepositState *ds = cls; + + ds->dh = NULL; + if (ds->expected_response_code != dr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + dr->hr.http_status, + ds->expected_response_code); + return; + } + if (MHD_HTTP_OK == dr->hr.http_status) + { + if (-1 != + TALER_amount_cmp (&dr->details.ok.total_deposited, + &dr->details.ok.purse_value_after_fees)) + { + const struct TALER_TESTING_Command *purse_cmd; + const struct TALER_ReserveSignatureP *reserve_sig; + const struct TALER_ReservePublicKeyP *reserve_pub; + const struct GNUNET_TIME_Timestamp *merge_timestamp; + const struct TALER_PurseMergePublicKeyP *merge_pub; + + purse_cmd = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->purse_ref); + GNUNET_assert (NULL != purse_cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_sig (purse_cmd, + &reserve_sig)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (purse_cmd, + &reserve_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_merge_pub (purse_cmd, + &merge_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->reserve_pub = *reserve_pub; + if (GNUNET_OK != + TALER_TESTING_get_trait_timestamp (purse_cmd, + 0, + &merge_timestamp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + + /* Deposits complete, create trait! */ + ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE; + { + struct TALER_EXCHANGE_Keys *keys; + const struct TALER_EXCHANGE_GlobalFee *gf; + + keys = TALER_TESTING_get_keys (ds->is); + GNUNET_assert (NULL != keys); + gf = TALER_EXCHANGE_get_global_fee (keys, + *merge_timestamp); + GNUNET_assert (NULL != gf); + + /* Note: change when flags below changes! */ + ds->reserve_history.amount + = dr->details.ok.purse_value_after_fees; + if (true) + { + ds->reserve_history.details.merge_details.purse_fee = gf->fees.purse; + } + else + { + TALER_amount_set_zero ( + ds->reserve_history.amount.currency, + &ds->reserve_history.details.merge_details.purse_fee); + } + } + ds->reserve_history.details.merge_details.h_contract_terms + = dr->details.ok.h_contract_terms; + ds->reserve_history.details.merge_details.merge_pub + = *merge_pub; + ds->reserve_history.details.merge_details.purse_pub + = ds->purse_pub; + ds->reserve_history.details.merge_details.reserve_sig + = *reserve_sig; + ds->reserve_history.details.merge_details.merge_timestamp + = *merge_timestamp; + ds->reserve_history.details.merge_details.purse_expiration + = dr->details.ok.purse_expiration; + ds->reserve_history.details.merge_details.min_age + = ds->min_age; + ds->reserve_history.details.merge_details.flags + = TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE; + ds->purse_complete = true; + } + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +deposit_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PurseDepositState *ds = cls; + struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references]; + const struct TALER_PurseContractPublicKeyP *purse_pub; + const struct TALER_TESTING_Command *purse_cmd; + + (void) cmd; + ds->is = is; + purse_cmd = TALER_TESTING_interpreter_lookup_command (is, + ds->purse_ref); + GNUNET_assert (NULL != purse_cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_purse_pub (purse_cmd, + &purse_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + ds->purse_pub = *purse_pub; + for (unsigned int i = 0; i<ds->num_coin_references; i++) + { + struct Coin *cr = &ds->coin_references[i]; + struct TALER_EXCHANGE_PurseDeposit *pd = &deposits[i]; + const struct TALER_TESTING_Command *coin_cmd; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + const struct TALER_DenominationSignature *denom_pub_sig; + + coin_cmd = TALER_TESTING_interpreter_lookup_command (is, + cr->command_ref); + GNUNET_assert (NULL != coin_cmd); + if ( (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_cmd, + cr->coin_index, + &coin_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (coin_cmd, + cr->coin_index, + &age_commitment_proof)) + || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (coin_cmd, + cr->coin_index, + &denom_pub)) || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_sig (coin_cmd, + cr->coin_index, + &denom_pub_sig)) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &cr->coin_pub.eddsa_pub); + cr->che.type = TALER_EXCHANGE_CTT_PURSE_DEPOSIT; + cr->che.amount = cr->deposit_with_fee; + cr->che.details.purse_deposit.purse_pub = *purse_pub; + cr->che.details.purse_deposit.exchange_base_url + = TALER_TESTING_get_exchange_url (is); + TALER_age_commitment_hash ( + &age_commitment_proof->commitment, + &cr->che.details.purse_deposit.phac); + pd->age_commitment_proof = age_commitment_proof; + pd->denom_sig = *denom_pub_sig; + pd->coin_priv = *coin_priv; + pd->amount = cr->deposit_with_fee; + pd->h_denom_pub = denom_pub->h_key; + } + + ds->dh = TALER_EXCHANGE_purse_deposit ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + NULL, /* FIXME #7271: WADs support: purse exchange URL */ + &ds->purse_pub, + ds->min_age, + ds->num_coin_references, + deposits, + &deposit_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not deposit into purse\n"); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "deposit" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct PurseDepositState`. + * @param cmd the command which is being cleaned up. + */ +static void +deposit_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PurseDepositState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_purse_deposit_cancel (ds->dh); + ds->dh = NULL; + } + for (unsigned int i = 0; i<ds->num_coin_references; i++) + GNUNET_free (ds->coin_references[i].command_ref); + GNUNET_free (ds->coin_references); + GNUNET_free (ds); +} + + +/** + * Offer internal data from a "deposit" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +deposit_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct PurseDepositState *ds = cls; + + if (index >= ds->num_coin_references) + return GNUNET_NO; + { + const struct Coin *co = &ds->coin_references[index]; + struct TALER_TESTING_Trait traits[] = { + /* history entry MUST be first due to response code logic below! */ + TALER_TESTING_make_trait_reserve_history (0, + &ds->reserve_history), + TALER_TESTING_make_trait_coin_history (index, + &co->che), + TALER_TESTING_make_trait_coin_pub (index, + &co->coin_pub), + TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub), + TALER_TESTING_make_trait_purse_pub (&ds->purse_pub), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (ds->purse_complete + ? &traits[0] /* we have reserve history */ + : &traits[1], /* skip reserve history */ + ret, + trait, + index); + } +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_deposit_coins ( + const char *label, + unsigned int expected_http_status, + uint8_t min_age, + const char *purse_ref, + ...) +{ + struct PurseDepositState *ds; + + ds = GNUNET_new (struct PurseDepositState); + ds->expected_response_code = expected_http_status; + ds->min_age = min_age; + ds->purse_ref = purse_ref; + { + va_list ap; + unsigned int i; + const char *ref; + const char *val; + + va_start (ap, purse_ref); + while (NULL != (va_arg (ap, const char *))) + ds->num_coin_references++; + va_end (ap); + GNUNET_assert (0 == (ds->num_coin_references % 2)); + ds->num_coin_references /= 2; + ds->coin_references = GNUNET_new_array (ds->num_coin_references, + struct Coin); + i = 0; + va_start (ap, purse_ref); + while (NULL != (ref = va_arg (ap, const char *))) + { + struct Coin *c = &ds->coin_references[i++]; + + GNUNET_assert (NULL != (val = va_arg (ap, + const char *))); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_parse_coin_reference ( + ref, + &c->command_ref, + &c->coin_index)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (val, + &c->deposit_with_fee)); + } + va_end (ap); + } + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &deposit_run, + .cleanup = &deposit_cleanup, + .traits = &deposit_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_purse_deposit.c */ diff --git a/src/testing/testing_api_cmd_purse_get.c b/src/testing/testing_api_cmd_purse_get.c new file mode 100644 index 000000000..d5246660b --- /dev/null +++ b/src/testing/testing_api_cmd_purse_get.c @@ -0,0 +1,367 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_purse_get.c + * @brief Implement the GET /purse/$RID test command. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "poll" CMD. + */ +struct PollState +{ + + /** + * How long do we give the exchange to respond? + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Label to the command which created the purse to check, + * needed to resort the purse key. + */ + const char *poll_reference; + + /** + * Timeout to wait for at most. + */ + struct GNUNET_SCHEDULER_Task *tt; + + /** + * The interpreter we are using. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * State for a "status" CMD. + */ +struct StatusState +{ + + /** + * How long do we give the exchange to respond? + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Poller waiting for us. + */ + struct PollState *ps; + + /** + * Label to the command which created the purse to check, + * needed to resort the purse key. + */ + const char *purse_reference; + + /** + * Handle to the "purse status" operation. + */ + struct TALER_EXCHANGE_PurseGetHandle *pgh; + + /** + * Expected purse balance. + */ + const char *expected_balance; + + /** + * Public key of the purse being analyzed. + */ + const struct TALER_PurseContractPublicKeyP *purse_pub; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Are we waiting for a merge or a deposit? + */ + bool wait_for_merge; + +}; + + +/** + * Check that the purse balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +purse_status_cb (void *cls, + const struct TALER_EXCHANGE_PurseGetResponse *rs) +{ + struct StatusState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + + ss->pgh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK == ss->expected_response_code) + { + struct TALER_Amount eb; + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ss->expected_balance, + &eb)); + if (0 != TALER_amount_cmp (&eb, + &rs->details.ok.balance)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected amount in purse: %s\n", + TALER_amount_to_string (&rs->details.ok.balance)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + if (NULL != ss->ps) + { + /* force continuation on long poller */ + GNUNET_SCHEDULER_cancel (ss->ps->tt); + ss->ps->tt = NULL; + TALER_TESTING_interpreter_next (is); + return; + } + if (GNUNET_TIME_relative_is_zero (ss->timeout)) + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +status_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct StatusState *ss = cls; + const struct TALER_TESTING_Command *create_purse; + + ss->is = is; + create_purse + = TALER_TESTING_interpreter_lookup_command (is, + ss->purse_reference); + GNUNET_assert (NULL != create_purse); + if (GNUNET_OK != + TALER_TESTING_get_trait_purse_pub (create_purse, + &ss->purse_pub)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find purse_pub for status query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + ss->pgh = TALER_EXCHANGE_purse_get ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + ss->purse_pub, + ss->timeout, + ss->wait_for_merge, + &purse_status_cb, + ss); + if (! GNUNET_TIME_relative_is_zero (ss->timeout)) + { + TALER_TESTING_interpreter_next (is); + return; + } +} + + +/** + * Cleanup the state from a "purse status" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +status_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct StatusState *ss = cls; + + if (NULL != ss->pgh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_purse_get_cancel (ss->pgh); + ss->pgh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_poll ( + const char *label, + unsigned int expected_http_status, + const char *purse_ref, + const char *expected_balance, + bool wait_for_merge, + struct GNUNET_TIME_Relative timeout) +{ + struct StatusState *ss; + + GNUNET_assert (NULL != purse_ref); + ss = GNUNET_new (struct StatusState); + ss->purse_reference = purse_ref; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_http_status; + ss->timeout = timeout; + ss->wait_for_merge = wait_for_merge; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &status_run, + .cleanup = &status_cleanup + }; + + return cmd; + } +} + + +/** + * Long poller timed out. Fail the test. + * + * @param cls a `struct PollState` + */ +static void +finish_timeout (void *cls) +{ + struct PollState *ps = cls; + + ps->tt = NULL; + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ps->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +finish_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PollState *ps = cls; + const struct TALER_TESTING_Command *poll_purse; + struct StatusState *ss; + + ps->is = is; + poll_purse + = TALER_TESTING_interpreter_lookup_command (is, + ps->poll_reference); + GNUNET_assert (NULL != poll_purse); + GNUNET_assert (poll_purse->run == &status_run); + ss = poll_purse->cls; + if (NULL == ss->pgh) + { + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_assert (NULL == ss->ps); + ss->ps = ps; + ps->tt = GNUNET_SCHEDULER_add_delayed (ps->timeout, + &finish_timeout, + ps); +} + + +/** + * Cleanup the state from a "purse finish" CMD. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +finish_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PollState *ps = cls; + + if (NULL != ps->tt) + { + GNUNET_SCHEDULER_cancel (ps->tt); + ps->tt = NULL; + } + GNUNET_free (ps); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_poll_finish (const char *label, + struct GNUNET_TIME_Relative timeout, + const char *poll_reference) +{ + struct PollState *ps; + + GNUNET_assert (NULL != poll_reference); + ps = GNUNET_new (struct PollState); + ps->timeout = timeout; + ps->poll_reference = poll_reference; + { + struct TALER_TESTING_Command cmd = { + .cls = ps, + .label = label, + .run = &finish_run, + .cleanup = &finish_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_purse_merge.c b/src/testing/testing_api_cmd_purse_merge.c new file mode 100644 index 000000000..cf9d4f996 --- /dev/null +++ b/src/testing/testing_api_cmd_purse_merge.c @@ -0,0 +1,436 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_purse_merge.c + * @brief command for testing /purses/$PID/merge + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "purse create deposit" CMD. + */ +struct PurseMergeState +{ + + /** + * Merge time. + */ + struct GNUNET_TIME_Timestamp merge_timestamp; + + /** + * Reserve public key (to be merged into) + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Reserve private key (useful especially if + * @e reserve_ref is NULL). + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Handle while operation is running. + */ + struct TALER_EXCHANGE_AccountMergeHandle *dh; + + /** + * Reference to the merge capability. + */ + const char *merge_ref; + + /** + * Reference to the reserve, or NULL (!). + */ + const char *reserve_ref; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Hash of the payto://-URI for the reserve we are + * merging into. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC. + */ + uint64_t requirement_row; + + /** + * Reserve history entry that corresponds to this operation. + * Will be of type #TALER_EXCHANGE_RTT_MERGE. + */ + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; + + /** + * Public key of the purse. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * Public key of the merge capability. + */ + struct TALER_PurseMergePublicKeyP merge_pub; + + /** + * Contract value. + */ + struct TALER_Amount value_after_fees; + + /** + * Hash of the contract. + */ + struct TALER_PrivateContractHashP h_contract_terms; + + /** + * When does the purse expire. + */ + struct GNUNET_TIME_Timestamp purse_expiration; + + /** + * Minimum age of deposits into the purse. + */ + uint32_t min_age; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + +}; + + +/** + * Callback to analyze the /purses/$PID/merge response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param dr merge response details + */ +static void +merge_cb (void *cls, + const struct TALER_EXCHANGE_AccountMergeResponse *dr) +{ + struct PurseMergeState *ds = cls; + + ds->dh = NULL; + switch (dr->hr.http_status) + { + case MHD_HTTP_OK: + ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE; + ds->reserve_history.amount = ds->value_after_fees; + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero ( + ds->value_after_fees.currency, + &ds->reserve_history.details.merge_details.purse_fee)); + ds->reserve_history.details.merge_details.h_contract_terms + = ds->h_contract_terms; + ds->reserve_history.details.merge_details.merge_pub + = ds->merge_pub; + ds->reserve_history.details.merge_details.purse_pub + = ds->purse_pub; + ds->reserve_history.details.merge_details.reserve_sig + = *dr->reserve_sig; + ds->reserve_history.details.merge_details.merge_timestamp + = ds->merge_timestamp; + ds->reserve_history.details.merge_details.purse_expiration + = ds->purse_expiration; + ds->reserve_history.details.merge_details.min_age + = ds->min_age; + ds->reserve_history.details.merge_details.flags + = TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE; + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* KYC required */ + ds->requirement_row = + dr->details.unavailable_for_legal_reasons.requirement_row; + break; + } + + + if (ds->expected_response_code != dr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + dr->hr.http_status, + ds->expected_response_code); + return; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +merge_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PurseMergeState *ds = cls; + const struct TALER_PurseMergePrivateKeyP *merge_priv; + const json_t *ct; + const struct TALER_TESTING_Command *ref; + + (void) cmd; + ds->is = is; + ref = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->merge_ref); + GNUNET_assert (NULL != ref); + if (GNUNET_OK != + TALER_TESTING_get_trait_merge_priv (ref, + &merge_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + { + const struct TALER_PurseContractPublicKeyP *purse_pub; + + if (GNUNET_OK != + TALER_TESTING_get_trait_purse_pub (ref, + &purse_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->purse_pub = *purse_pub; + } + + if (GNUNET_OK != + TALER_TESTING_get_trait_contract_terms (ref, + &ct)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + if (GNUNET_OK != + TALER_JSON_contract_hash (ct, + &ds->h_contract_terms)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_timestamp ("pay_deadline", + &ds->purse_expiration), + TALER_JSON_spec_amount_any ("amount", + &ds->value_after_fees), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("minimum_age", + &ds->min_age), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (ct, + spec, + NULL, NULL)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + } + + if (NULL == ds->reserve_ref) + { + GNUNET_CRYPTO_eddsa_key_create (&ds->reserve_priv.eddsa_priv); + } + else + { + const struct TALER_ReservePrivateKeyP *rp; + + ref = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->reserve_ref); + GNUNET_assert (NULL != ref); + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (ref, + &rp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->reserve_priv = *rp; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ds->reserve_priv.eddsa_priv, + &ds->reserve_pub.eddsa_pub); + { + char *payto_uri; + const char *exchange_url; + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + payto_uri = TALER_reserve_make_payto (exchange_url, + &ds->reserve_pub); + TALER_payto_hash (payto_uri, + &ds->h_payto); + GNUNET_free (payto_uri); + } + GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv, + &ds->merge_pub.eddsa_pub); + ds->merge_timestamp = GNUNET_TIME_timestamp_get (); + ds->dh = TALER_EXCHANGE_account_merge ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + NULL, /* no wad */ + &ds->reserve_priv, + &ds->purse_pub, + merge_priv, + &ds->h_contract_terms, + ds->min_age, + &ds->value_after_fees, + ds->purse_expiration, + ds->merge_timestamp, + &merge_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not merge purse\n"); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "merge" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct PurseMergeState`. + * @param cmd the command which is being cleaned up. + */ +static void +merge_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PurseMergeState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_account_merge_cancel (ds->dh); + ds->dh = NULL; + } + GNUNET_free (ds); +} + + +/** + * Offer internal data from a "merge" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +merge_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct PurseMergeState *ds = cls; + struct TALER_TESTING_Trait traits[] = { + /* history entry MUST be first due to response code logic below! */ + TALER_TESTING_make_trait_reserve_history (0, + &ds->reserve_history), + TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub), + TALER_TESTING_make_trait_timestamp (0, + &ds->merge_timestamp), + TALER_TESTING_make_trait_legi_requirement_row (&ds->requirement_row), + TALER_TESTING_make_trait_h_payto (&ds->h_payto), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait ((ds->expected_response_code == MHD_HTTP_OK) + ? &traits[0] /* we have reserve history */ + : &traits[1], /* skip reserve history */ + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_merge ( + const char *label, + unsigned int expected_http_status, + const char *merge_ref, + const char *reserve_ref) +{ + struct PurseMergeState *ds; + + ds = GNUNET_new (struct PurseMergeState); + ds->merge_ref = merge_ref; + ds->reserve_ref = reserve_ref; + ds->expected_response_code = expected_http_status; + + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &merge_run, + .cleanup = &merge_cleanup, + .traits = &merge_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_purse_merge.c */ diff --git a/src/testing/testing_api_cmd_recoup.c b/src/testing/testing_api_cmd_recoup.c index 7c4204d9e..cefcd96bb 100644 --- a/src/testing/testing_api_cmd_recoup.c +++ b/src/testing/testing_api_cmd_recoup.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -54,88 +54,44 @@ struct RecoupState struct TALER_EXCHANGE_RecoupHandle *ph; /** - * NULL if coin was not refreshed, otherwise reference - * to the melt operation underlying @a coin_reference. + * If the recoup filled a reserve, this is set to the reserve's public key. */ - const char *melt_reference; + struct TALER_ReservePublicKeyP reserve_pub; /** - * If the recoup filled a reserve, this is set to the reserve's public key. + * Entry in the coin's history generated by this operation. */ - struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_EXCHANGE_CoinHistoryEntry che; + + /** + * Public key of the refunded coin. + */ + struct TALER_CoinSpendPublicKeyP coin; /** * Reserve history entry, set if this recoup actually filled up a reserve. * Otherwise `reserve_history.type` will be zero. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; }; /** - * Parser reference to a coin. - * - * @param coin_reference of format $LABEL['#' $INDEX]? - * @param[out] cref where we return a copy of $LABEL - * @param[out] idx where we set $INDEX - * @return #GNUNET_SYSERR if $INDEX is present but not numeric - */ -static int -parse_coin_reference (const char *coin_reference, - char **cref, - unsigned int *idx) -{ - const char *index; - - /* We allow command references of the form "$LABEL#$INDEX" or - just "$LABEL", which implies the index is 0. Figure out - which one it is. */ - index = strchr (coin_reference, '#'); - if (NULL == index) - { - *idx = 0; - *cref = GNUNET_strdup (coin_reference); - return GNUNET_OK; - } - *cref = GNUNET_strndup (coin_reference, - index - coin_reference); - if (1 != sscanf (index + 1, - "%u", - idx)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Numeric index (not `%s') required after `#' in command reference of command in %s:%u\n", - index, - __FILE__, - __LINE__); - GNUNET_free (*cref); - *cref = NULL; - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** * Check the result of the recoup request: checks whether * the HTTP response code is good, and that the coin that * was paid back belonged to the right reserve. * * @param cls closure - * @param hr HTTP response details - * @param reserve_pub public key of the reserve receiving the recoup, NULL if refreshed or on error - * @param old_coin_pub public key of the dirty coin, NULL if not refreshed or on error + * @param rr response details */ static void recoup_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_CoinSpendPublicKeyP *old_coin_pub) + const struct TALER_EXCHANGE_RecoupResponse *rr) { struct RecoupState *ps = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr; struct TALER_TESTING_Interpreter *is = ps->is; - struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; const struct TALER_TESTING_Command *reserve_cmd; char *cref; unsigned int idx; @@ -143,29 +99,22 @@ recoup_cb (void *cls, ps->ph = NULL; if (ps->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - cmd->label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - fprintf (stderr, "\n"); - TALER_TESTING_interpreter_fail (is); + TALER_TESTING_unexpected_status (is, + hr->http_status, + ps->expected_response_code); return; } if (GNUNET_OK != - parse_coin_reference (ps->coin_reference, - &cref, - &idx)) + TALER_TESTING_parse_coin_reference ( + ps->coin_reference, + &cref, + &idx)) { TALER_TESTING_interpreter_fail (is); return; } + (void) idx; /* do NOT use! We ignore 'idx', must be 0 for melt! */ reserve_cmd = TALER_TESTING_interpreter_lookup_command (is, cref); @@ -182,52 +131,11 @@ recoup_cb (void *cls, { case MHD_HTTP_OK: /* check old_coin_pub or reserve_pub, respectively */ - if (NULL != ps->melt_reference) - { - const struct TALER_TESTING_Command *melt_cmd; - const struct TALER_CoinSpendPrivateKeyP *dirty_priv; - struct TALER_CoinSpendPublicKeyP oc; - - melt_cmd = TALER_TESTING_interpreter_lookup_command (is, - ps->melt_reference); - if (NULL == melt_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - if (GNUNET_OK != - TALER_TESTING_get_trait_coin_priv (melt_cmd, - 0, - &dirty_priv)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv, - &oc.eddsa_pub); - if (0 != GNUNET_memcmp (&oc, - old_coin_pub)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - } - else { const struct TALER_ReservePrivateKeyP *reserve_priv; - if (NULL == reserve_pub) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv (reserve_cmd, - idx, &reserve_priv)) { GNUNET_break (0); @@ -236,7 +144,7 @@ recoup_cb (void *cls, } GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, &ps->reserve_pub.eddsa_pub); - if (0 != GNUNET_memcmp (reserve_pub, + if (0 != GNUNET_memcmp (&rr->details.ok.reserve_pub, &ps->reserve_pub)) { GNUNET_break (0); @@ -247,6 +155,7 @@ recoup_cb (void *cls, TALER_amount_is_valid (&ps->reserve_history.amount)) ps->reserve_history.type = TALER_EXCHANGE_RTT_RECOUP; /* ps->reserve_history.details.recoup_details.coin_pub; // initialized earlier */ + ps->che.details.recoup.reserve_pub = ps->reserve_pub; } break; case MHD_HTTP_NOT_FOUND: @@ -279,18 +188,20 @@ recoup_run (void *cls, struct RecoupState *ps = cls; const struct TALER_TESTING_Command *coin_cmd; const struct TALER_CoinSpendPrivateKeyP *coin_priv; - const struct TALER_DenominationBlindingKeyP *blinding_key; const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; const struct TALER_DenominationSignature *coin_sig; - struct TALER_PlanchetSecretsP planchet; + const struct TALER_PlanchetMasterSecretP *planchet; char *cref; unsigned int idx; + const struct TALER_ExchangeWithdrawValues *ewv; + struct TALER_DenominationHashP h_denom_pub; ps->is = is; if (GNUNET_OK != - parse_coin_reference (ps->coin_reference, - &cref, - &idx)) + TALER_TESTING_parse_coin_reference ( + ps->coin_reference, + &cref, + &idx)) { TALER_TESTING_interpreter_fail (is); return; @@ -306,7 +217,6 @@ recoup_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv (coin_cmd, idx, @@ -316,18 +226,25 @@ recoup_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &ps->coin.eddsa_pub); + if (GNUNET_OK != + TALER_TESTING_get_trait_exchange_wd_value (coin_cmd, + idx, + &ewv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } if (GNUNET_OK != - TALER_TESTING_get_trait_blinding_key (coin_cmd, - idx, - &blinding_key)) + TALER_TESTING_get_trait_planchet_secret (coin_cmd, + &planchet)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - planchet.coin_priv = *coin_priv; - planchet.blinding_key = *blinding_key; GNUNET_CRYPTO_eddsa_key_get_public ( &coin_priv->eddsa_priv, &ps->reserve_history.details.recoup_details.coin_pub.eddsa_pub); @@ -341,7 +258,6 @@ recoup_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig (coin_cmd, idx, @@ -351,18 +267,30 @@ recoup_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Trying to recoup denomination '%s'\n", TALER_B2S (&denom_pub->h_key)); - - ps->ph = TALER_EXCHANGE_recoup (is->exchange, - denom_pub, - coin_sig, - &planchet, - NULL != ps->melt_reference, - recoup_cb, - ps); + ps->che.type = TALER_EXCHANGE_CTT_RECOUP; + ps->che.amount = ps->reserve_history.amount; + TALER_planchet_blinding_secret_create (planchet, + ewv, + &ps->che.details.recoup.coin_bks); + TALER_denom_pub_hash (&denom_pub->key, + &h_denom_pub); + TALER_wallet_recoup_sign (&h_denom_pub, + &ps->che.details.recoup.coin_bks, + coin_priv, + &ps->che.details.recoup.coin_sig); + ps->ph = TALER_EXCHANGE_recoup ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + denom_pub, + coin_sig, + ewv, + planchet, + &recoup_cb, + ps); GNUNET_assert (NULL != ps->ph); } @@ -398,7 +326,7 @@ recoup_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue recoup_traits (void *cls, const void **ret, const char *trait, @@ -410,10 +338,13 @@ recoup_traits (void *cls, return GNUNET_SYSERR; /* no traits */ { struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_reserve_pub (0, - &ps->reserve_pub), + TALER_TESTING_make_trait_reserve_pub (&ps->reserve_pub), TALER_TESTING_make_trait_reserve_history (0, &ps->reserve_history), + TALER_TESTING_make_trait_coin_history (0, + &ps->che), + TALER_TESTING_make_trait_coin_pub (0, + &ps->coin), TALER_TESTING_trait_end () }; @@ -425,22 +356,10 @@ recoup_traits (void *cls, } -/** - * Make a "recoup" command. - * - * @param label the command label - * @param expected_response_code expected HTTP status code - * @param coin_reference reference to any command which - * offers a coin & reserve private key. - * @param melt_reference NULL if coin was not refreshed - * @param amount how much do we expect to recoup? - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_recoup (const char *label, unsigned int expected_response_code, const char *coin_reference, - const char *melt_reference, const char *amount) { struct RecoupState *ps; @@ -448,11 +367,9 @@ TALER_TESTING_cmd_recoup (const char *label, ps = GNUNET_new (struct RecoupState); ps->expected_response_code = expected_response_code; ps->coin_reference = coin_reference; - ps->melt_reference = melt_reference; - if ( (NULL != amount) && - (GNUNET_OK != - TALER_string_to_amount (amount, - &ps->reserve_history.amount)) ) + if (GNUNET_OK != + TALER_string_to_amount (amount, + &ps->reserve_history.amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse amount `%s' at %s\n", diff --git a/src/testing/testing_api_cmd_recoup_refresh.c b/src/testing/testing_api_cmd_recoup_refresh.c new file mode 100644 index 000000000..68d267be4 --- /dev/null +++ b/src/testing/testing_api_cmd_recoup_refresh.c @@ -0,0 +1,441 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_recoup_refresh.c + * @brief Implement the /recoup-refresh test command. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "pay back" CMD. + */ +struct RecoupRefreshState +{ + /** + * Expected HTTP status code. + */ + unsigned int expected_response_code; + + /** + * Command that offers a reserve private key, + * plus a coin to be paid back. + */ + const char *coin_reference; + + /** + * Entry in the old coin's history generated by this operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che_old; + + /** + * Entry in the recouped coin's history generated by this operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che_new; + + /** + * Public key of the refunded coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub_old; + + /** + * Public key of the refunded coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub_new; + + /** + * Amount to be recouped. + */ + struct TALER_Amount amount; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Handle to the ongoing operation. + */ + struct TALER_EXCHANGE_RecoupRefreshHandle *ph; + + /** + * NULL if coin was not refreshed, otherwise reference + * to the melt operation underlying @a coin_reference. + */ + const char *melt_reference; + +}; + + +/** + * Check the result of the recoup_refresh request: checks whether + * the HTTP response code is good, and that the coin that + * was paid back belonged to the right old coin. + * + * @param cls closure + * @param rrr response details + */ +static void +recoup_refresh_cb (void *cls, + const struct TALER_EXCHANGE_RecoupRefreshResponse *rrr) +{ + struct RecoupRefreshState *rrs = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &rrr->hr; + struct TALER_TESTING_Interpreter *is = rrs->is; + char *cref; + unsigned int idx; + + rrs->ph = NULL; + if (rrs->expected_response_code != hr->http_status) + { + TALER_TESTING_unexpected_status (is, + hr->http_status, + rrs->expected_response_code); + return; + } + + if (GNUNET_OK != + TALER_TESTING_parse_coin_reference ( + rrs->coin_reference, + &cref, + &idx)) + { + TALER_TESTING_interpreter_fail (is); + return; + } + (void) idx; /* do NOT use! We ignore 'idx', must be 0 for melt! */ + + GNUNET_free (cref); + switch (hr->http_status) + { + case MHD_HTTP_OK: + /* check old_coin_pub */ + { + const struct TALER_TESTING_Command *melt_cmd; + const struct TALER_CoinSpendPrivateKeyP *dirty_priv; + struct TALER_CoinSpendPublicKeyP oc; + + melt_cmd = TALER_TESTING_interpreter_lookup_command (is, + rrs->melt_reference); + if (NULL == melt_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (melt_cmd, + 0, + &dirty_priv)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Coin %u not found in command %s\n", + 0, + rrs->melt_reference); + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv, + &oc.eddsa_pub); + if (0 != GNUNET_memcmp (&oc, + &rrr->details.ok.old_coin_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + } + break; + case MHD_HTTP_NOT_FOUND: + break; + case MHD_HTTP_CONFLICT: + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unmanaged HTTP status code %u/%d.\n", + hr->http_status, + (int) hr->ec); + break; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +recoup_refresh_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RecoupRefreshState *rrs = cls; + const struct TALER_TESTING_Command *coin_cmd; + const struct TALER_TESTING_Command *melt_cmd; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_CoinSpendPrivateKeyP *coin_priv_old; + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + const struct TALER_DenominationSignature *coin_sig; + const struct TALER_RefreshMasterSecretP *rplanchet; + const struct TALER_PlanchetMasterSecretP *planchet; + const struct TALER_ExchangeWithdrawValues *ewv; + char *cref; + unsigned int idx; + struct TALER_DenominationHashP h_denom_pub; + + rrs->is = is; + if (GNUNET_OK != + TALER_TESTING_parse_coin_reference ( + rrs->coin_reference, + &cref, + &idx)) + { + TALER_TESTING_interpreter_fail (is); + return; + } + + coin_cmd = TALER_TESTING_interpreter_lookup_command (is, + cref); + GNUNET_free (cref); + if (NULL == coin_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + melt_cmd = TALER_TESTING_interpreter_lookup_command (is, + rrs->melt_reference); + if (NULL == melt_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_cmd, + idx, + &coin_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (melt_cmd, + 0, + &coin_priv_old)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public ( + &coin_priv->eddsa_priv, + &rrs->coin_pub_new.eddsa_pub); + GNUNET_CRYPTO_eddsa_key_get_public ( + &coin_priv_old->eddsa_priv, + &rrs->coin_pub_old.eddsa_pub); + + if (GNUNET_OK != + TALER_TESTING_get_trait_exchange_wd_value (melt_cmd, + idx, + &ewv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_planchet_secrets (coin_cmd, + idx, + &planchet)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_refresh_secret (melt_cmd, + &rplanchet)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (coin_cmd, + idx, + &denom_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_denom_sig (coin_cmd, + idx, + &coin_sig)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Trying to recoup_refresh denomination '%s'\n", + TALER_B2S (&denom_pub->h_key)); + rrs->che_old.type + = TALER_EXCHANGE_CTT_OLD_COIN_RECOUP; + rrs->che_old.amount + = rrs->amount; + rrs->che_old.details.old_coin_recoup.new_coin_pub + = rrs->coin_pub_new; + rrs->che_new.type + = TALER_EXCHANGE_CTT_RECOUP_REFRESH; + rrs->che_new.amount + = rrs->amount; + rrs->che_new.details.recoup_refresh.old_coin_pub + = rrs->coin_pub_old; + TALER_planchet_blinding_secret_create ( + planchet, + ewv, + &rrs->che_new.details.recoup_refresh.coin_bks); + TALER_denom_pub_hash (&denom_pub->key, + &h_denom_pub); + TALER_wallet_recoup_refresh_sign ( + &h_denom_pub, + &rrs->che_new.details.recoup_refresh.coin_bks, + coin_priv, + &rrs->che_new.details.recoup_refresh.coin_sig); + rrs->ph = TALER_EXCHANGE_recoup_refresh ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + denom_pub, + coin_sig, + ewv, + rplanchet, + planchet, + idx, + &recoup_refresh_cb, + rrs); + GNUNET_assert (NULL != rrs->ph); +} + + +/** + * Cleanup the "recoup_refresh" CMD state, and possibly cancel + * a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +recoup_refresh_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct RecoupRefreshState *rrs = cls; + if (NULL != rrs->ph) + { + TALER_EXCHANGE_recoup_refresh_cancel (rrs->ph); + rrs->ph = NULL; + } + GNUNET_free (rrs); +} + + +/** + * Offer internal data from a "recoup-refresh" CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +recoup_refresh_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct RecoupRefreshState *rrs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_coin_history (0, + &rrs->che_old), + TALER_TESTING_make_trait_coin_pub (0, + &rrs->coin_pub_old), + TALER_TESTING_make_trait_coin_history (1, + &rrs->che_new), + TALER_TESTING_make_trait_coin_pub (1, + &rrs->coin_pub_new), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_recoup_refresh (const char *label, + unsigned int expected_response_code, + const char *coin_reference, + const char *melt_reference, + const char *amount) +{ + struct RecoupRefreshState *rrs; + + rrs = GNUNET_new (struct RecoupRefreshState); + rrs->expected_response_code = expected_response_code; + rrs->coin_reference = coin_reference; + rrs->melt_reference = melt_reference; + if (GNUNET_OK != + TALER_string_to_amount (amount, + &rrs->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 = rrs, + .label = label, + .run = &recoup_refresh_run, + .cleanup = &recoup_refresh_cleanup, + .traits = &recoup_refresh_traits + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_refresh.c b/src/testing/testing_api_cmd_refresh.c index fcf8540c0..111e9118f 100644 --- a/src/testing/testing_api_cmd_refresh.c +++ b/src/testing/testing_api_cmd_refresh.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2020 Taler Systems SA + Copyright (C) 2018-2022 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 @@ -70,10 +70,17 @@ struct TALER_TESTING_FreshCoinData */ struct TALER_CoinSpendPrivateKeyP coin_priv; + /* + * Fresh age commitment for the coin with proof and its hash, NULL if not + * applicable. + */ + struct TALER_AgeCommitmentProof *age_commitment_proof; + struct TALER_AgeCommitmentHash h_age_commitment; + /** * The blinding key (needed for recoup operations). */ - struct TALER_DenominationBlindingKeyP blinding_key; + union GNUNET_CRYPTO_BlindingSecretP blinding_key; }; @@ -91,9 +98,14 @@ struct RefreshMeltState const char *coin_reference; /** - * "Crypto data" used in the refresh operation. + * Data used in the refresh operation. + */ + struct TALER_EXCHANGE_RefreshData refresh_data; + + /** + * Our command. */ - char *refresh_data; + const struct TALER_TESTING_Command *cmd; /** * Reference to a previous melt command. @@ -106,6 +118,12 @@ struct RefreshMeltState struct TALER_EXCHANGE_MeltHandle *rmh; /** + * Expected entry in the coin history created by this + * operation. + */ + struct TALER_EXCHANGE_CoinHistoryEntry che; + + /** * Interpreter state. */ struct TALER_TESTING_Interpreter *is; @@ -117,11 +135,27 @@ struct RefreshMeltState struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; /** + * Array of @e num_fresh_coins of results from + * the melt operation. + */ + struct TALER_EXCHANGE_MeltBlindingDetail *mbds; + + /** + * Entropy seed for the refresh-melt operation. + */ + struct TALER_RefreshMasterSecretP rms; + + /** * Private key of the dirty coin being melted. */ const struct TALER_CoinSpendPrivateKeyP *melt_priv; /** + * Public key of the dirty coin being melted. + */ + struct TALER_CoinSpendPublicKeyP melt_pub; + + /** * Task scheduled to try later. */ struct GNUNET_SCHEDULER_Task *retry_task; @@ -137,11 +171,6 @@ struct RefreshMeltState struct GNUNET_TIME_Relative total_backoff; /** - * Number of bytes in @e refresh_data. - */ - size_t refresh_data_length; - - /** * Amounts to be generated during melt. */ const char **melt_fresh_amounts; @@ -167,7 +196,7 @@ struct RefreshMeltState * exchange to pick any previous /rerfesh/melt operation from * the database. */ - unsigned int double_melt; + bool double_melt; /** * How often should we retry on (transient) failures? @@ -197,6 +226,11 @@ struct RefreshRevealState struct TALER_EXCHANGE_RefreshesRevealHandle *rrh; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Convenience struct to keep in one place all the * data related to one fresh coin, set by the reveal callback * as it comes from the exchange. @@ -204,6 +238,12 @@ struct RefreshRevealState struct TALER_TESTING_FreshCoinData *fresh_coins; /** + * Array of @e num_fresh_coins planchet secrets derived + * from the transfer secret per fresh coin. + */ + struct TALER_PlanchetMasterSecretP *psa; + + /** * Interpreter state. */ struct TALER_TESTING_Interpreter *is; @@ -254,6 +294,11 @@ struct RefreshLinkState const char *reveal_reference; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Handle to the ongoing operation. */ struct TALER_EXCHANGE_LinkHandle *rlh; @@ -315,8 +360,7 @@ do_reveal_retry (void *cls) struct RefreshRevealState *rrs = cls; rrs->retry_task = NULL; - rrs->is->commands[rrs->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (rrs->is); refresh_reveal_run (rrs, NULL, rrs->is); @@ -329,23 +373,14 @@ do_reveal_retry (void *cls) * coming from the exchange, namely the fresh coins. * * @param cls closure, a `struct RefreshRevealState` - * @param hr HTTP response details - * @param num_coins number of fresh coins created, length of the - * @a sigs and @a coin_privs arrays, 0 if the operation - * failed. - * @param coin_privs array of @a num_coins private keys for the - * coins that were created, NULL on error. - * @param sigs array of signature over @a num_coins coins, - * NULL on error. + * @param rr HTTP response details */ static void reveal_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - unsigned int num_coins, - const struct TALER_PlanchetSecretsP *coin_privs, - const struct TALER_DenominationSignature *sigs) + const struct TALER_EXCHANGE_RevealResult *rr) { struct RefreshRevealState *rrs = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr; const struct TALER_TESTING_Command *melt_cmd; rrs->rrh = NULL; @@ -370,24 +405,16 @@ reveal_cb (void *cls, MAX_BACKOFF); rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff, rrs->backoff); - rrs->is->commands[rrs->is->ip].num_tries++; + TALER_TESTING_inc_tries (rrs->is); rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff, &do_reveal_retry, rrs); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - rrs->is->commands[rrs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rrs->is); + TALER_TESTING_unexpected_status (rrs->is, + hr->http_status, + rrs->expected_response_code); return; } melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is, @@ -398,16 +425,22 @@ reveal_cb (void *cls, TALER_TESTING_interpreter_fail (rrs->is); return; } - rrs->num_fresh_coins = num_coins; switch (hr->http_status) { case MHD_HTTP_OK: - rrs->fresh_coins = GNUNET_new_array (num_coins, + rrs->num_fresh_coins = rr->details.ok.num_coins; + rrs->psa = GNUNET_new_array (rrs->num_fresh_coins, + struct TALER_PlanchetMasterSecretP); + rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins, struct TALER_TESTING_FreshCoinData); - for (unsigned int i = 0; i<num_coins; i++) + for (unsigned int i = 0; i<rrs->num_fresh_coins; i++) { + const struct TALER_EXCHANGE_RevealedCoinInfo *coin + = &rr->details.ok.coins[i]; struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i]; + rrs->psa[i] = coin->ps; + fc->blinding_key = coin->bks; if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub (melt_cmd, i, @@ -417,18 +450,25 @@ reveal_cb (void *cls, TALER_TESTING_interpreter_fail (rrs->is); return; } - fc->coin_priv = coin_privs[i].coin_priv; - fc->blinding_key = coin_privs[i].blinding_key; - fc->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup - (sigs[i].rsa_signature); + fc->coin_priv = coin->coin_priv; + + if (NULL != coin->age_commitment_proof) + { + fc->age_commitment_proof = + TALER_age_commitment_proof_duplicate (coin->age_commitment_proof); + fc->h_age_commitment = coin->h_age_commitment; + } + + TALER_denom_sig_copy (&fc->sig, + &coin->sig); } if (0 != rrs->total_backoff.rel_value_us) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total reveal backoff for %s was %s\n", - rrs->is->commands[rrs->is->ip].label, + rrs->cmd->label, GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff, - GNUNET_YES)); + true)); } break; default: @@ -449,6 +489,19 @@ reveal_cb (void *cls, * @param is the interpreter state. */ static void +melt_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is); + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void refresh_reveal_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) @@ -457,6 +510,7 @@ refresh_reveal_run (void *cls, struct RefreshMeltState *rms; const struct TALER_TESTING_Command *melt_cmd; + rrs->cmd = cmd; rrs->is = is; melt_cmd = TALER_TESTING_interpreter_lookup_command (is, rrs->melt_reference); @@ -466,14 +520,24 @@ refresh_reveal_run (void *cls, TALER_TESTING_interpreter_fail (rrs->is); return; } + GNUNET_assert (melt_cmd->run == &melt_run); rms = melt_cmd->cls; - rrs->rrh = TALER_EXCHANGE_refreshes_reveal (is->exchange, - rms->refresh_data_length, - rms->refresh_data, - rms->noreveal_index, - &reveal_cb, - rrs); - + { + struct TALER_ExchangeWithdrawValues alg_values[rms->num_fresh_coins]; + + for (unsigned int i = 0; i<rms->num_fresh_coins; i++) + alg_values[i] = rms->mbds[i].alg_value; + rrs->rrh = TALER_EXCHANGE_refreshes_reveal ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + &rms->rms, + &rms->refresh_data, + rms->num_fresh_coins, + alg_values, + rms->noreveal_index, + &reveal_cb, + rrs); + } if (NULL == rrs->rrh) { GNUNET_break (0); @@ -496,12 +560,11 @@ refresh_reveal_cleanup (void *cls, { struct RefreshRevealState *rrs = cls; + (void) cmd; if (NULL != rrs->rrh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rrs->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (rrs->is, + cmd->label); TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh); rrs->rrh = NULL; } @@ -512,10 +575,14 @@ refresh_reveal_cleanup (void *cls, } for (unsigned int j = 0; j < rrs->num_fresh_coins; j++) - GNUNET_CRYPTO_rsa_signature_free (rrs->fresh_coins[j].sig.rsa_signature); + { + TALER_denom_sig_free (&rrs->fresh_coins[j].sig); + TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof); + GNUNET_free (rrs->fresh_coins[j].age_commitment_proof); + } GNUNET_free (rrs->fresh_coins); - rrs->fresh_coins = NULL; + GNUNET_free (rrs->psa); rrs->num_fresh_coins = 0; GNUNET_free (rrs); } @@ -545,8 +612,7 @@ do_link_retry (void *cls) struct RefreshLinkState *rls = cls; rls->retry_task = NULL; - rls->is->commands[rls->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (rls->is); refresh_link_run (rls, NULL, rls->is); @@ -559,29 +625,15 @@ do_link_retry (void *cls) * withdrawn by the "refresh reveal" CMD. * * @param cls closure. - * @param hr HTTP response details - * @param num_coins number of fresh coins created, length of the - * @a sigs and @a coin_privs arrays, 0 if the operation - * failed. - * @param coin_privs array of @a num_coins private keys for the - * coins that were created, NULL on error. - * @param sigs array of signature over @a num_coins coins, NULL on - * error. - * @param pubs array of public keys for the @a sigs, - * NULL on error. + * @param lr HTTP response details */ static void link_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - unsigned int num_coins, - const struct TALER_CoinSpendPrivateKeyP *coin_privs, - const struct TALER_DenominationSignature *sigs, - const struct TALER_DenominationPublicKey *pubs) + const struct TALER_EXCHANGE_LinkResult *lr) { - struct RefreshLinkState *rls = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &lr->hr; const struct TALER_TESTING_Command *reveal_cmd; - struct TALER_TESTING_Command *link_cmd = &rls->is->commands[rls->is->ip]; unsigned int found; const unsigned int *num_fresh_coins; @@ -607,24 +659,16 @@ link_cb (void *cls, MAX_BACKOFF); rls->total_backoff = GNUNET_TIME_relative_add (rls->total_backoff, rls->backoff); - rls->is->commands[rls->is->ip].num_tries++; + TALER_TESTING_inc_tries (rls->is); rls->retry_task = GNUNET_SCHEDULER_add_delayed (rls->backoff, &do_link_retry, rls); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - link_cmd->label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rls->is); + TALER_TESTING_unexpected_status (rls->is, + hr->http_status, + rls->expected_response_code); return; } reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is, @@ -641,19 +685,18 @@ link_cb (void *cls, case MHD_HTTP_OK: /* check that number of coins returned matches */ if (GNUNET_OK != - TALER_TESTING_get_trait_uint (reveal_cmd, - 0, - &num_fresh_coins)) + TALER_TESTING_get_trait_array_length (reveal_cmd, + &num_fresh_coins)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rls->is); return; } - if (num_coins != *num_fresh_coins) + if (lr->details.ok.num_coins != *num_fresh_coins) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected number of fresh coins: %d vs %d in %s:%u\n", - num_coins, + lr->details.ok.num_coins, *num_fresh_coins, __FILE__, __LINE__); @@ -661,22 +704,21 @@ link_cb (void *cls, return; } /* check that the coins match */ - for (unsigned int i = 0; i<num_coins; i++) - for (unsigned int j = i + 1; j<num_coins; j++) + for (unsigned int i = 0; i<lr->details.ok.num_coins; i++) + for (unsigned int j = i + 1; j<lr->details.ok.num_coins; j++) if (0 == - GNUNET_memcmp (&coin_privs[i], - &coin_privs[j])) + GNUNET_memcmp (&lr->details.ok.coins[i].coin_priv, + &lr->details.ok.coins[j].coin_priv)) GNUNET_break (0); /* Note: coins might be legitimately permutated in here... */ found = 0; /* Will point to the pointer inside the cmd state. */ { - const struct TALER_TESTING_FreshCoinData *fc = NULL; + const struct TALER_TESTING_FreshCoinData **fc = NULL; if (GNUNET_OK != TALER_TESTING_get_trait_fresh_coins (reveal_cmd, - 0, &fc)) { GNUNET_break (0); @@ -684,29 +726,38 @@ link_cb (void *cls, return; } - for (unsigned int i = 0; i<num_coins; i++) - for (unsigned int j = 0; j<num_coins; j++) + for (unsigned int i = 0; i<lr->details.ok.num_coins; i++) + { + const struct TALER_EXCHANGE_LinkedCoinInfo *lci_i + = &lr->details.ok.coins[i]; + + for (unsigned int j = 0; j<lr->details.ok.num_coins; j++) { + const struct TALER_TESTING_FreshCoinData *fcj + = &(*fc)[j]; + if ( (0 == - GNUNET_memcmp (&coin_privs[i], - &fc[j].coin_priv)) && + GNUNET_memcmp (&fcj->coin_priv, + &lci_i->coin_priv)) && (0 == - GNUNET_CRYPTO_rsa_signature_cmp (fc[i].sig.rsa_signature, - sigs[j].rsa_signature)) && + TALER_denom_sig_cmp (&fcj->sig, + &lci_i->sig)) && (0 == - GNUNET_CRYPTO_rsa_public_key_cmp (fc[i].pk->key.rsa_public_key, - pubs[j].rsa_public_key)) ) + TALER_denom_pub_cmp (&fcj->pk->key, + &lci_i->pub)) ) { found++; break; } - } + } /* for j*/ + } /* for i */ } - if (found != num_coins) + if (found != lr->details.ok.num_coins) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Only %u/%u coins match expectations\n", - found, num_coins); + found, + lr->details.ok.num_coins); GNUNET_break (0); TALER_TESTING_interpreter_fail (rls->is); return; @@ -715,9 +766,9 @@ link_cb (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total link backoff for %s was %s\n", - rls->is->commands[rls->is->ip].label, + rls->cmd->label, GNUNET_STRINGS_relative_time_to_string (rls->total_backoff, - GNUNET_YES)); + true)); } break; default: @@ -748,8 +799,16 @@ refresh_link_run (void *cls, const struct TALER_TESTING_Command *reveal_cmd; const struct TALER_TESTING_Command *melt_cmd; const struct TALER_TESTING_Command *coin_cmd; + const char *exchange_url; + rls->cmd = cmd; rls->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is, rls->reveal_reference); if (NULL == reveal_cmd) @@ -769,21 +828,22 @@ refresh_link_run (void *cls, } /* find reserve_withdraw command */ + GNUNET_assert (melt_cmd->run == &melt_run); + rms = melt_cmd->cls; + coin_cmd = TALER_TESTING_interpreter_lookup_command (rls->is, + rms->coin_reference); + if (NULL == coin_cmd) { - rms = melt_cmd->cls; - coin_cmd = TALER_TESTING_interpreter_lookup_command (rls->is, - rms->coin_reference); - if (NULL == coin_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } + GNUNET_break (0); + TALER_TESTING_interpreter_fail (rls->is); + return; } const struct TALER_CoinSpendPrivateKeyP *coin_priv; - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv - (coin_cmd, 0, &coin_priv)) + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_cmd, + 0, + &coin_priv)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rls->is); @@ -791,10 +851,13 @@ refresh_link_run (void *cls, } /* finally, use private key from withdraw sign command */ - rls->rlh = TALER_EXCHANGE_link (is->exchange, - coin_priv, - &link_cb, - rls); + rls->rlh = TALER_EXCHANGE_link ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + coin_priv, + rms->refresh_data.melt_age_commitment_proof, + &link_cb, + rls); if (NULL == rls->rlh) { @@ -820,11 +883,8 @@ refresh_link_cleanup (void *cls, if (NULL != rls->rlh) { - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rls->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (rls->is, + cmd->label); TALER_EXCHANGE_link_cancel (rls->rlh); rls->rlh = NULL; } @@ -838,19 +898,6 @@ refresh_link_cleanup (void *cls, /** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -melt_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** * Task scheduled to re-try #melt_run. * * @param cls a `struct RefreshMeltState` @@ -861,8 +908,7 @@ do_melt_retry (void *cls) struct RefreshMeltState *rms = cls; rms->retry_task = NULL; - rms->is->commands[rms->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (rms->is); melt_run (rms, NULL, rms->is); @@ -875,18 +921,14 @@ do_melt_retry (void *cls) * CMD was set to do so. * * @param cls closure. - * @param hr HTTP response details - * @param noreveal_index choice by the exchange in the - * cut-and-choose protocol, UINT16_MAX on error. - * @param exchange_pub public key the exchange used for signing. + * @param mr melt response details */ static void melt_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - uint32_t noreveal_index, - const struct TALER_ExchangePublicKeyP *exchange_pub) + const struct TALER_EXCHANGE_MeltResponse *mr) { struct RefreshMeltState *rms = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; rms->rmh = NULL; if (rms->expected_response_code != hr->http_status) @@ -910,45 +952,57 @@ melt_cb (void *cls, MAX_BACKOFF); rms->total_backoff = GNUNET_TIME_relative_add (rms->total_backoff, rms->backoff); - rms->is->commands[rms->is->ip].num_tries++; + TALER_TESTING_inc_tries (rms->is); rms->retry_task = GNUNET_SCHEDULER_add_delayed (rms->backoff, &do_melt_retry, rms); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - rms->is->commands[rms->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rms->is); + TALER_TESTING_unexpected_status_with_body (rms->is, + hr->http_status, + rms->expected_response_code, + hr->reply); return; } - rms->noreveal_index = noreveal_index; + if (MHD_HTTP_OK == hr->http_status) + { + rms->noreveal_index = mr->details.ok.noreveal_index; + if (mr->details.ok.num_mbds != rms->num_fresh_coins) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (rms->is); + return; + } + GNUNET_free (rms->mbds); + rms->mbds = GNUNET_new_array ( + mr->details.ok.num_mbds, + struct TALER_EXCHANGE_MeltBlindingDetail); + for (unsigned int i = 0; i<mr->details.ok.num_mbds; i++) + TALER_denom_ewv_copy (&rms->mbds[i].alg_value, + &mr->details.ok.mbds[i].alg_value); + } if (0 != rms->total_backoff.rel_value_us) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total melt backoff for %s was %s\n", - rms->is->commands[rms->is->ip].label, + rms->cmd->label, GNUNET_STRINGS_relative_time_to_string (rms->total_backoff, - GNUNET_YES)); + true)); } - if (GNUNET_YES == rms->double_melt) + if (rms->double_melt) { TALER_LOG_DEBUG ("Doubling the melt (%s)\n", - rms->is->commands[rms->is->ip].label); - rms->rmh = TALER_EXCHANGE_melt (rms->is->exchange, - rms->refresh_data_length, - rms->refresh_data, - &melt_cb, - rms); - rms->double_melt = GNUNET_NO; + rms->cmd->label); + rms->rmh = TALER_EXCHANGE_melt ( + TALER_TESTING_interpreter_get_context (rms->is), + TALER_TESTING_get_exchange_url (rms->is), + TALER_TESTING_get_keys (rms->is), + &rms->rms, + &rms->refresh_data, + &melt_cb, + rms); + rms->double_melt = false; return; } TALER_TESTING_interpreter_next (rms->is); @@ -975,36 +1029,53 @@ melt_run (void *cls, }; const char **melt_fresh_amounts; + rms->cmd = cmd; if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts)) melt_fresh_amounts = default_melt_fresh_amounts; rms->is = is; rms->noreveal_index = UINT16_MAX; + TALER_refresh_master_setup_random (&rms->rms); for (num_fresh_coins = 0; NULL != melt_fresh_amounts[num_fresh_coins]; num_fresh_coins++) ; rms->num_fresh_coins = num_fresh_coins; - rms->fresh_pks = GNUNET_new_array - (num_fresh_coins, - struct TALER_EXCHANGE_DenomPublicKey); + rms->fresh_pks = GNUNET_new_array ( + num_fresh_coins, + struct TALER_EXCHANGE_DenomPublicKey); { struct TALER_Amount melt_amount; struct TALER_Amount fresh_amount; + const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; + const struct TALER_AgeCommitmentHash *h_age_commitment = NULL; const struct TALER_DenominationSignature *melt_sig; const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; const struct TALER_TESTING_Command *coin_command; + bool age_restricted_denom; if (NULL == (coin_command - = TALER_TESTING_interpreter_lookup_command - (is, rms->coin_reference))) + = TALER_TESTING_interpreter_lookup_command ( + is, + rms->coin_reference))) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rms->is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv - (coin_command, 0, &rms->melt_priv)) + if (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_command, + 0, + &rms->melt_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (rms->is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (coin_command, + 0, + &age_commitment_proof)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rms->is); @@ -1012,6 +1083,15 @@ melt_run (void *cls, } if (GNUNET_OK != + TALER_TESTING_get_trait_h_age_commitment (coin_command, + 0, + &h_age_commitment)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (rms->is); + return; + } + if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig (coin_command, 0, &melt_sig)) @@ -1020,32 +1100,42 @@ melt_run (void *cls, TALER_TESTING_interpreter_fail (rms->is); return; } - if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub - (coin_command, 0, &melt_denom_pub)) + if (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (coin_command, + 0, + &melt_denom_pub)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (rms->is); return; } + /* Melt amount starts with the melt fee of the old coin; we'll add the values and withdraw fees of the fresh coins next */ - melt_amount = melt_denom_pub->fee_refresh; + melt_amount = melt_denom_pub->fees.refresh; + age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0; + GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof)); + GNUNET_assert ((NULL == age_commitment_proof) || + (0 < age_commitment_proof->commitment.num)); for (unsigned int i = 0; i<num_fresh_coins; i++) { const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; - if (GNUNET_OK != TALER_string_to_amount - (melt_fresh_amounts[i], &fresh_amount)) + if (GNUNET_OK != + TALER_string_to_amount (melt_fresh_amounts[i], + &fresh_amount)) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse amount `%s' at index %u\n", - melt_fresh_amounts[i], i); + melt_fresh_amounts[i], + i); TALER_TESTING_interpreter_fail (rms->is); return; } - fresh_pk = TALER_TESTING_find_pk - (TALER_EXCHANGE_get_keys (is->exchange), &fresh_amount); + fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is), + &fresh_amount, + age_restricted_denom); if (NULL == fresh_pk) { GNUNET_break (0); @@ -1060,32 +1150,49 @@ melt_run (void *cls, GNUNET_assert (0 <= TALER_amount_add (&melt_amount, &melt_amount, - &fresh_pk->fee_withdraw)); + &fresh_pk->fees.withdraw)); rms->fresh_pks[i] = *fresh_pk; /* Make a deep copy of the RSA key */ - rms->fresh_pks[i].key.rsa_public_key - = GNUNET_CRYPTO_rsa_public_key_dup (fresh_pk->key.rsa_public_key); - } - rms->refresh_data - = TALER_EXCHANGE_refresh_prepare (rms->melt_priv, - &melt_amount, - melt_sig, - melt_denom_pub, - num_fresh_coins, - rms->fresh_pks, - &rms->refresh_data_length); - - if (NULL == rms->refresh_data) + TALER_denom_pub_copy (&rms->fresh_pks[i].key, + &fresh_pk->key); + } /* end for */ + + rms->refresh_data.melt_priv = *rms->melt_priv; + GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv, + &rms->melt_pub.eddsa_pub); + rms->refresh_data.melt_amount = melt_amount; + rms->refresh_data.melt_sig = *melt_sig; + rms->refresh_data.melt_pk = *melt_denom_pub; + + if (NULL != age_commitment_proof) { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; + GNUNET_assert (NULL != h_age_commitment); + rms->refresh_data.melt_age_commitment_proof = age_commitment_proof; + rms->refresh_data.melt_h_age_commitment = h_age_commitment; } - rms->rmh = TALER_EXCHANGE_melt (is->exchange, - rms->refresh_data_length, - rms->refresh_data, - &melt_cb, - rms); + rms->refresh_data.fresh_pks = rms->fresh_pks; + rms->refresh_data.fresh_pks_len = num_fresh_coins; + + GNUNET_assert (age_restricted_denom == + (NULL != age_commitment_proof)); + GNUNET_assert ((NULL == age_commitment_proof) || + (0 < age_commitment_proof->commitment.num)); + + rms->che.type = TALER_EXCHANGE_CTT_MELT; + rms->che.amount = melt_amount; + if (NULL != age_commitment_proof) + rms->che.details.melt.h_age_commitment = *h_age_commitment; + else + rms->che.details.melt.no_hac = true; + + rms->rmh = TALER_EXCHANGE_melt ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + &rms->rms, + &rms->refresh_data, + &melt_cb, + rms); if (NULL == rms->rmh) { @@ -1110,11 +1217,11 @@ melt_cleanup (void *cls, { struct RefreshMeltState *rms = cls; + (void) cmd; if (NULL != rms->rmh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rms->is->ip, rms->is->commands[rms->is->ip].label); + TALER_TESTING_command_incomplete (rms->is, + cmd->label); TALER_EXCHANGE_melt_cancel (rms->rmh); rms->rmh = NULL; } @@ -1126,13 +1233,15 @@ melt_cleanup (void *cls, if (NULL != rms->fresh_pks) { for (unsigned int i = 0; i < rms->num_fresh_coins; i++) - GNUNET_CRYPTO_rsa_public_key_free (rms->fresh_pks[i].key.rsa_public_key); + TALER_denom_pub_free (&rms->fresh_pks[i].key); + GNUNET_free (rms->fresh_pks); + } + if (NULL != rms->mbds) + { + for (unsigned int i = 0; i < rms->num_fresh_coins; i++) + TALER_denom_ewv_free (&rms->mbds[i].alg_value); + GNUNET_free (rms->mbds); } - GNUNET_free (rms->fresh_pks); - rms->fresh_pks = NULL; - GNUNET_free (rms->refresh_data); - rms->refresh_data = NULL; - rms->refresh_data_length = 0; GNUNET_free (rms->melt_fresh_amounts); GNUNET_free (rms); } @@ -1147,7 +1256,7 @@ melt_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue melt_traits (void *cls, const void **ret, const char *trait, @@ -1162,8 +1271,25 @@ melt_traits (void *cls, } { struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_denom_pub (index, &rms->fresh_pks[index]), - TALER_TESTING_make_trait_coin_priv (0, rms->melt_priv), + TALER_TESTING_make_trait_denom_pub (index, + &rms->fresh_pks[index]), + TALER_TESTING_make_trait_coin_priv (0, + rms->melt_priv), + TALER_TESTING_make_trait_coin_pub (0, + &rms->melt_pub), + TALER_TESTING_make_trait_coin_history (0, + &rms->che), + TALER_TESTING_make_trait_age_commitment_proof ( + index, + rms->refresh_data.melt_age_commitment_proof), + TALER_TESTING_make_trait_h_age_commitment ( + index, + rms->refresh_data.melt_h_age_commitment), + TALER_TESTING_make_trait_refresh_secret (&rms->rms), + (NULL != rms->mbds) + ? TALER_TESTING_make_trait_exchange_wd_value (index, + &rms->mbds[index].alg_value) + : TALER_TESTING_trait_end (), TALER_TESTING_trait_end () }; @@ -1182,7 +1308,7 @@ melt_traits (void *cls, * @param ap NULL-termianted list of amounts to be melted (one per fresh coin) * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue parse_amounts (struct RefreshMeltState *rms, va_list ap) { @@ -1225,16 +1351,6 @@ parse_amounts (struct RefreshMeltState *rms, } -/** - * Create a "refresh melt" command. - * - * @param label command label. - * @param coin_reference reference to a command - * that will provide a coin to refresh. - * @param expected_response_code expected HTTP code. - * @param ... NULL-terminated list of amounts to be melted - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_melt (const char *label, const char *coin_reference, @@ -1247,7 +1363,8 @@ TALER_TESTING_cmd_melt (const char *label, rms = GNUNET_new (struct RefreshMeltState); rms->coin_reference = coin_reference; rms->expected_response_code = expected_response_code; - va_start (ap, expected_response_code); + va_start (ap, + expected_response_code); GNUNET_assert (GNUNET_OK == parse_amounts (rms, ap)); va_end (ap); @@ -1265,18 +1382,6 @@ TALER_TESTING_cmd_melt (const char *label, } -/** - * Create a "refresh melt" CMD that does TWO /refresh/melt - * requests. This was needed to test the replay of a valid melt - * request, see #5312. - * - * @param label command label - * @param coin_reference reference to a command that will provide - * a coin to refresh - * @param expected_response_code expected HTTP code - * @param ... NULL-terminated list of amounts to be melted - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_melt_double (const char *label, const char *coin_reference, @@ -1289,8 +1394,9 @@ TALER_TESTING_cmd_melt_double (const char *label, rms = GNUNET_new (struct RefreshMeltState); rms->coin_reference = coin_reference; rms->expected_response_code = expected_response_code; - rms->double_melt = GNUNET_YES; - va_start (ap, expected_response_code); + rms->double_melt = true; + va_start (ap, + expected_response_code); GNUNET_assert (GNUNET_OK == parse_amounts (rms, ap)); va_end (ap); @@ -1308,12 +1414,6 @@ TALER_TESTING_cmd_melt_double (const char *label, } -/** - * Modify a "refresh melt" command to enable retries. - * - * @param cmd command - * @return modified command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd) { @@ -1335,65 +1435,54 @@ TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd) * @param index index number of the object to offer. * @return #GNUNET_OK on success. */ -static int +static enum GNUNET_GenericReturnValue refresh_reveal_traits (void *cls, const void **ret, const char *trait, unsigned int index) { struct RefreshRevealState *rrs = cls; - unsigned int num_coins = rrs->num_fresh_coins; -#define NUM_TRAITS ((num_coins * 4) + 3) - struct TALER_TESTING_Trait traits[NUM_TRAITS]; - - /* Making coin privs traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[i] = TALER_TESTING_make_trait_coin_priv - (i, &rrs->fresh_coins[i].coin_priv); - - /* Making denom pubs traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[num_coins + i] - = TALER_TESTING_make_trait_denom_pub - (i, rrs->fresh_coins[i].pk); - - /* Making denom sigs traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[(num_coins * 2) + i] - = TALER_TESTING_make_trait_denom_sig - (i, &rrs->fresh_coins[i].sig); - /* blinding key traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[(num_coins * 3) + i] - = TALER_TESTING_make_trait_blinding_key (i, - &rrs->fresh_coins[i].blinding_key), - - /* number of fresh coins */ - traits[(num_coins * 4)] = TALER_TESTING_make_trait_uint - (0, &rrs->num_fresh_coins); - - /* whole array of fresh coins */ - traits[(num_coins * 4) + 1] - = TALER_TESTING_make_trait_fresh_coins (0, rrs->fresh_coins), - - /* end of traits */ - traits[(num_coins * 4) + 2] = TALER_TESTING_trait_end (); - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); + + if (index >= rrs->num_fresh_coins) + return GNUNET_SYSERR; + + { + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_coin_priv ( + index, + &rrs->fresh_coins[index].coin_priv), + TALER_TESTING_make_trait_age_commitment_proof ( + index, + rrs->fresh_coins[index].age_commitment_proof), + TALER_TESTING_make_trait_h_age_commitment ( + index, + &rrs->fresh_coins[index].h_age_commitment), + TALER_TESTING_make_trait_denom_pub ( + index, + rrs->fresh_coins[index].pk), + TALER_TESTING_make_trait_denom_sig ( + index, + &rrs->fresh_coins[index].sig), + TALER_TESTING_make_trait_blinding_key ( + index, + &rrs->fresh_coins[index].blinding_key), + TALER_TESTING_make_trait_array_length ( + &rrs->num_fresh_coins), + TALER_TESTING_make_trait_fresh_coins ( + (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins), + TALER_TESTING_make_trait_planchet_secrets (index, + &rrs->psa[index]), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); + } } -/** - * Create a "refresh reveal" command. - * - * @param label command label. - * @param melt_reference reference to a "refresh melt" command. - * @param expected_response_code expected HTTP response code. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_refresh_reveal (const char *label, const char *melt_reference, @@ -1418,12 +1507,6 @@ TALER_TESTING_cmd_refresh_reveal (const char *label, } -/** - * Modify a "refresh reveal" command to enable retries. - * - * @param cmd command - * @return modified command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_refresh_reveal_with_retry (struct TALER_TESTING_Command cmd) { @@ -1436,14 +1519,6 @@ TALER_TESTING_cmd_refresh_reveal_with_retry (struct TALER_TESTING_Command cmd) } -/** - * Create a "refresh link" command. - * - * @param label command label. - * @param reveal_reference reference to a "refresh reveal" CMD. - * @param expected_response_code expected HTTP response code - * @return the "refresh link" command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_refresh_link (const char *label, const char *reveal_reference, @@ -1467,12 +1542,6 @@ TALER_TESTING_cmd_refresh_link (const char *label, } -/** - * Modify a "refresh link" command to enable retries. - * - * @param cmd command - * @return modified command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_refresh_link_with_retry (struct TALER_TESTING_Command cmd) { diff --git a/src/testing/testing_api_cmd_refund.c b/src/testing/testing_api_cmd_refund.c index 30f257696..29b68ef08 100644 --- a/src/testing/testing_api_cmd_refund.c +++ b/src/testing/testing_api_cmd_refund.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -54,9 +54,14 @@ struct RefundState uint64_t refund_transaction_id; /** - * Connection to the exchange. + * Entry in the coin's history generated by this operation. */ - struct TALER_EXCHANGE_Handle *exchange; + struct TALER_EXCHANGE_CoinHistoryEntry che; + + /** + * Public key of the refunded coin. + */ + struct TALER_CoinSpendPublicKeyP coin; /** * Handle to the refund operation. @@ -75,38 +80,51 @@ struct RefundState * response code is acceptable. * * @param cls closure - * @param hr HTTP response details - * @param exchange_pub public key the exchange - * used for signing @a obj. - * @param exchange_sig actual signature confirming the refund + * @param rr response details */ static void refund_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const struct TALER_ExchangeSignatureP *exchange_sig) + const struct TALER_EXCHANGE_RefundResponse *rr) { - struct RefundState *rs = cls; - struct TALER_TESTING_Command *refund_cmd; + const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr; - refund_cmd = &rs->is->commands[rs->is->ip]; rs->rh = NULL; if (rs->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - hr->ec, - refund_cmd->label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rs->is); + TALER_TESTING_unexpected_status (rs->is, + hr->http_status, + rs->expected_response_code); return; } + if (MHD_HTTP_OK == hr->http_status) + { + struct TALER_Amount refund_amount; + + if (GNUNET_OK != + TALER_string_to_amount (rs->refund_amount, + &refund_amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s'\n", + rs->refund_amount); + TALER_TESTING_interpreter_fail (rs->is); + return; + } + if (0 > + TALER_amount_subtract (&rs->che.amount, + &refund_amount, + &rs->che.details.refund.refund_fee)) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to subtract %s from %s\n", + TALER_amount2s (&rs->che.details.refund.refund_fee), + rs->refund_amount); + TALER_TESTING_interpreter_fail (rs->is); + return; + } + } TALER_TESTING_interpreter_next (rs->is); } @@ -125,24 +143,21 @@ refund_run (void *cls, { struct RefundState *rs = cls; const struct TALER_CoinSpendPrivateKeyP *coin_priv; - struct TALER_CoinSpendPublicKeyP coin; const json_t *contract_terms; - struct GNUNET_HashCode h_contract_terms; + struct TALER_PrivateContractHashP h_contract_terms; struct TALER_Amount refund_amount; const struct TALER_MerchantPrivateKeyP *merchant_priv; const struct TALER_TESTING_Command *coin_cmd; + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; - rs->exchange = is->exchange; rs->is = is; - if (GNUNET_OK != TALER_string_to_amount (rs->refund_amount, &refund_amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u/%s\n", + "Failed to parse amount `%s' at %s\n", rs->refund_amount, - is->ip, cmd->label); TALER_TESTING_interpreter_fail (is); return; @@ -157,7 +172,6 @@ refund_run (void *cls, } if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms (coin_cmd, - 0, &contract_terms)) { GNUNET_break (0); @@ -169,10 +183,15 @@ refund_run (void *cls, &h_contract_terms)); /* Hunting for a coin .. */ - if (GNUNET_OK != - TALER_TESTING_get_trait_coin_priv (coin_cmd, - 0, - &coin_priv)) + if ( (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (coin_cmd, + 0, + &coin_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (coin_cmd, + 0, + &denom_pub)) ) + { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); @@ -180,29 +199,76 @@ refund_run (void *cls, } GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin.eddsa_pub); + &rs->coin.eddsa_pub); if (GNUNET_OK != TALER_TESTING_get_trait_merchant_priv (coin_cmd, - 0, &merchant_priv)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - rs->rh = TALER_EXCHANGE_refund (rs->exchange, - &refund_amount, - &h_contract_terms, - &coin, - rs->refund_transaction_id, - merchant_priv, - &refund_cb, - rs); + rs->che.type = TALER_EXCHANGE_CTT_REFUND; + rs->che.details.refund.h_contract_terms = h_contract_terms; + GNUNET_CRYPTO_eddsa_key_get_public ( + &merchant_priv->eddsa_priv, + &rs->che.details.refund.merchant_pub.eddsa_pub); + rs->che.details.refund.refund_fee = denom_pub->fees.refund; + rs->che.details.refund.sig_amount = refund_amount; + rs->che.details.refund.rtransaction_id = rs->refund_transaction_id; + TALER_merchant_refund_sign (&rs->coin, + &h_contract_terms, + rs->refund_transaction_id, + &refund_amount, + merchant_priv, + &rs->che.details.refund.sig); + rs->rh = TALER_EXCHANGE_refund ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + &refund_amount, + &h_contract_terms, + &rs->coin, + rs->refund_transaction_id, + merchant_priv, + &refund_cb, + rs); GNUNET_assert (NULL != rs->rh); } /** + * Offer internal data from a "refund" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +refund_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct RefundState *rs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_coin_history (0, + &rs->che), + TALER_TESTING_make_trait_coin_pub (0, + &rs->coin), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** * Free the state from a "refund" CMD, and possibly cancel * a pending operation thereof. * @@ -217,10 +283,8 @@ refund_cleanup (void *cls, if (NULL != rs->rh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rs->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (rs->is, + cmd->label); TALER_EXCHANGE_refund_cancel (rs->rh); rs->rh = NULL; } @@ -228,17 +292,6 @@ refund_cleanup (void *cls, } -/** - * Create a "refund" command. - * - * @param label command label. - * @param expected_response_code expected HTTP status code. - * @param refund_amount the amount to ask a refund for. - * @param coin_reference reference to a command that can - * provide a coin to be refunded. - * - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_refund (const char *label, unsigned int expected_response_code, @@ -248,7 +301,6 @@ TALER_TESTING_cmd_refund (const char *label, struct RefundState *rs; rs = GNUNET_new (struct RefundState); - rs->expected_response_code = expected_response_code; rs->refund_amount = refund_amount; rs->coin_reference = coin_reference; @@ -257,7 +309,8 @@ TALER_TESTING_cmd_refund (const char *label, .cls = rs, .label = label, .run = &refund_run, - .cleanup = &refund_cleanup + .cleanup = &refund_cleanup, + .traits = &refund_traits }; return cmd; @@ -265,23 +318,9 @@ TALER_TESTING_cmd_refund (const char *label, } -/** - * Create a "refund" command, allow to specify refund transaction - * id. Mainly used to create conflicting requests. - * - * @param label command label. - * @param expected_response_code expected HTTP status code. - * @param refund_amount the amount to ask a refund for. - * @param coin_reference reference to a command that can - * provide a coin to be refunded. - * @param refund_transaction_id transaction id to use - * in the request. - * - * @return the command. - */ struct TALER_TESTING_Command -TALER_TESTING_cmd_refund_with_id - (const char *label, +TALER_TESTING_cmd_refund_with_id ( + const char *label, unsigned int expected_response_code, const char *refund_amount, const char *coin_reference, @@ -299,7 +338,8 @@ TALER_TESTING_cmd_refund_with_id .cls = rs, .label = label, .run = &refund_run, - .cleanup = &refund_cleanup + .cleanup = &refund_cleanup, + .traits = &refund_traits }; return cmd; diff --git a/src/testing/testing_api_cmd_reserve_attest.c b/src/testing/testing_api_cmd_reserve_attest.c new file mode 100644 index 000000000..cf4b3a0c2 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_attest.c @@ -0,0 +1,263 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_attest.c + * @brief Implement the /reserve/$RID/attest test command. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + +/** + * State for a "attest" CMD. + */ +struct AttestState +{ + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve attest" operation. + */ + struct TALER_EXCHANGE_ReservesAttestHandle *rsh; + + /** + * Private key of the reserve being analyzed. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Array of attributes to request, of length @e attrs_len. + */ + const char **attrs; + + /** + * Length of the @e attrs array. + */ + unsigned int attrs_len; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /* TODO: expose fields below as traits... */ + + /** + * Attested attributes returned by the exchange. + */ + json_t *attributes; + + /** + * Expiration time of the attested attributes. + */ + struct GNUNET_TIME_Timestamp expiration_time; + + /** + * Signature by the exchange affirming the attributes. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Online signing key used by the exchange. + */ + struct TALER_ExchangePublicKeyP exchange_pub; +}; + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_attest_cb ( + void *cls, + const struct TALER_EXCHANGE_ReservePostAttestResult *rs) +{ + struct AttestState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + ss->attributes = json_incref ((json_t*) rs->details.ok.attributes); + ss->expiration_time = rs->details.ok.expiration_time; + ss->exchange_pub = rs->details.ok.exchange_pub; + ss->exchange_sig = rs->details.ok.exchange_sig; + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +attest_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AttestState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + const char *exchange_url; + + ss->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &ss->reserve_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_priv for attest query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + ss->rsh = TALER_EXCHANGE_reserves_attest ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + TALER_TESTING_get_keys (is), + ss->reserve_priv, + ss->attrs_len, + ss->attrs, + &reserve_attest_cb, + ss); +} + + +/** + * Cleanup the state from a "reserve attest" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +attest_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AttestState *ss = cls; + + if (NULL != ss->rsh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_reserves_attest_cancel (ss->rsh); + ss->rsh = NULL; + } + json_decref (ss->attributes); + GNUNET_free (ss->attrs); + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_attest (const char *label, + const char *reserve_reference, + unsigned int expected_response_code, + ...) +{ + struct AttestState *ss; + unsigned int num_args; + const char *ea; + va_list ap; + + num_args = 0; + va_start (ap, expected_response_code); + while (NULL != va_arg (ap, const char *)) + num_args++; + va_end (ap); + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct AttestState); + ss->reserve_reference = reserve_reference; + ss->expected_response_code = expected_response_code; + ss->attrs_len = num_args; + ss->attrs = GNUNET_new_array (num_args, + const char *); + num_args = 0; + va_start (ap, expected_response_code); + while (NULL != (ea = va_arg (ap, const char *))) + ss->attrs[num_args++] = ea; + va_end (ap); + + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &attest_run, + .cleanup = &attest_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_close.c b/src/testing/testing_api_cmd_reserve_close.c new file mode 100644 index 000000000..8e272f547 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_close.c @@ -0,0 +1,260 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_close.c + * @brief Implement the /reserve/$RID/close test command. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "close" CMD. + */ +struct CloseState +{ + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve close" operation. + */ + struct TALER_EXCHANGE_ReservesCloseHandle *rsh; + + /** + * payto://-URI where to wire the funds. + */ + const char *target_account; + + /** + * Private key of the reserve being analyzed. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Set to the KYC requirement payto hash *if* the exchange replied with a + * request for KYC. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC. + */ + uint64_t requirement_row; +}; + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_close_cb (void *cls, + const struct TALER_EXCHANGE_ReserveCloseResult *rs) +{ + struct CloseState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + switch (rs->hr.http_status) + { + case MHD_HTTP_OK: + break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* nothing to check */ + ss->requirement_row + = rs->details.unavailable_for_legal_reasons.requirement_row; + ss->h_payto + = rs->details.unavailable_for_legal_reasons.h_payto; + break; + default: + break; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +close_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct CloseState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + + ss->is = is; + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &ss->reserve_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_priv for close query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + ss->rsh = TALER_EXCHANGE_reserves_close ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + ss->reserve_priv, + ss->target_account, + &reserve_close_cb, + ss); +} + + +/** + * Cleanup the state from a "reserve close" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +close_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct CloseState *ss = cls; + + if (NULL != ss->rsh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_reserves_close_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +/** + * Offer internal data to a "close" CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +close_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct CloseState *cs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_legi_requirement_row ( + &cs->requirement_row), + TALER_TESTING_make_trait_h_payto ( + &cs->h_payto), + TALER_TESTING_trait_end () + }; + + if (cs->expected_response_code != MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS) + return GNUNET_NO; + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_close (const char *label, + const char *reserve_reference, + const char *target_account, + unsigned int expected_response_code) +{ + struct CloseState *ss; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct CloseState); + ss->reserve_reference = reserve_reference; + ss->target_account = target_account; + ss->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &close_run, + .cleanup = &close_cleanup, + .traits = &close_traits + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_get.c b/src/testing/testing_api_cmd_reserve_get.c new file mode 100644 index 000000000..9a938cf82 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_get.c @@ -0,0 +1,390 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_get.c + * @brief Implement the GET /reserve/$RID test command. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "poll" CMD. + */ +struct PollState +{ + + /** + * How long do we give the exchange to respond? + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *poll_reference; + + /** + * Timeout to wait for at most. + */ + struct GNUNET_SCHEDULER_Task *tt; + + /** + * The interpreter we are using. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * State for a "status" CMD. + */ +struct StatusState +{ + + /** + * How long do we give the exchange to respond? + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Poller waiting for us. + */ + struct PollState *ps; + + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve status" operation. + */ + struct TALER_EXCHANGE_ReservesGetHandle *rsh; + + /** + * Expected reserve balance. + */ + const char *expected_balance; + + /** + * Public key of the reserve being analyzed. + */ + const struct TALER_ReservePublicKeyP *reserve_pubp; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + +}; + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_status_cb (void *cls, + const struct TALER_EXCHANGE_ReserveSummary *rs) +{ + struct StatusState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + struct TALER_Amount eb; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK == ss->expected_response_code) + { + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ss->expected_balance, + &eb)); + if (0 != TALER_amount_cmp (&eb, + &rs->details.ok.balance)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected amount %s in reserve, wanted %s\n", + TALER_amount_to_string (&rs->details.ok.balance), + ss->expected_balance); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + if (NULL != ss->ps) + { + /* force continuation on long poller */ + GNUNET_SCHEDULER_cancel (ss->ps->tt); + ss->ps->tt = NULL; + TALER_TESTING_interpreter_next (is); + return; + } + if (GNUNET_TIME_relative_is_zero (ss->timeout)) + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +status_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct StatusState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + const char *exchange_url; + + ss->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + GNUNET_assert (NULL != create_reserve); + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (create_reserve, + &ss->reserve_pubp)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_pub for status query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + ss->rsh = TALER_EXCHANGE_reserves_get ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + ss->reserve_pubp, + ss->timeout, + &reserve_status_cb, + ss); + if (! GNUNET_TIME_relative_is_zero (ss->timeout)) + { + TALER_TESTING_interpreter_next (is); + return; + } +} + + +/** + * Cleanup the state from a "reserve status" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +status_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct StatusState *ss = cls; + + if (NULL != ss->rsh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_reserves_get_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_status (const char *label, + const char *reserve_reference, + const char *expected_balance, + unsigned int expected_response_code) +{ + struct StatusState *ss; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct StatusState); + ss->reserve_reference = reserve_reference; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &status_run, + .cleanup = &status_cleanup + }; + + return cmd; + } +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_poll (const char *label, + const char *reserve_reference, + const char *expected_balance, + struct GNUNET_TIME_Relative timeout, + unsigned int expected_response_code) +{ + struct StatusState *ss; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct StatusState); + ss->reserve_reference = reserve_reference; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_response_code; + ss->timeout = timeout; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &status_run, + .cleanup = &status_cleanup + }; + + return cmd; + } +} + + +/** + * Long poller timed out. Fail the test. + * + * @param cls a `struct PollState` + */ +static void +finish_timeout (void *cls) +{ + struct PollState *ps = cls; + + ps->tt = NULL; + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ps->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +finish_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PollState *ps = cls; + const struct TALER_TESTING_Command *poll_reserve; + struct StatusState *ss; + + ps->is = is; + poll_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ps->poll_reference); + GNUNET_assert (NULL != poll_reserve); + GNUNET_assert (poll_reserve->run == &status_run); + ss = poll_reserve->cls; + if (NULL == ss->rsh) + { + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_assert (NULL == ss->ps); + ss->ps = ps; + ps->tt = GNUNET_SCHEDULER_add_delayed (ps->timeout, + &finish_timeout, + ps); +} + + +/** + * Cleanup the state from a "reserve finish" CMD. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +finish_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PollState *ps = cls; + + if (NULL != ps->tt) + { + GNUNET_SCHEDULER_cancel (ps->tt); + ps->tt = NULL; + } + GNUNET_free (ps); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_poll_finish (const char *label, + struct GNUNET_TIME_Relative timeout, + const char *poll_reference) +{ + struct PollState *ps; + + GNUNET_assert (NULL != poll_reference); + ps = GNUNET_new (struct PollState); + ps->timeout = timeout; + ps->poll_reference = poll_reference; + { + struct TALER_TESTING_Command cmd = { + .cls = ps, + .label = label, + .run = &finish_run, + .cleanup = &finish_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_get_attestable.c b/src/testing/testing_api_cmd_reserve_get_attestable.c new file mode 100644 index 000000000..ed1eb1355 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_get_attestable.c @@ -0,0 +1,242 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_get_attestable.c + * @brief Implement the /reserve/$RID/get_attestable test command. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "get_attestable" CMD. + */ +struct GetAttestableState +{ + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve get_attestable" operation. + */ + struct TALER_EXCHANGE_ReservesGetAttestHandle *rgah; + + /** + * Expected attestable attributes. + */ + const char **expected_attestables; + + /** + * Length of the @e expected_attestables array. + */ + unsigned int expected_attestables_length; + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_get_attestable_cb ( + void *cls, + const struct TALER_EXCHANGE_ReserveGetAttestResult *rs) +{ + struct GetAttestableState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + + ss->rgah = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + // FIXME: check returned list matches expectations! + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +get_attestable_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetAttestableState *ss = cls; + const struct TALER_TESTING_Command *ref_reserve; + const struct TALER_ReservePrivateKeyP *reserve_priv; + const struct TALER_ReservePublicKeyP *reserve_pub; + const char *exchange_url; + + ss->is = is; + exchange_url = TALER_TESTING_get_exchange_url (is); + if (NULL == exchange_url) + { + GNUNET_break (0); + return; + } + ref_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + + if (NULL == ref_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK == + TALER_TESTING_get_trait_reserve_priv (ref_reserve, + &reserve_priv)) + { + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + } + else + { + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (ref_reserve, + &reserve_pub)) + { + GNUNET_break (0); + TALER_LOG_ERROR ( + "Failed to find reserve_priv for get_attestable query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + ss->reserve_pub = *reserve_pub; + } + ss->rgah = TALER_EXCHANGE_reserves_get_attestable ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + &ss->reserve_pub, + &reserve_get_attestable_cb, + ss); +} + + +/** + * Cleanup the state from a "reserve get_attestable" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +get_attestable_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetAttestableState *ss = cls; + + if (NULL != ss->rgah) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_reserves_get_attestable_cancel (ss->rgah); + ss->rgah = NULL; + } + GNUNET_free (ss->expected_attestables); + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_get_attestable (const char *label, + const char *reserve_reference, + unsigned int expected_response_code, + ...) +{ + struct GetAttestableState *ss; + va_list ap; + unsigned int num_expected; + const char *ea; + + num_expected = 0; + va_start (ap, expected_response_code); + while (NULL != va_arg (ap, const char *)) + num_expected++; + va_end (ap); + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct GetAttestableState); + ss->reserve_reference = reserve_reference; + ss->expected_response_code = expected_response_code; + ss->expected_attestables_length = num_expected; + ss->expected_attestables = GNUNET_new_array (num_expected, + const char *); + num_expected = 0; + va_start (ap, expected_response_code); + while (NULL != (ea = va_arg (ap, const char *))) + ss->expected_attestables[num_expected++] = ea; + va_end (ap); + + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &get_attestable_run, + .cleanup = &get_attestable_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_history.c b/src/testing/testing_api_cmd_reserve_history.c new file mode 100644 index 000000000..ecb236a54 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_history.c @@ -0,0 +1,586 @@ +/* + This file is part of TALER + Copyright (C) 2014-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_history.c + * @brief Implement the /reserve/history test command. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "history" CMD. + */ +struct HistoryState +{ + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve history" operation. + */ + struct TALER_EXCHANGE_ReservesHistoryHandle *rsh; + + /** + * Expected reserve balance. + */ + const char *expected_balance; + + /** + * Private key of the reserve being analyzed. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + +}; + + +/** + * Closure for analysis_cb(). + */ +struct AnalysisContext +{ + /** + * Reserve public key we are looking at. + */ + const struct TALER_ReservePublicKeyP *reserve_pub; + + /** + * Length of the @e history array. + */ + unsigned int history_length; + + /** + * Array of history items to match. + */ + const struct TALER_EXCHANGE_ReserveHistoryEntry *history; + + /** + * Array of @e history_length of matched entries. + */ + bool *found; + + /** + * Set to true if an entry could not be found. + */ + bool failure; +}; + + +/** + * Compare @a h1 and @a h2. + * + * @param h1 a history entry + * @param h2 a history entry + * @return 0 if @a h1 and @a h2 are equal + */ +static int +history_entry_cmp ( + const struct TALER_EXCHANGE_ReserveHistoryEntry *h1, + const struct TALER_EXCHANGE_ReserveHistoryEntry *h2) +{ + if (h1->type != h2->type) + return 1; + switch (h1->type) + { + case TALER_EXCHANGE_RTT_CREDIT: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == strcasecmp (h1->details.in_details.sender_url, + h2->details.in_details.sender_url)) && + (h1->details.in_details.wire_reference == + h2->details.in_details.wire_reference) && + (GNUNET_TIME_timestamp_cmp (h1->details.in_details.timestamp, + ==, + h2->details.in_details.timestamp)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_WITHDRAWAL: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.withdraw.fee, + &h2->details.withdraw.fee)) ) + /* testing_api_cmd_withdraw doesn't set the out_authorization_sig, + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + return 0; + return 1; + case TALER_EXCHANGE_RTT_AGEWITHDRAWAL: + /* testing_api_cmd_age_withdraw doesn't set the out_authorization_sig, + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.age_withdraw.fee, + &h2->details.age_withdraw.fee)) && + (h1->details.age_withdraw.max_age == + h2->details.age_withdraw.max_age)) + return 0; + return 1; + case TALER_EXCHANGE_RTT_RECOUP: + /* exchange_sig, exchange_pub and timestamp are NOT available + from the original recoup response, hence here NOT check(able/ed) */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + GNUNET_memcmp (&h1->details.recoup_details.coin_pub, + &h2->details.recoup_details.coin_pub)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_CLOSING: + /* testing_api_cmd_exec_closer doesn't set the + receiver_account_details, exchange_sig, exchange_pub or wtid or timestamp + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.close_details.fee, + &h2->details.close_details.fee)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_MERGE: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.merge_details.purse_fee, + &h2->details.merge_details.purse_fee)) && + (GNUNET_TIME_timestamp_cmp (h1->details.merge_details.merge_timestamp, + ==, + h2->details.merge_details.merge_timestamp)) + && + (GNUNET_TIME_timestamp_cmp (h1->details.merge_details.purse_expiration, + ==, + h2->details.merge_details.purse_expiration)) + && + (0 == + GNUNET_memcmp (&h1->details.merge_details.merge_pub, + &h2->details.merge_details.merge_pub)) && + (0 == + GNUNET_memcmp (&h1->details.merge_details.h_contract_terms, + &h2->details.merge_details.h_contract_terms)) && + (0 == + GNUNET_memcmp (&h1->details.merge_details.purse_pub, + &h2->details.merge_details.purse_pub)) && + (0 == + GNUNET_memcmp (&h1->details.merge_details.reserve_sig, + &h2->details.merge_details.reserve_sig)) && + (h1->details.merge_details.min_age == + h2->details.merge_details.min_age) && + (h1->details.merge_details.flags == + h2->details.merge_details.flags) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_OPEN: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (GNUNET_TIME_timestamp_cmp ( + h1->details.open_request.request_timestamp, + ==, + h2->details.open_request.request_timestamp)) && + (GNUNET_TIME_timestamp_cmp ( + h1->details.open_request.reserve_expiration, + ==, + h2->details.open_request.reserve_expiration)) && + (h1->details.open_request.purse_limit == + h2->details.open_request.purse_limit) && + (0 == + TALER_amount_cmp (&h1->details.open_request.reserve_payment, + &h2->details.open_request.reserve_payment)) && + (0 == + GNUNET_memcmp (&h1->details.open_request.reserve_sig, + &h2->details.open_request.reserve_sig)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_CLOSE: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (GNUNET_TIME_timestamp_cmp ( + h1->details.close_request.request_timestamp, + ==, + h2->details.close_request.request_timestamp)) && + (0 == + GNUNET_memcmp (&h1->details.close_request.target_account_h_payto, + &h2->details.close_request.target_account_h_payto)) && + (0 == + GNUNET_memcmp (&h1->details.close_request.reserve_sig, + &h2->details.close_request.reserve_sig)) ) + return 0; + return 1; + } + GNUNET_assert (0); + return 1; +} + + +/** + * Check if @a cmd changed the reserve, if so, find the + * entry in our history and set the respective index in found + * to true. If the entry is not found, set failure. + * + * @param cls our `struct AnalysisContext *` + * @param cmd command to analyze for impact on history + */ +static void +analyze_command (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AnalysisContext *ac = cls; + const struct TALER_ReservePublicKeyP *reserve_pub = ac->reserve_pub; + const struct TALER_EXCHANGE_ReserveHistoryEntry *history = ac->history; + unsigned int history_length = ac->history_length; + bool *found = ac->found; + + if (TALER_TESTING_cmd_is_batch (cmd)) + { + struct TALER_TESTING_Command *cur; + struct TALER_TESTING_Command *bcmd; + + cur = TALER_TESTING_cmd_batch_get_current (cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_batch_cmds (cmd, + &bcmd)) + { + GNUNET_break (0); + ac->failure = true; + return; + } + for (unsigned int i = 0; NULL != bcmd[i].label; i++) + { + struct TALER_TESTING_Command *step = &bcmd[i]; + + analyze_command (ac, + step); + if (ac->failure) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Entry for batch step `%s' missing in history\n", + step->label); + return; + } + if (step == cur) + break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */ + } + return; + } + + { + const struct TALER_ReservePublicKeyP *rp; + + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (cmd, + &rp)) + return; /* command does nothing for reserves */ + if (0 != + GNUNET_memcmp (rp, + reserve_pub)) + return; /* command affects some _other_ reserve */ + for (unsigned int j = 0; true; j++) + { + const struct TALER_EXCHANGE_ReserveHistoryEntry *he; + bool matched = false; + + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_history (cmd, + j, + &he)) + { + /* NOTE: only for debugging... */ + if (0 == j) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Command `%s' has the reserve_pub, but lacks reserve history trait\n", + cmd->label); + return; /* command does nothing for reserves */ + } + for (unsigned int i = 0; i<history_length; i++) + { + if (found[i]) + continue; /* already found, skip */ + if (0 == + history_entry_cmp (he, + &history[i])) + { + found[i] = true; + matched = true; + break; + } + } + if (! matched) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command `%s' reserve history entry #%u not found\n", + cmd->label, + j); + ac->failure = true; + return; + } + } + } +} + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_history_cb (void *cls, + const struct TALER_EXCHANGE_ReserveHistory *rs) +{ + struct HistoryState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + struct TALER_Amount eb; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ss->expected_balance, + &eb)); + + if (0 != TALER_amount_cmp (&eb, + &rs->details.ok.balance)) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected amount in reserve: %s\n", + TALER_amount_to_string (&rs->details.ok.balance)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected balance of: %s\n", + TALER_amount_to_string (&eb)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + { + bool found[rs->details.ok.history_len]; + struct AnalysisContext ac = { + .reserve_pub = &ss->reserve_pub, + .history = rs->details.ok.history, + .history_length = rs->details.ok.history_len, + .found = found + }; + + memset (found, + 0, + sizeof (found)); + TALER_TESTING_iterate (is, + true, + &analyze_command, + &ac); + if (ac.failure) + { + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + for (unsigned int i = 0; i<rs->details.ok.history_len; i++) + { + if (found[i]) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "History entry at index %u of type %d not justified by command history\n", + i, + rs->details.ok.history[i].type); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +history_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct HistoryState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + + ss->is = is; + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &ss->reserve_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_priv for history query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + ss->rsh = TALER_EXCHANGE_reserves_history ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + ss->reserve_priv, + 0, + &reserve_history_cb, + ss); +} + + +/** + * Offer internal data from a "history" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +history_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct HistoryState *hs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_reserve_pub (&hs->reserve_pub), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Cleanup the state from a "reserve 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 *ss = cls; + + if (NULL != ss->rsh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_reserves_history_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_history (const char *label, + const char *reserve_reference, + const char *expected_balance, + unsigned int expected_response_code) +{ + struct HistoryState *ss; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct HistoryState); + ss->reserve_reference = reserve_reference; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &history_run, + .cleanup = &history_cleanup, + .traits = &history_traits + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_open.c b/src/testing/testing_api_cmd_reserve_open.c new file mode 100644 index 000000000..189d06b26 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_open.c @@ -0,0 +1,349 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_open.c + * @brief Implement the /reserve/$RID/open test command. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * Information we track per coin used to pay for opening the + * reserve. + */ +struct CoinDetail +{ + /** + * Name of the command and index of the coin to use. + */ + const char *name; + + /** + * Amount to charge to this coin. + */ + struct TALER_Amount amount; +}; + + +/** + * State for a "open" CMD. + */ +struct OpenState +{ + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Requested expiration time. + */ + struct GNUNET_TIME_Relative req_expiration_time; + + /** + * Requested minimum number of purses. + */ + uint32_t min_purses; + + /** + * Amount to pay for the opening from the reserve balance. + */ + struct TALER_Amount reserve_pay; + + /** + * Handle to the "reserve open" operation. + */ + struct TALER_EXCHANGE_ReservesOpenHandle *rsh; + + /** + * Expected reserve balance. + */ + const char *expected_balance; + + /** + * Length of the @e cd array. + */ + unsigned int cpl; + + /** + * Coin details, array of length @e cpl. + */ + struct CoinDetail *cd; + + /** + * Private key of the reserve being analyzed. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_open_cb (void *cls, + const struct TALER_EXCHANGE_ReserveOpenResult *rs) +{ + struct OpenState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + JSON_INDENT (2)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +open_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct OpenState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + struct TALER_EXCHANGE_PurseDeposit cp[GNUNET_NZL (ss->cpl)]; + + ss->is = is; + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &ss->reserve_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_priv for open query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + for (unsigned int i = 0; i<ss->cpl; i++) + { + struct TALER_EXCHANGE_PurseDeposit *cpi = &cp[i]; + const struct TALER_TESTING_Command *cmdi; + const struct TALER_AgeCommitmentProof *age_commitment_proof; + const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_DenominationSignature *denom_sig; + const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + char *cref; + unsigned int cidx; + + if (GNUNET_OK != + TALER_TESTING_parse_coin_reference (ss->cd[i].name, + &cref, + &cidx)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to parse coin reference `%s'\n", + ss->cd[i].name); + TALER_TESTING_interpreter_fail (is); + return; + } + cmdi = TALER_TESTING_interpreter_lookup_command (is, + cref); + GNUNET_free (cref); + if (NULL == cmdi) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Command `%s' not found\n", + ss->cd[i].name); + TALER_TESTING_interpreter_fail (is); + return; + } + if ( (GNUNET_OK != + TALER_TESTING_get_trait_age_commitment_proof (cmdi, + cidx, + &age_commitment_proof)) + || + (GNUNET_OK != + TALER_TESTING_get_trait_coin_priv (cmdi, + cidx, + &coin_priv)) || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_sig (cmdi, + cidx, + &denom_sig)) || + (GNUNET_OK != + TALER_TESTING_get_trait_denom_pub (cmdi, + cidx, + &denom_pub)) ) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Coin trait not found in `%s'\n", + ss->cd[i].name); + TALER_TESTING_interpreter_fail (is); + return; + } + cpi->age_commitment_proof = age_commitment_proof; + cpi->coin_priv = *coin_priv; + cpi->denom_sig = *denom_sig; + cpi->amount = ss->cd[i].amount; + cpi->h_denom_pub = denom_pub->h_key; + } + ss->rsh = TALER_EXCHANGE_reserves_open ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + ss->reserve_priv, + &ss->reserve_pay, + ss->cpl, + cp, + GNUNET_TIME_relative_to_timestamp (ss->req_expiration_time), + ss->min_purses, + &reserve_open_cb, + ss); +} + + +/** + * Cleanup the state from a "reserve open" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +open_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct OpenState *ss = cls; + + if (NULL != ss->rsh) + { + TALER_TESTING_command_incomplete (ss->is, + cmd->label); + TALER_EXCHANGE_reserves_open_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_open (const char *label, + const char *reserve_reference, + const char *reserve_pay, + struct GNUNET_TIME_Relative expiration_time, + uint32_t min_purses, + unsigned int expected_response_code, + ...) +{ + struct OpenState *ss; + va_list ap; + const char *name; + unsigned int i; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct OpenState); + ss->reserve_reference = reserve_reference; + ss->req_expiration_time = expiration_time; + ss->min_purses = min_purses; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (reserve_pay, + &ss->reserve_pay)); + ss->expected_response_code = expected_response_code; + va_start (ap, + expected_response_code); + while (NULL != (name = va_arg (ap, const char *))) + ss->cpl++; + va_end (ap); + GNUNET_assert (0 == (ss->cpl % 2)); + ss->cpl /= 2; /* name and amount per coin */ + ss->cd = GNUNET_new_array (ss->cpl, + struct CoinDetail); + i = 0; + va_start (ap, + expected_response_code); + while (NULL != (name = va_arg (ap, const char *))) + { + struct CoinDetail *cd = &ss->cd[i]; + cd->name = name; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (va_arg (ap, + const char *), + &cd->amount)); + i++; + } + va_end (ap); + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &open_run, + .cleanup = &open_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_purse.c b/src/testing/testing_api_cmd_reserve_purse.c new file mode 100644 index 000000000..ef6964f26 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_purse.c @@ -0,0 +1,371 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_reserve_purse.c + * @brief command for testing /reserves/$PID/purse + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "purse create with merge" CMD. + */ +struct ReservePurseState +{ + + /** + * Merge time (local time when the command was + * executed). + */ + struct GNUNET_TIME_Timestamp merge_timestamp; + + /** + * Reserve private key. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Reserve public key. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Reserve signature generated for the request + * (client-side). + */ + struct TALER_ReserveSignatureP reserve_sig; + + /** + * Private key of the purse. + */ + struct TALER_PurseContractPrivateKeyP purse_priv; + + /** + * Public key of the purse. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * Private key with the merge capability. + */ + struct TALER_PurseMergePrivateKeyP merge_priv; + + /** + * Public key of the merge capability. + */ + struct TALER_PurseMergePublicKeyP merge_pub; + + /** + * Private key to decrypt the contract. + */ + struct TALER_ContractDiffiePrivateP contract_priv; + + /** + * Handle while operation is running. + */ + struct TALER_EXCHANGE_PurseCreateMergeHandle *dh; + + /** + * When will the purse expire? + */ + struct GNUNET_TIME_Relative expiration_rel; + + /** + * When will the purse expire? + */ + struct GNUNET_TIME_Timestamp purse_expiration; + + /** + * Hash of the payto://-URI for the reserve we are + * merging into. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC. + */ + uint64_t requirement_row; + + /** + * Contract terms for the purse. + */ + json_t *contract_terms; + + /** + * Reference to the reserve, or NULL (!). + */ + const char *reserve_ref; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * True to pay the purse fee. + */ + bool pay_purse_fee; +}; + + +/** + * Callback to analyze the /reserves/$PID/purse response, just used to check if + * the response code is acceptable. + * + * @param cls closure. + * @param dr purse response details + */ +static void +purse_cb (void *cls, + const struct TALER_EXCHANGE_PurseCreateMergeResponse *dr) +{ + struct ReservePurseState *ds = cls; + + ds->dh = NULL; + ds->reserve_sig = *dr->reserve_sig; + if (ds->expected_response_code != dr->hr.http_status) + { + TALER_TESTING_unexpected_status (ds->is, + dr->hr.http_status, + ds->expected_response_code); + return; + } + switch (dr->hr.http_status) + { + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* KYC required */ + ds->requirement_row = + dr->details.unavailable_for_legal_reasons.requirement_row; + break; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +purse_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct ReservePurseState *ds = cls; + const struct TALER_ReservePrivateKeyP *reserve_priv; + const struct TALER_TESTING_Command *ref; + + (void) cmd; + ds->is = is; + ref = TALER_TESTING_interpreter_lookup_command (ds->is, + ds->reserve_ref); + GNUNET_assert (NULL != ref); + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (ref, + &reserve_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->reserve_priv = *reserve_priv; + GNUNET_CRYPTO_eddsa_key_create (&ds->purse_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&ds->purse_priv.eddsa_priv, + &ds->purse_pub.eddsa_pub); + GNUNET_CRYPTO_eddsa_key_get_public (&ds->reserve_priv.eddsa_priv, + &ds->reserve_pub.eddsa_pub); + GNUNET_CRYPTO_eddsa_key_create (&ds->merge_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&ds->merge_priv.eddsa_priv, + &ds->merge_pub.eddsa_pub); + GNUNET_CRYPTO_ecdhe_key_create (&ds->contract_priv.ecdhe_priv); + ds->purse_expiration = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_relative_to_absolute (ds->expiration_rel)); + + { + char *payto_uri; + const char *exchange_url; + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + payto_uri = TALER_reserve_make_payto (exchange_url, + &ds->reserve_pub); + TALER_payto_hash (payto_uri, + &ds->h_payto); + GNUNET_free (payto_uri); + } + + GNUNET_assert (0 == + json_object_set_new ( + ds->contract_terms, + "pay_deadline", + GNUNET_JSON_from_timestamp (ds->purse_expiration))); + ds->merge_timestamp = GNUNET_TIME_timestamp_get (); + ds->dh = TALER_EXCHANGE_purse_create_with_merge ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + &ds->reserve_priv, + &ds->purse_priv, + &ds->merge_priv, + &ds->contract_priv, + ds->contract_terms, + true /* upload contract */, + ds->pay_purse_fee, + ds->merge_timestamp, + &purse_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not purse reserve\n"); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "purse" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct ReservePurseState`. + * @param cmd the command which is being cleaned up. + */ +static void +purse_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct ReservePurseState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_purse_create_with_merge_cancel (ds->dh); + ds->dh = NULL; + } + json_decref (ds->contract_terms); + GNUNET_free (ds); +} + + +/** + * Offer internal data from a "purse" CMD, to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static enum GNUNET_GenericReturnValue +purse_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct ReservePurseState *ds = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_timestamp (0, + &ds->merge_timestamp), + TALER_TESTING_make_trait_contract_terms (ds->contract_terms), + TALER_TESTING_make_trait_purse_priv (&ds->purse_priv), + TALER_TESTING_make_trait_purse_pub (&ds->purse_pub), + TALER_TESTING_make_trait_merge_priv (&ds->merge_priv), + TALER_TESTING_make_trait_merge_pub (&ds->merge_pub), + TALER_TESTING_make_trait_contract_priv (&ds->contract_priv), + TALER_TESTING_make_trait_reserve_priv (&ds->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub), + TALER_TESTING_make_trait_reserve_sig (&ds->reserve_sig), + TALER_TESTING_make_trait_legi_requirement_row (&ds->requirement_row), + TALER_TESTING_make_trait_h_payto (&ds->h_payto), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_purse_create_with_reserve ( + const char *label, + unsigned int expected_http_status, + const char *contract_terms, + bool upload_contract, + bool pay_purse_fee, + struct GNUNET_TIME_Relative expiration, + const char *reserve_ref) +{ + struct ReservePurseState *ds; + json_error_t err; + + ds = GNUNET_new (struct ReservePurseState); + ds->expiration_rel = expiration; + ds->contract_terms = json_loads (contract_terms, + 0 /* flags */, + &err); + GNUNET_assert (NULL != ds->contract_terms); + ds->pay_purse_fee = pay_purse_fee; + ds->reserve_ref = reserve_ref; + ds->expected_response_code = expected_http_status; + + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &purse_run, + .cleanup = &purse_cleanup, + .traits = &purse_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_reserve_purse.c */ diff --git a/src/testing/testing_api_cmd_revoke.c b/src/testing/testing_api_cmd_revoke.c index 9f904454e..f734be1a4 100644 --- a/src/testing/testing_api_cmd_revoke.c +++ b/src/testing/testing_api_cmd_revoke.c @@ -100,7 +100,7 @@ revoke_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue revoke_traits (void *cls, const void **ret, const char *trait, @@ -110,8 +110,7 @@ revoke_traits (void *cls, struct TALER_TESTING_Trait traits[] = { /* Needed by the handler which waits the proc' * death and calls the next command */ - TALER_TESTING_make_trait_process (0, - &rs->revoke_proc), + TALER_TESTING_make_trait_process (&rs->revoke_proc), TALER_TESTING_trait_end () }; @@ -142,14 +141,12 @@ revoke_run (void *cls, /* Get denom pub from trait */ coin_cmd = TALER_TESTING_interpreter_lookup_command (is, rs->coin_reference); - if (NULL == coin_cmd) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_denom_pub (coin_cmd, 0, @@ -183,16 +180,6 @@ revoke_run (void *cls, } -/** - * Make a "revoke" command. - * - * @param label the command label. - * @param expected_response_code expected HTTP status code. - * @param coin_reference reference to a CMD that will offer the - * denomination to revoke. - * @param config_filename configuration file name. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_revoke (const char *label, unsigned int expected_response_code, diff --git a/src/testing/testing_api_cmd_revoke_denom_key.c b/src/testing/testing_api_cmd_revoke_denom_key.c index 7c77c3566..2663c538f 100644 --- a/src/testing/testing_api_cmd_revoke_denom_key.c +++ b/src/testing/testing_api_cmd_revoke_denom_key.c @@ -65,28 +65,22 @@ struct RevokeState * Function called with information about the post revocation operation result. * * @param cls closure with a `struct RevokeState *` - * @param hr HTTP response data + * @param rdr response data */ static void success_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *rdr) { struct RevokeState *rs = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &rdr->hr; rs->kh = NULL; if (rs->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - rs->is->commands[rs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rs->is); + TALER_TESTING_unexpected_status (rs->is, + hr->http_status, + rs->expected_response_code); return; } TALER_TESTING_interpreter_next (rs->is); @@ -158,7 +152,23 @@ revoke_run (void *cls, const struct TALER_TESTING_Command *coin_cmd; const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; struct TALER_MasterSignatureP master_sig; + const char *exchange_url; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } rs->is = is; /* Get denom pub from trait */ coin_cmd = TALER_TESTING_interpreter_lookup_command (is, @@ -185,13 +195,27 @@ revoke_run (void *cls, } else { + const struct TALER_TESTING_Command *exchange_cmd; + const struct TALER_MasterPrivateKeyP *master_priv; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); TALER_exchange_offline_denomination_revoke_sign (&denom_pub->h_key, - &is->master_priv, + master_priv, &master_sig); } rs->kh = TALER_EXCHANGE_management_revoke_denomination_key ( - is->ctx, - is->exchange_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, &denom_pub->h_key, &master_sig, &success_cb, diff --git a/src/testing/testing_api_cmd_revoke_sign_key.c b/src/testing/testing_api_cmd_revoke_sign_key.c index 9745d728d..65b80b4c9 100644 --- a/src/testing/testing_api_cmd_revoke_sign_key.c +++ b/src/testing/testing_api_cmd_revoke_sign_key.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -65,28 +65,22 @@ struct RevokeState * Function called with information about the post revocation operation result. * * @param cls closure with a `struct RevokeState *` - * @param hr HTTP response data + * @param rsr response data */ static void success_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *rsr) { struct RevokeState *rs = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &rsr->hr; rs->kh = NULL; if (rs->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - rs->is->commands[rs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (rs->is); + TALER_TESTING_unexpected_status (rs->is, + hr->http_status, + rs->expected_response_code); return; } TALER_TESTING_interpreter_next (rs->is); @@ -158,7 +152,23 @@ revoke_run (void *cls, const struct TALER_TESTING_Command *coin_cmd; const struct TALER_ExchangePublicKeyP *exchange_pub; struct TALER_MasterSignatureP master_sig; + const char *exchange_url; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } rs->is = is; /* Get sign pub from trait */ coin_cmd = TALER_TESTING_interpreter_lookup_command (is, @@ -185,13 +195,27 @@ revoke_run (void *cls, } else { + const struct TALER_TESTING_Command *exchange_cmd; + const struct TALER_MasterPrivateKeyP *master_priv; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); TALER_exchange_offline_signkey_revoke_sign (exchange_pub, - &is->master_priv, + master_priv, &master_sig); } rs->kh = TALER_EXCHANGE_management_revoke_signing_key ( - is->ctx, - is->exchange_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, exchange_pub, &master_sig, &success_cb, diff --git a/src/testing/testing_api_cmd_rewind.c b/src/testing/testing_api_cmd_rewind.c deleted file mode 100644 index 979607cd7..000000000 --- a/src/testing/testing_api_cmd_rewind.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_cmd_rewind.c - * @brief command to rewind the instruction pointer. - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_exchange_service.h" -#include "taler_testing_lib.h" - - -/** - * State for a "rewind" CMD. - */ -struct RewindIpState -{ - /** - * Instruction pointer to set into the interpreter. - */ - const char *target_label; - - /** - * How many times this set should take place. However, this value lives at - * the calling process, and this CMD is only in charge of checking and - * decremeting it. - */ - unsigned int counter; -}; - - -/** - * Only defined to respect the API. - */ -static void -rewind_ip_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - (void) cls; - (void) cmd; -} - - -/** - * Seek for the @a target command in @a batch (and rewind to it - * if successful). - * - * @param is the interpreter state (for failures) - * @param cmd batch to search for @a target - * @param target command to search for - * @return #GNUNET_OK on success, #GNUNET_NO if target was not found, - * #GNUNET_SYSERR if target is in the future and we failed - */ -static int -seek_batch (struct TALER_TESTING_Interpreter *is, - const struct TALER_TESTING_Command *cmd, - const struct TALER_TESTING_Command *target) -{ - unsigned int new_ip; -#define BATCH_INDEX 1 - struct TALER_TESTING_Command *batch; - struct TALER_TESTING_Command *current; - struct TALER_TESTING_Command *icmd; - const struct TALER_TESTING_Command *match; - - current = TALER_TESTING_cmd_batch_get_current (cmd); - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_cmd (cmd, - BATCH_INDEX, - &batch)); - match = NULL; - for (new_ip = 0; - NULL != (icmd = &batch[new_ip]); - new_ip++) - { - if (current == target) - current = NULL; - if (icmd == target) - { - match = icmd; - break; - } - if (TALER_TESTING_cmd_is_batch (icmd)) - { - int ret = seek_batch (is, - icmd, - target); - if (GNUNET_SYSERR == ret) - return GNUNET_SYSERR; /* failure! */ - if (GNUNET_OK == ret) - { - match = icmd; - break; - } - } - } - if (NULL == current) - { - /* refuse to jump forward */ - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - if (NULL == match) - return GNUNET_NO; /* not found */ - TALER_TESTING_cmd_batch_set_current (cmd, - new_ip); - return GNUNET_OK; -} - - -/** - * Run the "rewind" CMD. - * - * @param cls closure. - * @param cmd command being executed now. - * @param is the interpreter state. - */ -static void -rewind_ip_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct RewindIpState *ris = cls; - const struct TALER_TESTING_Command *target; - unsigned int new_ip; - - (void) cmd; - if (0 == ris->counter) - { - TALER_TESTING_interpreter_next (is); - return; - } - target - = TALER_TESTING_interpreter_lookup_command (is, - ris->target_label); - if (NULL == target) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - ris->counter--; - for (new_ip = 0; - NULL != is->commands[new_ip].label; - new_ip++) - { - const struct TALER_TESTING_Command *cmd = &is->commands[new_ip]; - - if (cmd == target) - break; - if (TALER_TESTING_cmd_is_batch (cmd)) - { - int ret = seek_batch (is, - cmd, - target); - if (GNUNET_SYSERR == ret) - return; /* failure! */ - if (GNUNET_OK == ret) - break; - } - } - if (new_ip > is->ip) - { - /* refuse to jump forward */ - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - is->ip = new_ip - 1; /* -1 because the next function will advance by one */ - TALER_TESTING_interpreter_next (is); -} - - -/** - * Make the instruction pointer point to @a new_ip - * only if @a counter is greater than zero. - * - * @param label command label - * @param target_label label of the new instruction pointer's destination after the jump; - * must be before the current instruction - * @param counter counts how many times the rewinding is to happen. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_rewind_ip (const char *label, - const char *target_label, - unsigned int counter) -{ - struct RewindIpState *ris; - - ris = GNUNET_new (struct RewindIpState); - ris->target_label = target_label; - ris->counter = counter; - { - struct TALER_TESTING_Command cmd = { - .cls = ris, - .label = label, - .run = &rewind_ip_run, - .cleanup = &rewind_ip_cleanup - }; - - return cmd; - } -} diff --git a/src/testing/testing_api_cmd_run_fakebank.c b/src/testing/testing_api_cmd_run_fakebank.c new file mode 100644 index 000000000..7739d3c0c --- /dev/null +++ b/src/testing/testing_api_cmd_run_fakebank.c @@ -0,0 +1,214 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_run_fakebank.c + * @brief Command to run fakebank in-process + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + +/** + * State for a "run fakebank" CMD. + */ +struct RunFakebankState +{ + + /** + * Our interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Handle to the fakebank we are running. + */ + struct TALER_FAKEBANK_Handle *fakebank; + + /** + * URL of the bank. + */ + char *bank_url; + + /** + * Currency to use. + */ + char *currency; + + /** + * Data for access control. + */ + struct TALER_BANK_AuthenticationData ba; + + /** + * Port to use. + */ + uint16_t port; +}; + + +/** + * Run the "get_exchange" command. + * + * @param cls closure. + * @param cmd the command currently being executed. + * @param is the interpreter state. + */ +static void +run_fakebank_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RunFakebankState *rfs = cls; + + (void) cmd; + rfs->fakebank = TALER_FAKEBANK_start (rfs->port, + rfs->currency); + if (NULL == rfs->fakebank) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Cleanup the state. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +run_fakebank_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct RunFakebankState *rfs = cls; + + if (NULL != rfs->fakebank) + { + TALER_FAKEBANK_stop (rfs->fakebank); + rfs->fakebank = NULL; + } + GNUNET_free (rfs->ba.wire_gateway_url); + GNUNET_free (rfs->bank_url); + GNUNET_free (rfs->currency); + GNUNET_free (rfs); +} + + +/** + * Offer internal data to a "run_fakebank" CMD state to other commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +run_fakebank_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct RunFakebankState *rfs = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_bank_auth_data (&rfs->ba), + TALER_TESTING_make_trait_fakebank (rfs->fakebank), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_run_fakebank ( + const char *label, + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *exchange_account_section) +{ + struct RunFakebankState *rfs; + unsigned long long fakebank_port; + char *exchange_payto_uri; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "BANK", + "HTTP_PORT", + &fakebank_port)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "BANK", + "HTTP_PORT"); + GNUNET_assert (0); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + exchange_account_section, + "PAYTO_URI", + &exchange_payto_uri)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + exchange_account_section, + "PAYTO_URI"); + GNUNET_assert (0); + } + rfs = GNUNET_new (struct RunFakebankState); + rfs->port = (uint16_t) fakebank_port; + GNUNET_asprintf (&rfs->bank_url, + "http://localhost:%u/", + (unsigned int) rfs->port); + GNUNET_assert (GNUNET_OK == + TALER_config_get_currency (cfg, + &rfs->currency)); + { + char *exchange_xtalerbank_account; + + exchange_xtalerbank_account + = TALER_xtalerbank_account_from_payto (exchange_payto_uri); + GNUNET_assert (NULL != exchange_xtalerbank_account); + GNUNET_asprintf (&rfs->ba.wire_gateway_url, + "http://localhost:%u/%s/", + (unsigned int) fakebank_port, + exchange_xtalerbank_account); + GNUNET_free (exchange_xtalerbank_account); + GNUNET_free (exchange_payto_uri); + } + GNUNET_free (exchange_payto_uri); + rfs->ba.method = TALER_BANK_AUTH_NONE; + { + struct TALER_TESTING_Command cmd = { + .cls = rfs, + .label = label, + .run = &run_fakebank_run, + .cleanup = &run_fakebank_cleanup, + .traits = &run_fakebank_traits, + .name = "fakebank" + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_serialize_keys.c b/src/testing/testing_api_cmd_serialize_keys.c deleted file mode 100644 index 82df0f064..000000000 --- a/src/testing/testing_api_cmd_serialize_keys.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - This file is part of TALER - (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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_cmd_serialize_keys.c - * @brief Lets tests use the keys serialization API. - * @author Marcello Stanisci - */ -#include "platform.h" -#include <jansson.h> -#include "taler_testing_lib.h" - - -/** - * Internal state for a serialize-keys CMD. - */ -struct SerializeKeysState -{ - /** - * Serialized keys. - */ - json_t *keys; - - /** - * Exchange URL. Needed because the exchange gets disconnected - * from, after keys serialization. This value is then needed by - * subsequent commands that have to reconnect to the exchange. - */ - char *exchange_url; -}; - - -/** - * Internal state for a connect-with-state CMD. - */ -struct ConnectWithStateState -{ - - /** - * Reference to a CMD that offers a serialized key-state - * that will be used in the reconnection. - */ - const char *state_reference; - - /** - * If set to GNUNET_YES, then the /keys callback has already - * been passed the control to the next CMD. This is necessary - * because it is not uncommon that the /keys callback gets - * invoked multiple times, and without this flag, we would keep - * going "next" CMD upon every invocation (causing impredictable - * behaviour as for the instruction pointer.) - */ - unsigned int consumed; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; -}; - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -serialize_keys_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct SerializeKeysState *sks = cls; - - sks->keys = TALER_EXCHANGE_serialize_data (is->exchange); - if (NULL == sks->keys) - TALER_TESTING_interpreter_fail (is); - - sks->exchange_url = GNUNET_strdup - (TALER_EXCHANGE_get_base_url (is->exchange)); - TALER_EXCHANGE_disconnect (is->exchange); - is->exchange = NULL; - is->working = GNUNET_NO; - TALER_TESTING_interpreter_next (is); -} - - -/** - * Cleanup the state of a "serialize keys" CMD. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -serialize_keys_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct SerializeKeysState *sks = cls; - - if (NULL != sks->keys) - { - json_decref (sks->keys); - } - GNUNET_free (sks->exchange_url); - GNUNET_free (sks); -} - - -/** - * Offer serialized keys as trait. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -serialize_keys_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct SerializeKeysState *sks = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_exchange_keys (0, sks->keys), - TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BASE_URL, - sks->exchange_url), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -connect_with_state_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct ConnectWithStateState *cwss = cls; - const struct TALER_TESTING_Command *state_cmd; - const json_t *serialized_keys; - const char *exchange_url; - - /* This command usually gets rescheduled after serialized - * reconnection. */ - if (GNUNET_YES == cwss->consumed) - { - TALER_TESTING_interpreter_next (is); - return; - } - - cwss->is = is; - state_cmd = TALER_TESTING_interpreter_lookup_command - (is, cwss->state_reference); - - /* Command providing serialized keys not found. */ - if (NULL == state_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_exchange_keys (state_cmd, - 0, - &serialized_keys)); - { - char *dump; - - dump = json_dumps (serialized_keys, - JSON_INDENT (1)); - TALER_LOG_DEBUG ("Serialized key-state: %s\n", - dump); - free (dump); - } - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_url (state_cmd, - TALER_TESTING_UT_EXCHANGE_BASE_URL, - &exchange_url)); - is->exchange = TALER_EXCHANGE_connect (is->ctx, - exchange_url, - &TALER_TESTING_cert_cb, - cwss, - TALER_EXCHANGE_OPTION_DATA, - serialized_keys, - TALER_EXCHANGE_OPTION_END); - cwss->consumed = GNUNET_YES; -} - - -/** - * Cleanup the state of a "connect with state" CMD. Just - * a placeholder to avoid jumping on an invalid address. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -connect_with_state_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct ConnectWithStateState *cwss = cls; - - GNUNET_free (cwss); -} - - -/** - * Make a serialize-keys CMD. It will ask for - * keys serialization __and__ disconnect from the - * exchange. - * - * @param label CMD label - * @return the CMD. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_serialize_keys (const char *label) -{ - struct SerializeKeysState *sks; - - sks = GNUNET_new (struct SerializeKeysState); - { - struct TALER_TESTING_Command cmd = { - .cls = sks, - .label = label, - .run = serialize_keys_run, - .cleanup = serialize_keys_cleanup, - .traits = serialize_keys_traits - }; - - return cmd; - } -} - - -/** - * Make a connect-with-state CMD. This command - * will use a serialized key state to reconnect - * to the exchange. - * - * @param label command label - * @param state_reference label of a CMD offering - * a serialized key state. - * @return the CMD. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_connect_with_state (const char *label, - const char *state_reference) -{ - struct ConnectWithStateState *cwss; - - cwss = GNUNET_new (struct ConnectWithStateState); - cwss->state_reference = state_reference; - cwss->consumed = GNUNET_NO; - { - struct TALER_TESTING_Command cmd = { - .cls = cwss, - .label = label, - .run = connect_with_state_run, - .cleanup = connect_with_state_cleanup - }; - - return cmd; - } -} diff --git a/src/testing/testing_api_cmd_set_officer.c b/src/testing/testing_api_cmd_set_officer.c new file mode 100644 index 000000000..4fbe5e368 --- /dev/null +++ b/src/testing/testing_api_cmd_set_officer.c @@ -0,0 +1,301 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_set_officer.c + * @brief command for testing /management/aml-officers + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "set_officer" CMD. + */ +struct SetOfficerState +{ + + /** + * Update AML officer handle while operation is running. + */ + struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *dh; + + /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to command to previous set officer + * to update, or NULL. + */ + const char *ref_cmd; + + /** + * Name to use for the officer. + */ + const char *name; + + /** + * Private key of the AML officer. + */ + struct TALER_AmlOfficerPrivateKeyP officer_priv; + + /** + * Public key of the AML officer. + */ + struct TALER_AmlOfficerPublicKeyP officer_pub; + + /** + * Is the officer supposed to be enabled? + */ + bool is_active; + + /** + * Is access supposed to be read-only? + */ + bool read_only; + +}; + + +/** + * Callback to analyze the /management/XXX response, just used to check + * if the response code is acceptable. + * + * @param cls closure. + * @param ar response details + */ +static void +set_officer_cb (void *cls, + const struct + TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar) +{ + struct SetOfficerState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr; + + ds->dh = NULL; + if (MHD_HTTP_NO_CONTENT != hr->http_status) + { + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + MHD_HTTP_NO_CONTENT); + return; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +set_officer_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct SetOfficerState *ds = cls; + struct GNUNET_TIME_Timestamp now; + struct TALER_MasterSignatureP master_sig; + const char *exchange_url; + + (void) cmd; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } + now = GNUNET_TIME_timestamp_get (); + ds->is = is; + if (NULL == ds->ref_cmd) + { + GNUNET_CRYPTO_eddsa_key_create (&ds->officer_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&ds->officer_priv.eddsa_priv, + &ds->officer_pub.eddsa_pub); + } + else + { + const struct TALER_TESTING_Command *ref; + const struct TALER_AmlOfficerPrivateKeyP *officer_priv; + const struct TALER_AmlOfficerPublicKeyP *officer_pub; + + ref = TALER_TESTING_interpreter_lookup_command (is, + ds->ref_cmd); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_officer_pub (ref, + &officer_pub)); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_officer_priv (ref, + &officer_priv)); + ds->officer_pub = *officer_pub; + ds->officer_priv = *officer_priv; + } + { + const struct TALER_TESTING_Command *exchange_cmd; + const struct TALER_MasterPrivateKeyP *master_priv; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); + + TALER_exchange_offline_aml_officer_status_sign (&ds->officer_pub, + ds->name, + now, + ds->is_active, + ds->read_only, + master_priv, + &master_sig); + } + ds->dh = TALER_EXCHANGE_management_update_aml_officer ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + &ds->officer_pub, + ds->name, + now, + ds->is_active, + ds->read_only, + &master_sig, + &set_officer_cb, + ds); + if (NULL == ds->dh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "set_officer" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct SetOfficerState`. + * @param cmd the command which is being cleaned up. + */ +static void +set_officer_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct SetOfficerState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_management_update_aml_officer_cancel (ds->dh); + ds->dh = NULL; + } + GNUNET_free (ds); +} + + +/** + * Offer internal data of a "set officer" CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +set_officer_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct SetOfficerState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_officer_pub (&ws->officer_pub), + TALER_TESTING_make_trait_officer_priv (&ws->officer_priv), + TALER_TESTING_make_trait_officer_name (ws->name), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_set_officer ( + const char *label, + const char *ref_cmd, + const char *name, + bool is_active, + bool read_only) +{ + struct SetOfficerState *ds; + + ds = GNUNET_new (struct SetOfficerState); + ds->ref_cmd = ref_cmd; + ds->name = name; + ds->is_active = is_active; + ds->read_only = read_only; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &set_officer_run, + .cleanup = &set_officer_cleanup, + .traits = &set_officer_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_set_officer.c */ diff --git a/src/testing/testing_api_cmd_set_wire_fee.c b/src/testing/testing_api_cmd_set_wire_fee.c index 613e0f713..460a71e40 100644 --- a/src/testing/testing_api_cmd_set_wire_fee.c +++ b/src/testing/testing_api_cmd_set_wire_fee.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020, 2022 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 @@ -77,27 +77,21 @@ struct WireFeeState * if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param sfr response details */ static void wire_add_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementSetWireFeeResponse *sfr) { struct WireFeeState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &sfr->hr; ds->dh = NULL; if (ds->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response_code); return; } TALER_TESTING_interpreter_next (ds->is); @@ -119,25 +113,41 @@ wire_add_run (void *cls, struct WireFeeState *ds = cls; struct TALER_MasterSignatureP master_sig; struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Absolute start_time; - struct GNUNET_TIME_Absolute end_time; - struct TALER_Amount wire_fee; - struct TALER_Amount closing_fee; + struct GNUNET_TIME_Timestamp start_time; + struct GNUNET_TIME_Timestamp end_time; + struct TALER_WireFeeSet fees; + const char *exchange_url; (void) cmd; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } ds->is = is; now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); - start_time = GNUNET_TIME_absolute_subtract (now, - GNUNET_TIME_UNIT_HOURS); - end_time = GNUNET_TIME_absolute_add (now, - GNUNET_TIME_UNIT_HOURS); + start_time = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_subtract (now, + GNUNET_TIME_UNIT_HOURS)); + end_time = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (now, + GNUNET_TIME_UNIT_HOURS)); if ( (GNUNET_OK != TALER_string_to_amount (ds->closing_fee, - &closing_fee)) || + &fees.closing)) || (GNUNET_OK != TALER_string_to_amount (ds->wire_fee, - &wire_fee)) ) + &fees.wire)) ) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); @@ -152,22 +162,34 @@ wire_add_run (void *cls, } else { + const struct TALER_TESTING_Command *exchange_cmd; + const struct TALER_MasterPrivateKeyP *master_priv; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); TALER_exchange_offline_wire_fee_sign (ds->wire_method, start_time, end_time, - &wire_fee, - &closing_fee, - &is->master_priv, + &fees, + master_priv, &master_sig); } ds->dh = TALER_EXCHANGE_management_set_wire_fees ( - is->ctx, - is->exchange_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, ds->wire_method, start_time, end_time, - &wire_fee, - &closing_fee, + &fees, &master_sig, &wire_add_cb, ds); @@ -195,10 +217,8 @@ wire_add_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); TALER_EXCHANGE_management_set_wire_fees_cancel (ds->dh); ds->dh = NULL; } @@ -206,26 +226,6 @@ wire_add_cleanup (void *cls, } -/** - * Offer internal data from a "wire_add" CMD, to other commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * - * @return #GNUNET_OK on success. - */ -static int -wire_add_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_NO; -} - - struct TALER_TESTING_Command TALER_TESTING_cmd_set_wire_fee (const char *label, const char *wire_method, @@ -247,8 +247,7 @@ TALER_TESTING_cmd_set_wire_fee (const char *label, .cls = ds, .label = label, .run = &wire_add_run, - .cleanup = &wire_add_cleanup, - .traits = &wire_add_traits + .cleanup = &wire_add_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_signal.c b/src/testing/testing_api_cmd_signal.c index b2116ebf6..be3a58bdd 100644 --- a/src/testing/testing_api_cmd_signal.c +++ b/src/testing/testing_api_cmd_signal.c @@ -62,7 +62,6 @@ signal_run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Signaling '%d'..\n", ss->signal); - sleep (6); TALER_TESTING_interpreter_next (is); } diff --git a/src/testing/testing_api_cmd_stat.c b/src/testing/testing_api_cmd_stat.c index 9461a22cf..8723aac0d 100644 --- a/src/testing/testing_api_cmd_stat.c +++ b/src/testing/testing_api_cmd_stat.c @@ -28,43 +28,16 @@ /** - * Cleanup the state from a "stat service" CMD. + * Run a "stat" CMD. * * @param cls closure. - * @param cmd the command which is being cleaned up. + * @param cmd the command being run. + * @param is the interpreter state. */ static void -stat_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - (void) cls; - (void) cmd; - /* nothing to clean. */ -} - - -/** - * No traits to offer, just provide a stub to be called when - * some CMDs iterates through the list of all the commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the trait to return. - * @return #GNUNET_OK on success. - */ -static int -stat_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - (void) cls; - (void) ret; - (void) trait; - (void) index; - return GNUNET_NO; -} +stat_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is); /** @@ -80,9 +53,20 @@ stat_cmd (struct TALER_TESTING_Timer *timings, struct GNUNET_TIME_Relative duration; struct GNUNET_TIME_Relative lat; - if (cmd->start_time.abs_value_us > cmd->finish_time.abs_value_us) + if (GNUNET_TIME_absolute_cmp (cmd->start_time, + >, + cmd->finish_time)) { - GNUNET_break (0); + /* This is a problem, except of course for + this command itself, as we clearly did not yet + finish... */ + if (cmd->run != &stat_run) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Bad timings for `%s'\n", + cmd->label); + GNUNET_break (0); + } return; } duration = GNUNET_TIME_absolute_get_difference (cmd->start_time, @@ -114,36 +98,35 @@ stat_cmd (struct TALER_TESTING_Timer *timings, /** * Obtain statistics for @a timings of @a cmd * - * @param timings what timings to get + * @param[in,out] cls what timings to get * @param cmd command to process */ static void -do_stat (struct TALER_TESTING_Timer *timings, +do_stat (void *cls, const struct TALER_TESTING_Command *cmd) { + struct TALER_TESTING_Timer *timings = cls; + if (TALER_TESTING_cmd_is_batch (cmd)) { -#define BATCH_INDEX 1 struct TALER_TESTING_Command *bcmd; if (GNUNET_OK != - TALER_TESTING_get_trait_cmd (cmd, - BATCH_INDEX, - &bcmd)) + TALER_TESTING_get_trait_batch_cmds (cmd, + &bcmd)) { GNUNET_break (0); return; } - - for (unsigned int j = 0; NULL != bcmd[j].label; j++) + for (unsigned int j = 0; + NULL != bcmd[j].label; + j++) do_stat (timings, &bcmd[j]); + return; } - else - { - stat_cmd (timings, - cmd); - } + stat_cmd (timings, + cmd); } @@ -161,31 +144,20 @@ stat_run (void *cls, { struct TALER_TESTING_Timer *timings = cls; - for (unsigned int i = 0; NULL != is->commands[i].label; i++) - { - if (cmd == &is->commands[i]) - break; /* skip at our current command */ - do_stat (timings, - &is->commands[i]); - } + TALER_TESTING_iterate (is, + true, + &do_stat, + timings); TALER_TESTING_interpreter_next (is); } -/** - * Obtain performance data from the interpreter. - * - * @param timers what commands (by label) to obtain runtimes for - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_stat (struct TALER_TESTING_Timer *timers) { struct TALER_TESTING_Command cmd = { .label = "stat", - .run = stat_run, - .cleanup = stat_cleanup, - .traits = stat_traits, + .run = &stat_run, .cls = (void *) timers }; @@ -193,4 +165,4 @@ TALER_TESTING_cmd_stat (struct TALER_TESTING_Timer *timers) } -/* end of testing_api_cmd_sleep.c */ +/* end of testing_api_cmd_stat.c */ diff --git a/src/testing/testing_api_cmd_status.c b/src/testing/testing_api_cmd_status.c deleted file mode 100644 index 6acfcc75b..000000000 --- a/src/testing/testing_api_cmd_status.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_cmd_status.c - * @brief Implement the /reserve/status test command. - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_testing_lib.h" - - -/** - * State for a "status" CMD. - */ -struct StatusState -{ - /** - * Label to the command which created the reserve to check, - * needed to resort the reserve key. - */ - const char *reserve_reference; - - /** - * Handle to the "reserve status" operation. - */ - struct TALER_EXCHANGE_ReservesGetHandle *rsh; - - /** - * Expected reserve balance. - */ - const char *expected_balance; - - /** - * Public key of the reserve being analyzed. - */ - const struct TALER_ReservePublicKeyP *reserve_pubp; - - /** - * Expected HTTP response code. - */ - unsigned int expected_response_code; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; -}; - - -/** - * Compare @a h1 and @a h2. - * - * @param h1 a history entry - * @param h2 a history entry - * @return 0 if @a h1 and @a h2 are equal - */ -static int -history_entry_cmp (const struct TALER_EXCHANGE_ReserveHistory *h1, - const struct TALER_EXCHANGE_ReserveHistory *h2) -{ - if (h1->type != h2->type) - return 1; - switch (h1->type) - { - case TALER_EXCHANGE_RTT_CREDIT: - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == strcasecmp (h1->details.in_details.sender_url, - h2->details.in_details.sender_url)) && - (h1->details.in_details.wire_reference == - h2->details.in_details.wire_reference) && - (h1->details.in_details.timestamp.abs_value_us == - h2->details.in_details.timestamp.abs_value_us) ) - return 0; - return 1; - case TALER_EXCHANGE_RTT_WITHDRAWAL: - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == - TALER_amount_cmp (&h1->details.withdraw.fee, - &h2->details.withdraw.fee)) ) - /* testing_api_cmd_withdraw doesn't set the out_authorization_sig, - so we cannot test for it here. but if the amount matches, - that should be good enough. */ - return 0; - return 1; - case TALER_EXCHANGE_RTT_RECOUP: - /* exchange_sig, exchange_pub and timestamp are NOT available - from the original recoup response, hence here NOT check(able/ed) */ - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == - GNUNET_memcmp (&h1->details.recoup_details.coin_pub, - &h2->details.recoup_details.coin_pub)) ) - return 0; - return 1; - case TALER_EXCHANGE_RTT_CLOSE: - /* testing_api_cmd_exec_closer doesn't set the - receiver_account_details, exchange_sig, exchange_pub or wtid or timestamp - so we cannot test for it here. but if the amount matches, - that should be good enough. */ - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == - TALER_amount_cmp (&h1->details.close_details.fee, - &h2->details.close_details.fee)) ) - return 0; - return 1; - } - GNUNET_assert (0); - return 1; -} - - -/** - * Check if @a cmd changed the reserve, if so, find the - * entry in @a history and set the respective index in @a found - * to #GNUNET_YES. If the entry is not found, return #GNUNET_SYSERR. - * - * @param reserve_pub public key of the reserve for which we have the @a history - * @param cmd command to analyze for impact on history - * @param history_length number of entries in @a history and @a found - * @param history history to check - * @param[in,out] found array to update - * @return #GNUNET_OK if @a cmd action on reserve was found in @a history - */ -static int -analyze_command (const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_TESTING_Command *cmd, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history, - int *found) -{ - if (TALER_TESTING_cmd_is_batch (cmd)) - { -#define BATCH_INDEX 1 - struct TALER_TESTING_Command *cur; - struct TALER_TESTING_Command *bcmd; - - cur = TALER_TESTING_cmd_batch_get_current (cmd); - if (GNUNET_OK != - TALER_TESTING_get_trait_cmd (cmd, - BATCH_INDEX, - &bcmd)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 0; NULL != bcmd[i].label; i++) - { - struct TALER_TESTING_Command *step = &bcmd[i]; - - if (step == cur) - break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */ - if (GNUNET_OK != - analyze_command (reserve_pub, - step, - history_length, - history, - found)) - return GNUNET_SYSERR; - } - return GNUNET_OK; - } - else - { - const struct TALER_ReservePublicKeyP *rp; - const struct TALER_EXCHANGE_ReserveHistory *he; - - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_pub (cmd, - 0, - &rp)) - return GNUNET_OK; /* command does nothing for reserves */ - if (0 != - GNUNET_memcmp (rp, - reserve_pub)) - return GNUNET_OK; /* command affects some _other_ reserve */ - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_history (cmd, - 0, - &he)) - { - /* NOTE: only for debugging... */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Command `%s' has the reserve_pub trait, but does not reserve history trait\n", - cmd->label); - return GNUNET_OK; /* command does nothing for reserves */ - } - for (unsigned int i = 0; i<history_length; i++) - { - if (found[i]) - continue; /* already found, skip */ - if (0 == - history_entry_cmp (he, - &history[i])) - { - found[i] = GNUNET_YES; - return GNUNET_OK; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Command `%s' reserve history entry not found\n", - cmd->label); - return GNUNET_SYSERR; - } -} - - -/** - * Check that the reserve balance and HTTP response code are - * both acceptable. - * - * @param cls closure. - * @param hr HTTP response details - * @param balance current balance in the reserve, NULL on error. - * @param history_length number of entries in the transaction - * history, 0 on error. - * @param history detailed transaction history, NULL on error. - */ -static void -reserve_status_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history) -{ - struct StatusState *ss = cls; - struct TALER_TESTING_Interpreter *is = ss->is; - struct TALER_Amount eb; - - ss->rsh = NULL; - if (ss->expected_response_code != hr->http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected HTTP response code: %d in %s:%u\n", - hr->http_status, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ss->is); - return; - } - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (ss->expected_balance, - &eb)); - - if (0 != TALER_amount_cmp (&eb, - balance)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected amount in reserve: %s\n", - TALER_amount_to_string (balance)); - TALER_TESTING_interpreter_fail (ss->is); - return; - } - { - int found[history_length]; - - memset (found, - 0, - sizeof (found)); - for (unsigned int i = 0; i<=is->ip; i++) - { - struct TALER_TESTING_Command *cmd = &is->commands[i]; - - if (GNUNET_OK != - analyze_command (ss->reserve_pubp, - cmd, - history_length, - history, - found)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Entry for command `%s' missing in history\n", - cmd->label); - TALER_TESTING_interpreter_fail (ss->is); - return; - } - } - for (unsigned int i = 0; i<history_length; i++) - if (! found[i]) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "History entry at index %u of type %d not justified by command history\n", - i, - history[i].type); - TALER_TESTING_interpreter_fail (ss->is); - return; - } - } - TALER_TESTING_interpreter_next (is); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command being executed. - * @param is the interpreter state. - */ -static void -status_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct StatusState *ss = cls; - const struct TALER_TESTING_Command *create_reserve; - - ss->is = is; - create_reserve - = TALER_TESTING_interpreter_lookup_command (is, - ss->reserve_reference); - - if (NULL == create_reserve) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_pub (create_reserve, - 0, - &ss->reserve_pubp)) - { - GNUNET_break (0); - TALER_LOG_ERROR ("Failed to find reserve_pub for status query\n"); - TALER_TESTING_interpreter_fail (is); - return; - } - ss->rsh = TALER_EXCHANGE_reserves_get (is->exchange, - ss->reserve_pubp, - GNUNET_TIME_UNIT_ZERO, - &reserve_status_cb, - ss); -} - - -/** - * Cleanup the state from a "reserve status" CMD, and possibly - * cancel a pending operation thereof. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -status_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct StatusState *ss = cls; - - if (NULL != ss->rsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ss->is->ip, - cmd->label); - TALER_EXCHANGE_reserves_get_cancel (ss->rsh); - ss->rsh = NULL; - } - GNUNET_free (ss); -} - - -/** - * Create a "reserve status" command. - * - * @param label the command label. - * @param reserve_reference reference to the reserve to check. - * @param expected_balance expected balance for the reserve. - * @param expected_response_code expected HTTP response code. - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_status (const char *label, - const char *reserve_reference, - const char *expected_balance, - unsigned int expected_response_code) -{ - struct StatusState *ss; - - GNUNET_assert (NULL != reserve_reference); - ss = GNUNET_new (struct StatusState); - ss->reserve_reference = reserve_reference; - ss->expected_balance = expected_balance; - ss->expected_response_code = expected_response_code; - { - struct TALER_TESTING_Command cmd = { - .cls = ss, - .label = label, - .run = &status_run, - .cleanup = &status_cleanup - }; - - return cmd; - } -} diff --git a/src/testing/testing_api_cmd_system_start.c b/src/testing/testing_api_cmd_system_start.c new file mode 100644 index 000000000..541ad75c1 --- /dev/null +++ b/src/testing/testing_api_cmd_system_start.c @@ -0,0 +1,395 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_system_start.c + * @brief run taler-benchmark-setup.sh command + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +/** + * State for a "system" CMD. + */ +struct SystemState +{ + + /** + * System process. + */ + struct GNUNET_OS_Process *system_proc; + + /** + * Input pipe to @e system_proc, used to keep the + * process alive until we are done. + */ + struct GNUNET_DISK_PipeHandle *pipe_in; + + /** + * Output pipe to @e system_proc, used to find out + * when the services are ready. + */ + struct GNUNET_DISK_PipeHandle *pipe_out; + + /** + * Task reading from @e pipe_in. + */ + struct GNUNET_SCHEDULER_Task *reader; + + /** + * Waiting for child to die. + */ + struct GNUNET_ChildWaitHandle *cwh; + + /** + * Our interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * NULL-terminated array of command-line arguments. + */ + char **args; + + /** + * Current input buffer, 0-terminated. Contains the last 15 bytes of input + * so we can search them again for the "<<READY>>" tag. + */ + char ibuf[16]; + + /** + * Did we find the ready tag? + */ + bool ready; + + /** + * Is the child process still running? + */ + bool active; +}; + + +/** + * Defines a GNUNET_ChildCompletedCallback which is sent back + * upon death or completion of a child process. + * + * @param cls our `struct SystemState *` + * @param type type of the process + * @param exit_code status code of the process + */ +static void +setup_terminated (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + struct SystemState *as = cls; + + as->cwh = NULL; + as->active = false; + if (NULL != as->reader) + { + GNUNET_SCHEDULER_cancel (as->reader); + as->reader = NULL; + } + if (! as->ready) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Launching Taler system failed: %d/%llu\n", + (int) type, + (unsigned long long) exit_code); + TALER_TESTING_interpreter_fail (as->is); + return; + } +} + + +/** + * Start helper to read from stdout of child. + * + * @param as our system state + */ +static void +start_reader (struct SystemState *as); + + +static void +read_stdout (void *cls) +{ + struct SystemState *as = cls; + const struct GNUNET_DISK_FileHandle *fh; + char buf[1024 * 10]; + ssize_t ret; + size_t off = 0; + + as->reader = NULL; + strcpy (buf, + as->ibuf); + off = strlen (buf); + fh = GNUNET_DISK_pipe_handle (as->pipe_out, + GNUNET_DISK_PIPE_END_READ); + ret = GNUNET_DISK_file_read (fh, + &buf[off], + sizeof (buf) - off); + if (-1 == ret) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "read"); + TALER_TESTING_interpreter_fail (as->is); + return; + } + if (0 == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Child closed stdout\n"); + return; + } + /* forward log, except single '.' outputs */ + if ( (1 != ret) || + ('.' != buf[off]) ) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "TUS: %.*s\n", + (int) ret, + &buf[off]); + start_reader (as); + off += ret; + if (as->ready) + { + /* already done */ + return; + } + if (NULL != + memmem (buf, + off, + "\n<<READY>>\n", + strlen ("\n<<READY>>\n"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Taler system UP\n"); + as->ready = true; + TALER_TESTING_interpreter_next (as->is); + return; + } + + { + size_t mcpy; + + mcpy = GNUNET_MIN (off, + sizeof (as->ibuf) - 1); + memcpy (as->ibuf, + &buf[off - mcpy], + mcpy); + as->ibuf[mcpy] = '\0'; + } +} + + +static void +start_reader (struct SystemState *as) +{ + const struct GNUNET_DISK_FileHandle *fh; + + GNUNET_assert (NULL == as->reader); + fh = GNUNET_DISK_pipe_handle (as->pipe_out, + GNUNET_DISK_PIPE_END_READ); + as->reader = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + fh, + &read_stdout, + as); +} + + +/** + * Run the command. Use the `taler-exchange-system' program. + * + * @param cls closure. + * @param cmd command being run. + * @param is interpreter state. + */ +static void +system_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct SystemState *as = cls; + + (void) cmd; + as->is = is; + as->pipe_in = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); + GNUNET_assert (NULL != as->pipe_in); + as->pipe_out = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); + GNUNET_assert (NULL != as->pipe_out); + as->system_proc + = GNUNET_OS_start_process_vap ( + GNUNET_OS_INHERIT_STD_ERR, + as->pipe_in, as->pipe_out, NULL, + "taler-unified-setup.sh", + as->args); + if (NULL == as->system_proc) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + as->active = true; + start_reader (as); + as->cwh = GNUNET_wait_child (as->system_proc, + &setup_terminated, + as); +} + + +/** + * Free the state of a "system" CMD, and possibly kill its + * process if it did not terminate correctly. + * + * @param cls closure. + * @param cmd the command being freed. + */ +static void +system_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct SystemState *as = cls; + + (void) cmd; + if (NULL != as->cwh) + { + GNUNET_wait_child_cancel (as->cwh); + as->cwh = NULL; + } + if (NULL != as->reader) + { + GNUNET_SCHEDULER_cancel (as->reader); + as->reader = NULL; + } + if (NULL != as->system_proc) + { + if (as->active) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (as->system_proc, + SIGTERM)); + GNUNET_OS_process_wait (as->system_proc); + } + GNUNET_OS_process_destroy (as->system_proc); + as->system_proc = NULL; + } + if (NULL != as->pipe_in) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (as->pipe_in)); + as->pipe_in = NULL; + } + if (NULL != as->pipe_out) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (as->pipe_out)); + as->pipe_out = NULL; + } + + for (unsigned int i = 0; NULL != as->args[i]; i++) + GNUNET_free (as->args[i]); + GNUNET_free (as->args); + GNUNET_free (as); +} + + +/** + * Offer "system" CMD internal data to other commands. + * + * @param cls closure. + * @param[out] ret result. + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +system_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct SystemState *as = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_process (&as->system_proc), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_system_start ( + const char *label, + const char *config_file, + ...) +{ + struct SystemState *as; + va_list ap; + const char *arg; + unsigned int cnt; + + as = GNUNET_new (struct SystemState); + cnt = 4; /* 0-2 reserved, +1 for NULL termination */ + va_start (ap, + config_file); + while (NULL != (arg = va_arg (ap, + const char *))) + { + cnt++; + } + va_end (ap); + as->args = GNUNET_new_array (cnt, + char *); + as->args[0] = GNUNET_strdup ("taler-unified-setup"); + as->args[1] = GNUNET_strdup ("-c"); + as->args[2] = GNUNET_strdup (config_file); + cnt = 3; + va_start (ap, + config_file); + while (NULL != (arg = va_arg (ap, + const char *))) + { + as->args[cnt++] = GNUNET_strdup (arg); + } + va_end (ap); + + { + struct TALER_TESTING_Command cmd = { + .cls = as, + .label = label, + .run = &system_run, + .cleanup = &system_cleanup, + .traits = &system_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_system_start.c */ diff --git a/src/testing/testing_api_cmd_take_aml_decision.c b/src/testing/testing_api_cmd_take_aml_decision.c new file mode 100644 index 000000000..c0e23de22 --- /dev/null +++ b/src/testing/testing_api_cmd_take_aml_decision.c @@ -0,0 +1,321 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_cmd_take_aml_decision.c + * @brief command for testing /aml/$OFFICER_PUB/decision + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" +#include "taler_signatures.h" +#include "backoff.h" + + +/** + * State for a "take_aml_decision" CMD. + */ +struct AmlDecisionState +{ + + /** + * Auditor enable handle while operation is running. + */ + struct TALER_EXCHANGE_AddAmlDecision *dh; + + /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to command to previous set officer command that gives + * us an officer_priv trait. + */ + const char *officer_ref_cmd; + + /** + * Reference to command to previous AML-triggering event that gives + * us a payto-hash trait. + */ + const char *account_ref_cmd; + + /** + * Payto hash of the account we are manipulating the AML settings for. + */ + struct TALER_PaytoHashP h_payto; + + /** + * New AML state to use. + */ + enum TALER_AmlDecisionState new_state; + + /** + * Justification given. + */ + const char *justification; + + /** + * KYC requirement to add. + */ + const char *kyc_requirement; + + /** + * Threshold transaction amount. + */ + struct TALER_Amount new_threshold; + + /** + * Expected response code. + */ + unsigned int expected_response; +}; + + +/** + * Callback to analyze the /aml-decision/$OFFICER_PUB response, just used to check + * if the response code is acceptable. + * + * @param cls closure. + * @param adr response details + */ +static void +take_aml_decision_cb ( + void *cls, + const struct TALER_EXCHANGE_AddAmlDecisionResponse *adr) +{ + struct AmlDecisionState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr; + + ds->dh = NULL; + if (ds->expected_response != hr->http_status) + { + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response); + return; + } + TALER_TESTING_interpreter_next (ds->is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. + */ +static void +take_aml_decision_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct AmlDecisionState *ds = cls; + struct GNUNET_TIME_Timestamp now; + const struct TALER_PaytoHashP *h_payto; + const struct TALER_AmlOfficerPrivateKeyP *officer_priv; + const struct TALER_TESTING_Command *ref; + json_t *kyc_requirements = NULL; + const char *exchange_url; + + (void) cmd; + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } + now = GNUNET_TIME_timestamp_get (); + ds->is = is; + ref = TALER_TESTING_interpreter_lookup_command (is, + ds->account_ref_cmd); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_h_payto (ref, + &h_payto)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + ref = TALER_TESTING_interpreter_lookup_command (is, + ds->officer_ref_cmd); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_officer_priv (ref, + &officer_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + ds->h_payto = *h_payto; + if (NULL != ds->kyc_requirement) + { + kyc_requirements = json_array (); + GNUNET_assert (NULL != kyc_requirements); + GNUNET_assert (0 == + json_array_append (kyc_requirements, + json_string (ds->kyc_requirement))); + } + + ds->dh = TALER_EXCHANGE_add_aml_decision ( + TALER_TESTING_interpreter_get_context (is), + exchange_url, + ds->justification, + now, + &ds->new_threshold, + h_payto, + ds->new_state, + kyc_requirements, + officer_priv, + &take_aml_decision_cb, + ds); + json_decref (kyc_requirements); + if (NULL == ds->dh) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } +} + + +/** + * Free the state of a "take_aml_decision" CMD, and possibly cancel a + * pending operation thereof. + * + * @param cls closure, must be a `struct AmlDecisionState`. + * @param cmd the command which is being cleaned up. + */ +static void +take_aml_decision_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct AmlDecisionState *ds = cls; + + if (NULL != ds->dh) + { + TALER_TESTING_command_incomplete (ds->is, + cmd->label); + TALER_EXCHANGE_add_aml_decision_cancel (ds->dh); + ds->dh = NULL; + } + GNUNET_free (ds); +} + + +/** + * Offer internal data of a "AML decision" CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to offer. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +take_aml_decision_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct AmlDecisionState *ws = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_h_payto (&ws->h_payto), + TALER_TESTING_make_trait_aml_justification (ws->justification), + TALER_TESTING_make_trait_aml_decision (&ws->new_state), + TALER_TESTING_make_trait_amount (&ws->new_threshold), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_take_aml_decision ( + const char *label, + const char *ref_officer, + const char *ref_operation, + const char *new_threshold, + const char *justification, + enum TALER_AmlDecisionState new_state, + const char *kyc_requirement, + unsigned int expected_response) +{ + struct AmlDecisionState *ds; + + ds = GNUNET_new (struct AmlDecisionState); + ds->officer_ref_cmd = ref_officer; + ds->account_ref_cmd = ref_operation; + ds->kyc_requirement = kyc_requirement; + if (GNUNET_OK != + TALER_string_to_amount (new_threshold, + &ds->new_threshold)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %s\n", + new_threshold, + label); + GNUNET_assert (0); + } + ds->new_state = new_state; + ds->justification = justification; + ds->expected_response = expected_response; + { + struct TALER_TESTING_Command cmd = { + .cls = ds, + .label = label, + .run = &take_aml_decision_run, + .cleanup = &take_aml_decision_cleanup, + .traits = &take_aml_decision_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_take_aml_decision.c */ diff --git a/src/testing/testing_api_cmd_transfer_get.c b/src/testing/testing_api_cmd_transfer_get.c index 3ca319cb5..405c8b7f9 100644 --- a/src/testing/testing_api_cmd_transfer_get.c +++ b/src/testing/testing_api_cmd_transfer_get.c @@ -44,6 +44,11 @@ struct TrackTransferState const char *expected_wire_fee; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Reference to any operation that can provide a WTID. * Will be the WTID to track. */ @@ -79,11 +84,6 @@ struct TrackTransferState */ unsigned int expected_response_code; - /** - * Index to the WTID to pick, in case @a wtid_reference has - * many on offer. - */ - unsigned int index; }; @@ -103,10 +103,8 @@ track_transfer_cleanup (void *cls, if (NULL != tts->tth) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - tts->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (tts->is, + cmd->label); TALER_EXCHANGE_transfers_get_cancel (tts->tth); tts->tth = NULL; } @@ -120,182 +118,178 @@ track_transfer_cleanup (void *cls, * wire fees and hashed wire details as well. * * @param cls closure. - * @param hr HTTP response details - * @param ta transfer data returned by the exchange + * @param tgr response details */ static void track_transfer_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_EXCHANGE_TransferData *ta) + const struct TALER_EXCHANGE_TransfersGetResponse *tgr) { struct TrackTransferState *tts = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &tgr->hr; struct TALER_TESTING_Interpreter *is = tts->is; - struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; struct TALER_Amount expected_amount; tts->tth = NULL; if (tts->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - hr->http_status, - (int) hr->ec, - cmd->label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (is); + TALER_TESTING_unexpected_status (is, + hr->http_status, + tts->expected_response_code); return; } switch (hr->http_status) { case MHD_HTTP_OK: - if (NULL == tts->expected_total_amount) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - if (NULL == tts->expected_wire_fee) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - if (GNUNET_OK != - TALER_string_to_amount (tts->expected_total_amount, - &expected_amount)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - if (0 != TALER_amount_cmp (&ta->total_amount, - &expected_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Total amount mismatch to command %s - " - "%s vs %s\n", - cmd->label, - TALER_amount_to_string (&ta->total_amount), - TALER_amount_to_string (&expected_amount)); - json_dumpf (hr->reply, - stderr, - 0); - fprintf (stderr, "\n"); - TALER_TESTING_interpreter_fail (is); - return; - } - - if (GNUNET_OK != - TALER_string_to_amount (tts->expected_wire_fee, - &expected_amount)) { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - if (0 != TALER_amount_cmp (&ta->wire_fee, - &expected_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fee mismatch to command %s\n", - cmd->label); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (is); - return; - } + const struct TALER_EXCHANGE_TransferData *ta + = &tgr->details.ok.td; - /** - * Optionally checking: (1) wire-details for this transfer - * match the ones from a referenced "deposit" operation - - * or any operation that could provide wire-details. (2) - * Total amount for this transfer matches the one from any - * referenced command that could provide one. - */if (NULL != tts->wire_details_reference) - { - const struct TALER_TESTING_Command *wire_details_cmd; - const json_t *wire_details; - struct GNUNET_HashCode h_wire_details; - - wire_details_cmd - = TALER_TESTING_interpreter_lookup_command (is, - tts->wire_details_reference); - if (NULL == wire_details_cmd) + if (NULL == tts->expected_total_amount) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } + if (NULL == tts->expected_wire_fee) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != - TALER_TESTING_get_trait_wire_details (wire_details_cmd, - 0, - &wire_details)) + TALER_string_to_amount (tts->expected_total_amount, + &expected_amount)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - GNUNET_assert (GNUNET_OK == - TALER_JSON_merchant_wire_signature_hash (wire_details, - &h_wire_details)); - if (0 != GNUNET_memcmp (&h_wire_details, - &ta->h_wire)) + if (0 != TALER_amount_cmp (&ta->total_amount, + &expected_amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire hash missmath to command %s\n", - cmd->label); + "Total amount mismatch to command %s - " + "%s vs %s\n", + tts->cmd->label, + TALER_amount_to_string (&ta->total_amount), + TALER_amount_to_string (&expected_amount)); json_dumpf (hr->reply, stderr, 0); + fprintf (stderr, "\n"); TALER_TESTING_interpreter_fail (is); return; } - } - if (NULL != tts->total_amount_reference) - { - const struct TALER_TESTING_Command *total_amount_cmd; - const struct TALER_Amount *total_amount_from_reference; - total_amount_cmd - = TALER_TESTING_interpreter_lookup_command (is, - tts->total_amount_reference); - if (NULL == total_amount_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } if (GNUNET_OK != - TALER_TESTING_get_trait_amount_obj (total_amount_cmd, - 0, - &total_amount_from_reference)) + TALER_string_to_amount (tts->expected_wire_fee, + &expected_amount)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - if (0 != TALER_amount_cmp (&ta->total_amount, - total_amount_from_reference)) + + if (0 != TALER_amount_cmp (&ta->wire_fee, + &expected_amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Amount missmath to command %s\n", - cmd->label); + "Wire fee mismatch to command %s\n", + tts->cmd->label); json_dumpf (hr->reply, stderr, 0); TALER_TESTING_interpreter_fail (is); return; } - } - } + + /** + * Optionally checking: (1) wire-details for this transfer + * match the ones from a referenced "deposit" operation - + * or any operation that could provide wire-details. (2) + * Total amount for this transfer matches the one from any + * referenced command that could provide one. + */ + if (NULL != tts->wire_details_reference) + { + const struct TALER_TESTING_Command *wire_details_cmd; + const char *payto_uri; + struct TALER_PaytoHashP h_payto; + + wire_details_cmd + = TALER_TESTING_interpreter_lookup_command (is, + tts-> + wire_details_reference); + if (NULL == wire_details_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_payto_uri (wire_details_cmd, + &payto_uri)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_payto_hash (payto_uri, + &h_payto); + if (0 != GNUNET_memcmp (&h_payto, + &ta->h_payto)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire hash missmath to command %s\n", + tts->cmd->label); + json_dumpf (hr->reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (is); + return; + } + } + if (NULL != tts->total_amount_reference) + { + const struct TALER_TESTING_Command *total_amount_cmd; + const struct TALER_Amount *total_amount_from_reference; + + total_amount_cmd + = TALER_TESTING_interpreter_lookup_command (is, + tts-> + total_amount_reference); + if (NULL == total_amount_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_amount (total_amount_cmd, + &total_amount_from_reference)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (0 != TALER_amount_cmp (&ta->total_amount, + total_amount_from_reference)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Amount mismatch in command %s\n", + tts->cmd->label); + json_dumpf (hr->reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (is); + return; + } + } + break; + } /* case OK */ + } /* switch on status */ TALER_TESTING_interpreter_next (is); } @@ -317,19 +311,20 @@ track_transfer_run (void *cls, struct TALER_WireTransferIdentifierRawP wtid; const struct TALER_WireTransferIdentifierRawP *wtid_ptr; + tts->cmd = cmd; /* If no reference is given, we'll use a all-zeros * WTID */ - memset (&wtid, 0, sizeof (wtid)); + memset (&wtid, + 0, + sizeof (wtid)); wtid_ptr = &wtid; - tts->is = is; if (NULL != tts->wtid_reference) { const struct TALER_TESTING_Command *wtid_cmd; - wtid_cmd = TALER_TESTING_interpreter_lookup_command - (tts->is, tts->wtid_reference); - + wtid_cmd = TALER_TESTING_interpreter_lookup_command (tts->is, + tts->wtid_reference); if (NULL == wtid_cmd) { GNUNET_break (0); @@ -337,8 +332,9 @@ track_transfer_run (void *cls, return; } - if (GNUNET_OK != TALER_TESTING_get_trait_wtid - (wtid_cmd, tts->index, &wtid_ptr)) + if (GNUNET_OK != + TALER_TESTING_get_trait_wtid (wtid_cmd, + &wtid_ptr)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); @@ -346,41 +342,26 @@ track_transfer_run (void *cls, } GNUNET_assert (NULL != wtid_ptr); } - tts->tth = TALER_EXCHANGE_transfers_get (is->exchange, - wtid_ptr, - &track_transfer_cb, - tts); + tts->tth = TALER_EXCHANGE_transfers_get ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + wtid_ptr, + &track_transfer_cb, + tts); GNUNET_assert (NULL != tts->tth); } -/** - * Make a "track transfer" CMD where no "expected"-arguments, - * except the HTTP response code, are given. The best use case - * is when what matters to check is the HTTP response code, e.g. - * when a bogus WTID was passed. - * - * @param label the command label - * @param wtid_reference reference to any command which can provide - * a wtid. If NULL is given, then a all zeroed WTID is - * used that will at 99.9999% probability NOT match any - * existing WTID known to the exchange. - * @param index index number of the WTID to track, in case there - * are multiple on offer. - * @param expected_response_code expected HTTP response code. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_track_transfer_empty (const char *label, const char *wtid_reference, - unsigned int index, unsigned int expected_response_code) { struct TrackTransferState *tts; tts = GNUNET_new (struct TrackTransferState); tts->wtid_reference = wtid_reference; - tts->index = index; tts->expected_response_code = expected_response_code; { struct TALER_TESTING_Command cmd = { @@ -395,25 +376,9 @@ TALER_TESTING_cmd_track_transfer_empty (const char *label, } -/** - * Make a "track transfer" command, specifying which amount and - * wire fee are expected. - * - * @param label the command label. - * @param wtid_reference reference to any command which can provide - * a wtid. Will be the one tracked. - * @param index in case there are multiple WTID offered, this - * parameter selects a particular one. - * @param expected_response_code expected HTTP response code. - * @param expected_total_amount how much money we expect being moved - * with this wire-transfer. - * @param expected_wire_fee expected wire fee. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_track_transfer (const char *label, const char *wtid_reference, - unsigned int index, unsigned int expected_response_code, const char *expected_total_amount, const char *expected_wire_fee) @@ -422,7 +387,6 @@ TALER_TESTING_cmd_track_transfer (const char *label, tts = GNUNET_new (struct TrackTransferState); tts->wtid_reference = wtid_reference; - tts->index = index; tts->expected_response_code = expected_response_code; tts->expected_total_amount = expected_total_amount; tts->expected_wire_fee = expected_wire_fee; diff --git a/src/testing/testing_api_cmd_twister_exec_client.c b/src/testing/testing_api_cmd_twister_exec_client.c index b3903f29a..bf83c1f80 100644 --- a/src/testing/testing_api_cmd_twister_exec_client.c +++ b/src/testing/testing_api_cmd_twister_exec_client.c @@ -194,7 +194,7 @@ hack_response_code_cleanup * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue hack_response_code_traits (void *cls, const void **ret, const char *trait, @@ -203,7 +203,7 @@ hack_response_code_traits (void *cls, struct HackResponseCodeState *hrcs = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &hrcs->proc), + TALER_TESTING_make_trait_process (&hrcs->proc), TALER_TESTING_trait_end () }; @@ -251,16 +251,6 @@ hack_response_code_run (void *cls, } -/** - * Define a "hack response code" CMD. This causes the next - * response code (from the service proxied by the twister) to - * be substituted with @a http_status. - * - * @param label command label - * @param config_filename configuration filename. - * @param http_status new response code to use - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_hack_response_code (const char *label, const char *config_filename, @@ -271,16 +261,17 @@ TALER_TESTING_cmd_hack_response_code (const char *label, hrcs = GNUNET_new (struct HackResponseCodeState); hrcs->http_status = http_status; hrcs->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &hack_response_code_run, - .cleanup = &hack_response_code_cleanup, - .traits = &hack_response_code_traits, - .cls = hrcs - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &hack_response_code_run, + .cleanup = &hack_response_code_cleanup, + .traits = &hack_response_code_traits, + .cls = hrcs + }; + + return cmd; + } } @@ -320,7 +311,7 @@ delete_object_cleanup * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue delete_object_traits (void *cls, const void **ret, const char *trait, @@ -329,7 +320,7 @@ delete_object_traits (void *cls, struct DeleteObjectState *dos = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &dos->proc), + TALER_TESTING_make_trait_process (&dos->proc), TALER_TESTING_trait_end () }; @@ -379,9 +370,8 @@ delete_object_run (void *cls, * @param cmd the command being cleaned up. */ static void -modify_object_cleanup - (void *cls, - const struct TALER_TESTING_Command *cmd) +modify_object_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) { struct ModifyObjectState *mos = cls; @@ -407,7 +397,7 @@ modify_object_cleanup * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue modify_object_traits (void *cls, const void **ret, const char *trait, @@ -416,7 +406,7 @@ modify_object_traits (void *cls, struct ModifyObjectState *mos = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &mos->proc), + TALER_TESTING_make_trait_process (&mos->proc), TALER_TESTING_trait_end () }; @@ -523,16 +513,6 @@ modify_header_dl_run (void *cls, } -/** - * Create a "delete object" CMD. This command deletes - * the JSON object pointed by @a path. - * - * @param label command label - * @param config_filename configuration filename. - * @param path object-like path notation to point the object - * to delete. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_delete_object (const char *label, const char *config_filename, @@ -592,16 +572,15 @@ flip_object_cleanup * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue flip_object_traits (void *cls, const void **ret, const char *trait, unsigned int index) { - struct FlipObjectState *fos = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &fos->proc), + TALER_TESTING_make_trait_process (&fos->proc), TALER_TESTING_trait_end () }; @@ -674,15 +653,6 @@ flip_download_run (void *cls, } -/** - * Define a "flip object" command, for objects to upload. - * - * @param label command label - * @param config_filename configuration filename. - * @param path object-like path notation to point the object - * to flip. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_flip_upload (const char *label, const char *config_filename, @@ -693,28 +663,20 @@ TALER_TESTING_cmd_flip_upload (const char *label, dos = GNUNET_new (struct FlipObjectState); dos->path = path; dos->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &flip_upload_run, - .cleanup = &flip_object_cleanup, - .traits = &flip_object_traits, - .cls = dos - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &flip_upload_run, + .cleanup = &flip_object_cleanup, + .traits = &flip_object_traits, + .cls = dos + }; + + return cmd; + } } -/** - * Define a "flip object" command, for objects to download. - * - * @param label command label - * @param config_filename configuration filename. - * @param path object-like path notation to point the object - * to flip. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_flip_download (const char *label, const char *config_filename, @@ -725,16 +687,17 @@ TALER_TESTING_cmd_flip_download (const char *label, dos = GNUNET_new (struct FlipObjectState); dos->path = path; dos->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &flip_download_run, - .cleanup = &flip_object_cleanup, - .traits = &flip_object_traits, - .cls = dos - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &flip_download_run, + .cleanup = &flip_object_cleanup, + .traits = &flip_object_traits, + .cls = dos + }; + + return cmd; + } } @@ -773,7 +736,7 @@ malform_request_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue malform_request_traits (void *cls, const void **ret, const char *trait, @@ -781,7 +744,7 @@ malform_request_traits (void *cls, { struct MalformRequestState *mrs = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &mrs->proc), + TALER_TESTING_make_trait_process (&mrs->proc), TALER_TESTING_trait_end () }; @@ -831,9 +794,8 @@ malform_request_run (void *cls, * @param cmd the command being cleaned up. */ static void -malform_response_cleanup - (void *cls, - const struct TALER_TESTING_Command *cmd) +malform_response_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) { struct MalformResponseState *mrs = cls; @@ -859,7 +821,7 @@ malform_response_cleanup * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue malform_response_traits (void *cls, const void **ret, const char *trait, @@ -867,7 +829,7 @@ malform_response_traits (void *cls, { struct MalformResponseState *mrs = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_process (0, &mrs->proc), + TALER_TESTING_make_trait_process (&mrs->proc), TALER_TESTING_trait_end () }; @@ -909,14 +871,6 @@ malform_response_run (void *cls, } -/** - * Create a "malform request" CMD. This command makes the - * next request randomly malformed (by truncating it). - * - * @param label command label - * @param config_filename configuration filename. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_malform_request (const char *label, const char *config_filename) @@ -925,28 +879,20 @@ TALER_TESTING_cmd_malform_request (const char *label, mrs = GNUNET_new (struct MalformRequestState); mrs->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &malform_request_run, - .cleanup = &malform_request_cleanup, - .traits = &malform_request_traits, - .cls = mrs - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &malform_request_run, + .cleanup = &malform_request_cleanup, + .traits = &malform_request_traits, + .cls = mrs + }; + + return cmd; + } } -/** - * Create a "malform response" CMD. This command makes - * the next response randomly malformed (by truncating it). - * - * @param label command label - * @param config_filename configuration filename. - * - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_malform_response (const char *label, const char *config_filename) @@ -969,19 +915,6 @@ TALER_TESTING_cmd_malform_response (const char *label, } -/** - * Create a "modify object" CMD. This command instructs - * the twister to modify the next object that is downloaded - * from the proxied service. - * - * @param label command label - * @param config_filename configuration filename. - * @param path object-like path notation to point the object - * to modify. - * @param value value to put as the object's. - * - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_modify_object_dl (const char *label, const char *config_filename, @@ -994,31 +927,20 @@ TALER_TESTING_cmd_modify_object_dl (const char *label, mos->path = path; mos->value = value; mos->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &modify_object_dl_run, - .cleanup = &modify_object_cleanup, - .traits = &modify_object_traits, - .cls = mos - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &modify_object_dl_run, + .cleanup = &modify_object_cleanup, + .traits = &modify_object_traits, + .cls = mos + }; + + return cmd; + } } -/** - * Create a "modify object" CMD. This command instructs - * the twister to modify the next object that will be uploaded - * to the proxied service. - * - * @param label command label - * @param config_filename configuration filename. - * @param path object-like path notation pointing the object - * to modify. - * @param value value to put as the object's. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_modify_object_ul (const char *label, const char *config_filename, @@ -1031,29 +953,20 @@ TALER_TESTING_cmd_modify_object_ul (const char *label, mos->path = path; mos->value = value; mos->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &modify_object_ul_run, - .cleanup = &modify_object_cleanup, - .traits = &modify_object_traits, - .cls = mos - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &modify_object_ul_run, + .cleanup = &modify_object_cleanup, + .traits = &modify_object_traits, + .cls = mos + }; + + return cmd; + } } -/** - * Create a "modify header" CMD. This command instructs - * the twister to modify a header in the next HTTP response. - * - * @param label command label - * @param config_filename configuration filename. - * @param path identifies the location to modify - * @param value value to set the header to. - * @return the command - */ struct TALER_TESTING_Command TALER_TESTING_cmd_modify_header_dl (const char *label, const char *config_filename, @@ -1066,16 +979,17 @@ TALER_TESTING_cmd_modify_header_dl (const char *label, mos->path = path; mos->value = value; mos->config_filename = config_filename; - - struct TALER_TESTING_Command cmd = { - .label = label, - .run = &modify_header_dl_run, - .cleanup = &modify_object_cleanup, - .traits = &modify_object_traits, - .cls = mos - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .run = &modify_header_dl_run, + .cleanup = &modify_object_cleanup, + .traits = &modify_object_traits, + .cls = mos + }; + + return cmd; + } } diff --git a/src/testing/testing_api_cmd_wire.c b/src/testing/testing_api_cmd_wire.c index 66d018c79..41ff7a978 100644 --- a/src/testing/testing_api_cmd_wire.c +++ b/src/testing/testing_api_cmd_wire.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018 Taler Systems SA + Copyright (C) 2018, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,6 +39,11 @@ struct WireState struct TALER_EXCHANGE_WireHandle *wh; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * Which wire-method we expect is offered by the exchange. */ const char *expected_method; @@ -72,19 +77,14 @@ struct WireState * that the wire fee is acceptable too. * * @param cls closure. - * @param hr HTTP response details - * @param accounts_len length of the @a accounts array. - * @param accounts list of wire accounts of the exchange, - * NULL on error. + * @param wr response details */ static void wire_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - unsigned int accounts_len, - const struct TALER_EXCHANGE_WireAccount *accounts) + const struct TALER_EXCHANGE_WireResponse *wr) { struct WireState *ws = cls; - struct TALER_TESTING_Command *cmd = &ws->is->commands[ws->is->ip]; + const struct TALER_EXCHANGE_HttpResponse *hr = &wr->hr; struct TALER_Amount expected_fee; TALER_LOG_DEBUG ("Checking parsed /wire response\n"); @@ -100,41 +100,70 @@ wire_cb (void *cls, if (MHD_HTTP_OK == hr->http_status) { + unsigned int accounts_len + = wr->details.ok.accounts_len; + unsigned int fees_len + = wr->details.ok.fees_len; + const struct TALER_EXCHANGE_WireAccount *accounts + = wr->details.ok.accounts; + const struct TALER_EXCHANGE_WireFeesByMethod *fees + = wr->details.ok.fees; + for (unsigned int i = 0; i<accounts_len; i++) { char *method; method = TALER_payto_get_method (accounts[i].payto_uri); + if (NULL == method) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ws->is); + return; + } if (0 == strcmp (ws->expected_method, method)) { ws->method_found = GNUNET_OK; - if (NULL != ws->expected_fee) + } + GNUNET_free (method); + } + if (NULL != ws->expected_fee) + { + bool fee_found = false; + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ws->expected_fee, + &expected_fee)); + for (unsigned int i = 0; i<fees_len; i++) + { + if (0 != strcmp (fees[i].method, + ws->expected_method)) + continue; + for (const struct TALER_EXCHANGE_WireAggregateFees *waf + = fees[i].fees_head; + NULL != waf; + waf = waf->next) { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (ws->expected_fee, - &expected_fee)); - for (const struct TALER_EXCHANGE_WireAggregateFees *waf - = accounts[i].fees; - NULL != waf; - waf = waf->next) + if (0 != TALER_amount_cmp (&waf->fees.wire, + &expected_fee)) { - if (0 != TALER_amount_cmp (&waf->wire_fee, - &expected_fee)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fee mismatch to command %s\n", - cmd->label); - TALER_TESTING_interpreter_fail (ws->is); - GNUNET_free (method); - return; - } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire fee mismatch to command %s\n", + ws->cmd->label); + TALER_TESTING_interpreter_fail (ws->is); + return; } + fee_found = true; } } - TALER_LOG_DEBUG ("Freeing method '%s'\n", - method); - GNUNET_free (method); + if (! fee_found) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "/wire does not contain expected fee '%s'\n", + ws->expected_fee); + TALER_TESTING_interpreter_fail (ws->is); + return; + } } if (GNUNET_OK != ws->method_found) { @@ -163,11 +192,14 @@ wire_run (void *cls, { struct WireState *ws = cls; - (void) cmd; + ws->cmd = cmd; ws->is = is; - ws->wh = TALER_EXCHANGE_wire (is->exchange, - &wire_cb, - ws); + ws->wh = TALER_EXCHANGE_wire ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + &wire_cb, + ws); } @@ -186,10 +218,8 @@ wire_cleanup (void *cls, if (NULL != ws->wh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ws->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ws->is, + cmd->label); TALER_EXCHANGE_wire_cancel (ws->wh); ws->wh = NULL; } @@ -197,17 +227,6 @@ wire_cleanup (void *cls, } -/** - * Create a "wire" command. - * - * @param label the command label. - * @param expected_method which wire-transfer method is expected - * to be offered by the exchange. - * @param expected_fee the fee the exchange should charge. - * @param expected_response_code the HTTP response the exchange - * should return. - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_wire (const char *label, const char *expected_method, diff --git a/src/testing/testing_api_cmd_wire_add.c b/src/testing/testing_api_cmd_wire_add.c index 8d8a3a4f6..d2a15894a 100644 --- a/src/testing/testing_api_cmd_wire_add.c +++ b/src/testing/testing_api_cmd_wire_add.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,27 +67,21 @@ struct WireAddState * if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param wer response details */ static void wire_add_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer) { struct WireAddState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr; ds->dh = NULL; if (ds->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response_code); return; } TALER_TESTING_interpreter_next (ds->is); @@ -109,12 +103,31 @@ wire_add_run (void *cls, struct WireAddState *ds = cls; struct TALER_MasterSignatureP master_sig1; struct TALER_MasterSignatureP master_sig2; - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp now; + json_t *credit_rest; + json_t *debit_rest; + const char *exchange_url; (void) cmd; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } + now = GNUNET_TIME_timestamp_get (); ds->is = is; + debit_rest = json_array (); + credit_rest = json_array (); if (ds->bad_sig) { memset (&master_sig1, @@ -126,23 +139,50 @@ wire_add_run (void *cls, } else { + const struct TALER_TESTING_Command *exchange_cmd; + const struct TALER_MasterPrivateKeyP *master_priv; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); TALER_exchange_offline_wire_add_sign (ds->payto_uri, + NULL, + debit_rest, + credit_rest, now, - &is->master_priv, + master_priv, &master_sig1); TALER_exchange_wire_signature_make (ds->payto_uri, - &is->master_priv, + NULL, + debit_rest, + credit_rest, + master_priv, &master_sig2); } ds->dh = TALER_EXCHANGE_management_enable_wire ( - is->ctx, - is->exchange_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, ds->payto_uri, + NULL, + debit_rest, + credit_rest, now, &master_sig1, &master_sig2, + NULL, + 0LL, &wire_add_cb, ds); + json_decref (debit_rest); + json_decref (credit_rest); if (NULL == ds->dh) { GNUNET_break (0); @@ -167,10 +207,8 @@ wire_add_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); TALER_EXCHANGE_management_enable_wire_cancel (ds->dh); ds->dh = NULL; } @@ -178,26 +216,6 @@ wire_add_cleanup (void *cls, } -/** - * Offer internal data from a "wire_add" CMD, to other commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * - * @return #GNUNET_OK on success. - */ -static int -wire_add_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_NO; -} - - struct TALER_TESTING_Command TALER_TESTING_cmd_wire_add (const char *label, const char *payto_uri, @@ -215,8 +233,7 @@ TALER_TESTING_cmd_wire_add (const char *label, .cls = ds, .label = label, .run = &wire_add_run, - .cleanup = &wire_add_cleanup, - .traits = &wire_add_traits + .cleanup = &wire_add_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_wire_del.c b/src/testing/testing_api_cmd_wire_del.c index 2a1c9e455..50ebfc7cb 100644 --- a/src/testing/testing_api_cmd_wire_del.c +++ b/src/testing/testing_api_cmd_wire_del.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,27 +67,22 @@ struct WireDelState * if the response code is acceptable. * * @param cls closure. - * @param hr HTTP response details + * @param wdr response details */ static void wire_del_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdr) { struct WireDelState *ds = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &wdr->hr; ds->dh = NULL; if (ds->expected_response_code != hr->http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s in %s:%u\n", - hr->http_status, - ds->is->commands[ds->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (hr->reply, - stderr, - 0); - TALER_TESTING_interpreter_fail (ds->is); + TALER_TESTING_unexpected_status (ds->is, + hr->http_status, + ds->expected_response_code); + return; } TALER_TESTING_interpreter_next (ds->is); @@ -108,11 +103,26 @@ wire_del_run (void *cls, { struct WireDelState *ds = cls; struct TALER_MasterSignatureP master_sig; - struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Timestamp now; + const char *exchange_url; (void) cmd; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + { + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)); + } + now = GNUNET_TIME_timestamp_get (); ds->is = is; if (ds->bad_sig) { @@ -122,14 +132,28 @@ wire_del_run (void *cls, } else { + const struct TALER_TESTING_Command *exchange_cmd; + const struct TALER_MasterPrivateKeyP *master_priv; + + exchange_cmd = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_master_priv (exchange_cmd, + &master_priv)); TALER_exchange_offline_wire_del_sign (ds->payto_uri, now, - &is->master_priv, + master_priv, &master_sig); } ds->dh = TALER_EXCHANGE_management_disable_wire ( - is->ctx, - is->exchange_url, + TALER_TESTING_interpreter_get_context (is), + exchange_url, ds->payto_uri, now, &master_sig, @@ -159,10 +183,8 @@ wire_del_cleanup (void *cls, if (NULL != ds->dh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - ds->is->ip, - cmd->label); + TALER_TESTING_command_incomplete (ds->is, + cmd->label); TALER_EXCHANGE_management_disable_wire_cancel (ds->dh); ds->dh = NULL; } @@ -170,26 +192,6 @@ wire_del_cleanup (void *cls, } -/** - * Offer internal data from a "wire_del" CMD, to other commands. - * - * @param cls closure. - * @param[out] ret result. - * @param trait name of the trait. - * @param index index number of the object to offer. - * - * @return #GNUNET_OK on success. - */ -static int -wire_del_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_NO; -} - - struct TALER_TESTING_Command TALER_TESTING_cmd_wire_del (const char *label, const char *payto_uri, @@ -207,8 +209,7 @@ TALER_TESTING_cmd_wire_del (const char *label, .cls = ds, .label = label, .run = &wire_del_run, - .cleanup = &wire_del_cleanup, - .traits = &wire_del_traits + .cleanup = &wire_del_cleanup }; return cmd; diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c index 23beb6065..f8ff0205b 100644 --- a/src/testing/testing_api_cmd_withdraw.c +++ b/src/testing/testing_api_cmd_withdraw.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018-2021 Taler Systems SA + Copyright (C) 2018-2022 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 @@ -27,6 +27,7 @@ #include <microhttpd.h> #include <gnunet/gnunet_curl_lib.h> #include "taler_signatures.h" +#include "taler_extensions.h" #include "taler_testing_lib.h" #include "backoff.h" @@ -61,11 +62,16 @@ struct WithdrawState /** * Reference to a withdraw or reveal operation from which we should - * re-use the private coin key, or NULL for regular withdrawal. + * reuse the private coin key, or NULL for regular withdrawal. */ const char *reuse_coin_key_ref; /** + * Our command. + */ + const struct TALER_TESTING_Command *cmd; + + /** * String describing the denomination value we should withdraw. * A corresponding denomination key must exist in the exchange's * offerings. Can be NULL if @e pk is set instead. @@ -85,6 +91,37 @@ struct WithdrawState char *exchange_url; /** + * URI if the reserve we are withdrawing from. + */ + char *reserve_payto_uri; + + /** + * Private key of the reserve we are withdrawing from. + */ + struct TALER_ReservePrivateKeyP reserve_priv; + + /** + * Public key of the reserve we are withdrawing from. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Private key of the coin. + */ + struct TALER_CoinSpendPrivateKeyP coin_priv; + + /** + * Blinding key used during the operation. + */ + union GNUNET_CRYPTO_BlindingSecretP bks; + + /** + * Values contributed from the exchange during the + * withdraw protocol. + */ + struct TALER_ExchangeWithdrawValues exchange_vals; + + /** * Interpreter state (during command). */ struct TALER_TESTING_Interpreter *is; @@ -98,18 +135,30 @@ struct WithdrawState /** * Private key material of the coin, set by the interpreter. */ - struct TALER_PlanchetSecretsP ps; + struct TALER_PlanchetMasterSecretP ps; + + /** + * An age > 0 signifies age restriction is required + */ + uint8_t age; + + /** + * If age > 0, put here the corresponding age commitment with its proof and + * its hash, respectively. + */ + struct TALER_AgeCommitmentProof age_commitment_proof; + struct TALER_AgeCommitmentHash h_age_commitment; /** * Reserve history entry that corresponds to this operation. * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; /** * Withdraw handle (while operation is running). */ - struct TALER_EXCHANGE_WithdrawHandle *wsh; + struct TALER_EXCHANGE_BatchWithdrawHandle *wsh; /** * Task scheduled to try later. @@ -127,6 +176,18 @@ struct WithdrawState struct GNUNET_TIME_Relative total_backoff; /** + * Set to the KYC requirement payto hash *if* the exchange replied with a + * request for KYC. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Set to the KYC requirement row *if* the exchange replied with + * a request for KYC. + */ + uint64_t requirement_row; + + /** * Expected HTTP response code to the request. */ unsigned int expected_response_code; @@ -164,8 +225,7 @@ do_retry (void *cls) struct WithdrawState *ws = cls; ws->retry_task = NULL; - ws->is->commands[ws->is->ip].last_req_time - = GNUNET_TIME_absolute_get (); + TALER_TESTING_touch_cmd (ws->is); withdraw_run (ws, NULL, ws->is); @@ -182,7 +242,7 @@ do_retry (void *cls) */ static void reserve_withdraw_cb (void *cls, - const struct TALER_EXCHANGE_WithdrawResponse *wr) + const struct TALER_EXCHANGE_BatchWithdrawResponse *wr) { struct WithdrawState *ws = cls; struct TALER_TESTING_Interpreter *is = ws->is; @@ -192,12 +252,12 @@ reserve_withdraw_cb (void *cls, { if (0 != ws->do_retry) { - if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != wr->hr.ec) + if (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN != wr->hr.ec) ws->do_retry--; /* we don't count reserve unknown as failures here */ if ( (0 == wr->hr.http_status) || (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec) || (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS == wr->hr.ec) || - (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN == wr->hr.ec) || + (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN == wr->hr.ec) || (MHD_HTTP_INTERNAL_SERVER_ERROR == wr->hr.http_status) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -207,7 +267,7 @@ reserve_withdraw_cb (void *cls, /* on DB conflicts, do not use backoff */ if (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec) ws->backoff = GNUNET_TIME_UNIT_ZERO; - else if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != wr->hr.ec) + else if (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN != wr->hr.ec) ws->backoff = EXCHANGE_LIB_BACKOFF (ws->backoff); else ws->backoff = GNUNET_TIME_relative_max (UNKNOWN_MIN_BACKOFF, @@ -216,45 +276,38 @@ reserve_withdraw_cb (void *cls, UNKNOWN_MAX_BACKOFF); ws->total_backoff = GNUNET_TIME_relative_add (ws->total_backoff, ws->backoff); - ws->is->commands[ws->is->ip].num_tries++; + TALER_TESTING_inc_tries (ws->is); ws->retry_task = GNUNET_SCHEDULER_add_delayed (ws->backoff, &do_retry, ws); return; } } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - wr->hr.http_status, - (int) wr->hr.ec, - TALER_TESTING_interpreter_get_current_label (is), - __FILE__, - __LINE__); - json_dumpf (wr->hr.reply, - stderr, - 0); - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); + TALER_TESTING_unexpected_status_with_body (is, + wr->hr.http_status, + ws->expected_response_code, + wr->hr.reply); return; } switch (wr->hr.http_status) { case MHD_HTTP_OK: - ws->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup ( - wr->details.success.sig.rsa_signature); + GNUNET_assert (1 == wr->details.ok.num_coins); + TALER_denom_sig_copy (&ws->sig, + &wr->details.ok.coins[0].sig); + ws->coin_priv = wr->details.ok.coins[0].coin_priv; + ws->bks = wr->details.ok.coins[0].bks; + TALER_denom_ewv_copy (&ws->exchange_vals, + &wr->details.ok.coins[0].exchange_vals); if (0 != ws->total_backoff.rel_value_us) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total withdraw backoff for %s was %s\n", - is->commands[is->ip].label, + ws->cmd->label, GNUNET_STRINGS_relative_time_to_string (ws->total_backoff, - GNUNET_YES)); + true)); } break; - case MHD_HTTP_ACCEPTED: - /* nothing to check */ - /* TODO: trait for returned uuid! */ - break; case MHD_HTTP_FORBIDDEN: /* nothing to check */ break; @@ -267,6 +320,13 @@ reserve_withdraw_cb (void *cls, case MHD_HTTP_GONE: /* theoretically could check that the key was actually */ break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* KYC required */ + ws->requirement_row = + wr->details.unavailable_for_legal_reasons.requirement_row; + ws->h_payto + = wr->details.unavailable_for_legal_reasons.h_payto; + break; default: /* Unsupported status code (by test harness) */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -280,50 +340,6 @@ reserve_withdraw_cb (void *cls, /** - * Parser reference to a coin. - * - * @param coin_reference of format $LABEL['#' $INDEX]? - * @param[out] cref where we return a copy of $LABEL - * @param[out] idx where we set $INDEX - * @return #GNUNET_SYSERR if $INDEX is present but not numeric - */ -static int -parse_coin_reference (const char *coin_reference, - char **cref, - unsigned int *idx) -{ - const char *index; - - /* We allow command references of the form "$LABEL#$INDEX" or - just "$LABEL", which implies the index is 0. Figure out - which one it is. */ - index = strchr (coin_reference, '#'); - if (NULL == index) - { - *idx = 0; - *cref = GNUNET_strdup (coin_reference); - return GNUNET_OK; - } - *cref = GNUNET_strndup (coin_reference, - index - coin_reference); - if (1 != sscanf (index + 1, - "%u", - idx)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Numeric index (not `%s') required after `#' in command reference of command in %s:%u\n", - index, - __FILE__, - __LINE__); - GNUNET_free (*cref); - *cref = NULL; - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** * Run the command. */ static void @@ -336,7 +352,9 @@ withdraw_run (void *cls, const struct TALER_TESTING_Command *create_reserve; const struct TALER_EXCHANGE_DenomPublicKey *dpk; - (void) cmd; + if (NULL != cmd) + ws->cmd = cmd; + ws->is = is; create_reserve = TALER_TESTING_interpreter_lookup_command ( is, @@ -349,44 +367,53 @@ withdraw_run (void *cls, } if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv (create_reserve, - 0, &rp)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } + if (NULL == ws->exchange_url) + ws->exchange_url + = GNUNET_strdup (TALER_TESTING_get_exchange_url (is)); + ws->reserve_priv = *rp; + GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv, + &ws->reserve_pub.eddsa_pub); + ws->reserve_payto_uri + = TALER_reserve_make_payto (ws->exchange_url, + &ws->reserve_pub); + if (NULL == ws->reuse_coin_key_ref) { - TALER_planchet_setup_random (&ws->ps); + TALER_planchet_master_setup_random (&ws->ps); } else { - const struct TALER_CoinSpendPrivateKeyP *coin_priv; + const struct TALER_PlanchetMasterSecretP *ps; const struct TALER_TESTING_Command *cref; char *cstr; unsigned int index; GNUNET_assert (GNUNET_OK == - parse_coin_reference (ws->reuse_coin_key_ref, - &cstr, - &index)); + TALER_TESTING_parse_coin_reference ( + ws->reuse_coin_key_ref, + &cstr, + &index)); cref = TALER_TESTING_interpreter_lookup_command (is, cstr); GNUNET_assert (NULL != cref); GNUNET_free (cstr); GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_coin_priv (cref, - index, - &coin_priv)); - TALER_planchet_setup_random (&ws->ps); - ws->ps.coin_priv = *coin_priv; + TALER_TESTING_get_trait_planchet_secret (cref, + &ps)); + ws->ps = *ps; } - ws->is = is; + if (NULL == ws->pk) { - dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange), - &ws->amount); + dpk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (is), + &ws->amount, + ws->age > 0); if (NULL == dpk) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -404,18 +431,30 @@ withdraw_run (void *cls, { ws->amount = ws->pk->value; } + ws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL; GNUNET_assert (0 <= TALER_amount_add (&ws->reserve_history.amount, &ws->amount, - &ws->pk->fee_withdraw)); - ws->reserve_history.details.withdraw.fee = ws->pk->fee_withdraw; - ws->wsh = TALER_EXCHANGE_withdraw (is->exchange, - ws->pk, - rp, - &ws->ps, - &reserve_withdraw_cb, - ws); + &ws->pk->fees.withdraw)); + ws->reserve_history.details.withdraw.fee = ws->pk->fees.withdraw; + { + struct TALER_EXCHANGE_WithdrawCoinInput wci = { + .pk = ws->pk, + .ps = &ws->ps, + .ach = 0 < ws->age ? &ws->h_age_commitment : NULL + }; + + ws->wsh = TALER_EXCHANGE_batch_withdraw ( + TALER_TESTING_interpreter_get_context (is), + TALER_TESTING_get_exchange_url (is), + TALER_TESTING_get_keys (is), + rp, + 1, + &wci, + &reserve_withdraw_cb, + ws); + } if (NULL == ws->wsh) { GNUNET_break (0); @@ -440,10 +479,9 @@ withdraw_cleanup (void *cls, if (NULL != ws->wsh) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %s did not complete\n", - cmd->label); - TALER_EXCHANGE_withdraw_cancel (ws->wsh); + TALER_TESTING_command_incomplete (ws->is, + cmd->label); + TALER_EXCHANGE_batch_withdraw_cancel (ws->wsh); ws->wsh = NULL; } if (NULL != ws->retry_task) @@ -451,17 +489,17 @@ withdraw_cleanup (void *cls, GNUNET_SCHEDULER_cancel (ws->retry_task); ws->retry_task = NULL; } - if (NULL != ws->sig.rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (ws->sig.rsa_signature); - ws->sig.rsa_signature = NULL; - } + TALER_denom_sig_free (&ws->sig); + TALER_denom_ewv_free (&ws->exchange_vals); if (NULL != ws->pk) { TALER_EXCHANGE_destroy_denomination_key (ws->pk); ws->pk = NULL; } + if (ws->age > 0) + TALER_age_commitment_proof_free (&ws->age_commitment_proof); GNUNET_free (ws->exchange_url); + GNUNET_free (ws->reserve_payto_uri); GNUNET_free (ws); } @@ -476,104 +514,83 @@ withdraw_cleanup (void *cls, * @param index index number of the object to offer. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue withdraw_traits (void *cls, const void **ret, const char *trait, unsigned int index) { struct WithdrawState *ws = cls; - const struct TALER_TESTING_Command *reserve_cmd; - const struct TALER_ReservePrivateKeyP *reserve_priv; - const struct TALER_ReservePublicKeyP *reserve_pub; - - /* We offer the reserve key where these coins were withdrawn - * from. */ - reserve_cmd = TALER_TESTING_interpreter_lookup_command (ws->is, - ws->reserve_reference); - - if (NULL == reserve_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (ws->is); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_priv (reserve_cmd, - 0, - &reserve_priv)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (ws->is); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_pub (reserve_cmd, - 0, - &reserve_pub)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (ws->is); - return GNUNET_SYSERR; - } - if (NULL == ws->exchange_url) - ws->exchange_url - = GNUNET_strdup (TALER_EXCHANGE_get_base_url (ws->is->exchange)); - { - struct TALER_TESTING_Trait traits[] = { - /* history entry MUST be first due to response code logic below! */ - TALER_TESTING_make_trait_reserve_history (0, - &ws->reserve_history), - TALER_TESTING_make_trait_coin_priv (0 /* only one coin */, - &ws->ps.coin_priv), - TALER_TESTING_make_trait_blinding_key (0 /* only one coin */, - &ws->ps.blinding_key), - TALER_TESTING_make_trait_denom_pub (0 /* only one coin */, - ws->pk), - TALER_TESTING_make_trait_denom_sig (0 /* only one coin */, - &ws->sig), - TALER_TESTING_make_trait_reserve_priv (0, - reserve_priv), - TALER_TESTING_make_trait_reserve_pub (0, - reserve_pub), - TALER_TESTING_make_trait_amount_obj (0, - &ws->amount), - TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BASE_URL, - ws->exchange_url), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK) - ? &traits[0] /* we have reserve history */ - : &traits[1],/* skip reserve history */ - ret, - trait, - index); - } + struct TALER_TESTING_Trait traits[] = { + /* history entry MUST be first due to response code logic below! */ + TALER_TESTING_make_trait_reserve_history (0, + &ws->reserve_history), + TALER_TESTING_make_trait_coin_priv (0 /* only one coin */, + &ws->coin_priv), + TALER_TESTING_make_trait_planchet_secret (&ws->ps), + TALER_TESTING_make_trait_blinding_key (0 /* only one coin */, + &ws->bks), + TALER_TESTING_make_trait_exchange_wd_value (0 /* only one coin */, + &ws->exchange_vals), + TALER_TESTING_make_trait_denom_pub (0 /* only one coin */, + ws->pk), + TALER_TESTING_make_trait_denom_sig (0 /* only one coin */, + &ws->sig), + TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub), + TALER_TESTING_make_trait_amount (&ws->amount), + TALER_TESTING_make_trait_legi_requirement_row (&ws->requirement_row), + TALER_TESTING_make_trait_h_payto (&ws->h_payto), + TALER_TESTING_make_trait_payto_uri (ws->reserve_payto_uri), + TALER_TESTING_make_trait_exchange_url (ws->exchange_url), + TALER_TESTING_make_trait_age_commitment_proof (0, + 0 < ws->age + ? &ws->age_commitment_proof + : NULL), + TALER_TESTING_make_trait_h_age_commitment (0, + 0 < ws->age + ? &ws->h_age_commitment + : NULL), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK) + ? &traits[0] /* we have reserve history */ + : &traits[1], /* skip reserve history */ + ret, + trait, + index); } -/** - * Create a withdraw command, letting the caller specify - * the desired amount as string. - * - * @param label command label. - * @param reserve_reference command providing us with a reserve to withdraw from - * @param amount how much we withdraw. - * @param expected_response_code which HTTP response code - * we expect from the exchange. - * @return the withdraw command to be executed by the interpreter. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_withdraw_amount (const char *label, const char *reserve_reference, const char *amount, + uint8_t age, unsigned int expected_response_code) { struct WithdrawState *ws; ws = GNUNET_new (struct WithdrawState); + ws->age = age; + if (0 < age) + { + struct GNUNET_HashCode seed; + struct TALER_AgeMask mask; + + mask = TALER_extensions_get_age_restriction_mask (); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &seed, + sizeof(seed)); + TALER_age_restriction_commit (&mask, + age, + &seed, + &ws->age_commitment_proof); + TALER_age_commitment_hash (&ws->age_commitment_proof.commitment, + &ws->h_age_commitment); + } + ws->reserve_reference = reserve_reference; if (GNUNET_OK != TALER_string_to_amount (amount, @@ -600,26 +617,12 @@ TALER_TESTING_cmd_withdraw_amount (const char *label, } -/** - * Create a withdraw command, letting the caller specify - * the desired amount as string and also re-using an existing - * coin private key in the process (violating the specification, - * which will result in an error when spending the coin!). - * - * @param label command label. - * @param reserve_reference command providing us with a reserve to withdraw from - * @param amount how much we withdraw. - * @param coin_ref reference to (withdraw/reveal) command of a coin - * from which we should re-use the private key - * @param expected_response_code which HTTP response code - * we expect from the exchange. - * @return the withdraw command to be executed by the interpreter. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_withdraw_amount_reuse_key ( const char *label, const char *reserve_reference, const char *amount, + uint8_t age, const char *coin_ref, unsigned int expected_response_code) { @@ -628,6 +631,7 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key ( cmd = TALER_TESTING_cmd_withdraw_amount (label, reserve_reference, amount, + age, expected_response_code); { struct WithdrawState *ws = cmd.cls; @@ -638,18 +642,6 @@ TALER_TESTING_cmd_withdraw_amount_reuse_key ( } -/** - * Create withdraw command, letting the caller specify the - * amount by a denomination key. - * - * @param label command label. - * @param reserve_reference reference to the reserve to withdraw - * from; will provide reserve priv to sign the request. - * @param dk denomination public key. - * @param expected_response_code expected HTTP response code. - * - * @return the command. - */ struct TALER_TESTING_Command TALER_TESTING_cmd_withdraw_denomination ( const char *label, @@ -684,14 +676,6 @@ TALER_TESTING_cmd_withdraw_denomination ( } -/** - * Modify a withdraw command to enable retries when the - * reserve is not yet full or we get other transient - * errors from the exchange. - * - * @param cmd a withdraw command - * @return the command with retries enabled - */ struct TALER_TESTING_Command TALER_TESTING_cmd_withdraw_with_retry (struct TALER_TESTING_Command cmd) { diff --git a/src/testing/testing_api_helpers_auditor.c b/src/testing/testing_api_helpers_auditor.c deleted file mode 100644 index b74258004..000000000 --- a/src/testing/testing_api_helpers_auditor.c +++ /dev/null @@ -1,231 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_helpers_auditor.c - * @brief helper functions - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_testing_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 hr http response details - * @param vi basic information about the auditor - * @param compat protocol compatibility information - */ -static void -auditor_version_cb (void *cls, - const struct TALER_AUDITOR_HttpResponse *hr, - const struct TALER_AUDITOR_VersionInformation *vi, - enum TALER_AUDITOR_VersionCompatibility compat) -{ - struct TALER_TESTING_Interpreter *is = cls; - - (void) hr; - 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[in,out] is 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_cls; - 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/testing/testing_api_helpers_bank.c b/src/testing/testing_api_helpers_bank.c deleted file mode 100644 index 2876c2470..000000000 --- a/src/testing/testing_api_helpers_bank.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018-2021 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 3, or - (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_helpers_bank.c - * @brief convenience functions for bank tests. - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include "taler_testing_lib.h" -#include "taler_fakebank_lib.h" - -#define BANK_FAIL() \ - do {GNUNET_break (0); return NULL; } while (0) - - -struct TALER_FAKEBANK_Handle * -TALER_TESTING_run_fakebank (const char *bank_url, - const char *currency) -{ - 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, - currency); - if (NULL == fakebankd) - { - GNUNET_break (0); - return NULL; - } - return fakebankd; -} - - -int -TALER_TESTING_has_in_name (const char *prog, - const char *marker) -{ - size_t name_pos; - size_t pos; - - if (! prog || ! marker) - return GNUNET_NO; - - pos = 0; - name_pos = 0; - while (prog[pos]) - { - if ('/' == prog[pos]) - name_pos = pos + 1; - pos++; - } - if (name_pos == pos) - return GNUNET_YES; - return (NULL != strstr (prog + name_pos, marker)); -} - - -struct TALER_TESTING_LibeufinServices -TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc) -{ - struct GNUNET_OS_Process *nexus_proc; - struct GNUNET_OS_Process *sandbox_proc; - struct TALER_TESTING_LibeufinServices ret = { 0 }; - unsigned int iter; - char *curl_check_cmd; - - nexus_proc = GNUNET_OS_start_process ( - GNUNET_OS_INHERIT_STD_ERR, - NULL, NULL, NULL, - "libeufin-nexus", - "libeufin-nexus", - "serve", - "--db-name", "/tmp/nexus-exchange-test.sqlite3", - NULL); - if (NULL == nexus_proc) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "exec", - "libeufin-nexus"); - return ret; - } - GNUNET_asprintf (&curl_check_cmd, - "curl -s %s", - bc->exchange_auth.wire_gateway_url); - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `nexus' to be ready (via %s)\n", curl_check_cmd); - iter = 0; - do - { - if (10 == iter) - { - fprintf ( - stderr, - "Failed to launch `nexus'\n"); - GNUNET_OS_process_kill (nexus_proc, - SIGTERM); - GNUNET_OS_process_wait (nexus_proc); - GNUNET_OS_process_destroy (nexus_proc); - GNUNET_free (curl_check_cmd); - GNUNET_break (0); - return ret; - } - fprintf (stderr, "."); - sleep (1); - iter++; - } - while (0 != system (curl_check_cmd)); - - // start sandbox. - GNUNET_free (curl_check_cmd); - fprintf (stderr, "\n"); - - sandbox_proc = GNUNET_OS_start_process ( - GNUNET_OS_INHERIT_STD_ERR, - NULL, NULL, NULL, - "libeufin-sandbox", - "libeufin-sandbox", - "serve", - "--db-name", "/tmp/sandbox-exchange-test.sqlite3", - NULL); - if (NULL == sandbox_proc) - { - GNUNET_break (0); - return ret; - } - - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `sandbox' to be ready.\n"); - iter = 0; - do - { - if (10 == iter) - { - fprintf ( - stderr, - "Failed to launch `sandbox'\n"); - GNUNET_OS_process_kill (sandbox_proc, - SIGTERM); - GNUNET_OS_process_wait (sandbox_proc); - GNUNET_OS_process_destroy (sandbox_proc); - GNUNET_break (0); - return ret; - } - fprintf (stderr, "."); - sleep (1); - iter++; - } - while (0 != system ("curl -s http://localhost:5000/")); - fprintf (stderr, "\n"); - - // Creates nexus user + bank loopback connection + Taler facade. - if (0 != system ("taler-nexus-prepare")) - { - GNUNET_OS_process_kill (nexus_proc, SIGTERM); - GNUNET_OS_process_wait (nexus_proc); - GNUNET_OS_process_destroy (nexus_proc); - GNUNET_OS_process_kill (sandbox_proc, SIGTERM); - GNUNET_OS_process_wait (sandbox_proc); - GNUNET_OS_process_destroy (sandbox_proc); - TALER_LOG_ERROR ("Could not prepare nexus\n"); - GNUNET_break (0); - return ret; - } - ret.nexus = nexus_proc; - ret.sandbox = sandbox_proc; - return ret; -} - - -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; - 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); - } - GNUNET_CONFIGURATION_destroy (cfg); - bank_proc = GNUNET_OS_start_process ( - GNUNET_OS_INHERIT_STD_ERR, - NULL, NULL, NULL, - "taler-bank-manage-testing", - "taler-bank-manage-testing", - config_filename, - database, - "serve", 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 (via %s)\n", wget_cmd); - 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; - -} - - -int -TALER_TESTING_prepare_nexus (const char *config_filename, - int reset_db, - const char *config_section, - struct TALER_TESTING_BankConfiguration *bc) -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - unsigned long long port; - char *database = NULL; // silence compiler - char *exchange_payto_uri; - - GNUNET_assert (0 == - strncasecmp (config_section, - "exchange-account-", - strlen ("exchange-account-"))); - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - config_filename)) - { - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_break (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - config_section, - "PAYTO_URI", - &exchange_payto_uri)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - config_section, - "PAYTO_URI"); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - - 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); - GNUNET_break (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - (uint16_t) port)) - { - fprintf (stderr, - "Required port %llu not available, skipping.\n", - port); - GNUNET_break (0); - GNUNET_free (database); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - - /* DB preparation */ - if (GNUNET_YES == reset_db) - { - if (0 != system ( - "rm -f /tmp/nexus-exchange-test.sqlite3 && rm -f /tmp/sandbox-exchange-test.sqlite3")) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to invoke db-removal command.\n"); - GNUNET_free (database); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - } - - { - char *csn; - - GNUNET_asprintf (&csn, - "exchange-accountcredentials-%s", - &config_section[strlen ("exchange-account-")]); - - - if (GNUNET_OK != - TALER_BANK_auth_parse_cfg (cfg, - csn, - &bc->exchange_auth)) - { - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (csn); - return GNUNET_SYSERR; - } - GNUNET_free (csn); - } - GNUNET_CONFIGURATION_destroy (cfg); - bc->exchange_payto = exchange_payto_uri; - bc->user42_payto = - "payto://iban/BIC/FR7630006000011234567890189?receiver-name=User42"; - bc->user43_payto = - "payto://iban/BIC/GB33BUKB20201555555555?receiver-name=User43"; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Relying on nexus %s on port %u\n", - bc->exchange_auth.wire_gateway_url, - (unsigned int) port); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "exchange payto: %s\n", - bc->exchange_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "user42_payto: %s\n", - bc->user42_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "user42_payto: %s\n", - bc->user43_payto); - return GNUNET_OK; -} - - -int -TALER_TESTING_prepare_bank (const char *config_filename, - int reset_db, - const char *config_section, - struct TALER_TESTING_BankConfiguration *bc) -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - unsigned long long port; - struct GNUNET_OS_Process *dbreset_proc; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - char *database; - char *exchange_payto_uri; - - GNUNET_assert (0 == - strncasecmp (config_section, - "exchange-account-", - strlen ("exchange-account-"))); - cfg = GNUNET_CONFIGURATION_create (); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - config_filename)) - { - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_break (0); - return GNUNET_SYSERR; - } - 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); - GNUNET_break (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - config_section, - "PAYTO_URI", - &exchange_payto_uri)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - config_section, - "PAYTO_URI"); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - - 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); - GNUNET_break (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - (uint16_t) port)) - { - fprintf (stderr, - "Required port %llu not available, skipping.\n", - port); - GNUNET_break (0); - GNUNET_free (database); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - - /* DB preparation */ - if (GNUNET_YES == reset_db) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Flushing bank database\n"); - if (NULL == - (dbreset_proc = GNUNET_OS_start_process ( - GNUNET_OS_INHERIT_STD_ERR, - NULL, NULL, NULL, - "taler-bank-manage", - "taler-bank-manage", - "-c", config_filename, - "--with-db", database, - "django", - "flush", - "--no-input", NULL))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to flush the bank db.\n"); - GNUNET_free (database); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - - if (GNUNET_SYSERR == - GNUNET_OS_process_wait_status (dbreset_proc, - &type, - &code)) - { - GNUNET_OS_process_destroy (dbreset_proc); - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (database); - return GNUNET_SYSERR; - } - if ( (type == GNUNET_OS_PROCESS_EXITED) && - (0 != code) ) - { - fprintf (stderr, - "Failed to setup database `%s'\n", - database); - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (database); - return GNUNET_SYSERR; - } - GNUNET_free (database); - if ( (type != GNUNET_OS_PROCESS_EXITED) || - (0 != code) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected error running `taler-bank-manage django flush'!\n"); - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - GNUNET_OS_process_destroy (dbreset_proc); - } - { - char *csn; - - GNUNET_asprintf (&csn, - "exchange-accountcredentials-%s", - &config_section[strlen ("exchange-account-")]); - - if (GNUNET_OK != - TALER_BANK_auth_parse_cfg (cfg, - csn, - &bc->exchange_auth)) - { - GNUNET_break (0); - GNUNET_free (csn); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - GNUNET_free (csn); - } - GNUNET_CONFIGURATION_destroy (cfg); - bc->exchange_payto = exchange_payto_uri; - bc->user42_payto = "payto://x-taler-bank/localhost/42"; - bc->user43_payto = "payto://x-taler-bank/localhost/43"; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Using pybank %s on port %u\n", - bc->exchange_auth.wire_gateway_url, - (unsigned int) port); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "exchange payto: %s\n", - bc->exchange_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "user42_payto: %s\n", - bc->user42_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "user43_payto: %s\n", - bc->user43_payto); - return GNUNET_OK; -} - - -int -TALER_TESTING_prepare_fakebank (const char *config_filename, - const char *config_section, - struct TALER_TESTING_BankConfiguration *bc) -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - unsigned long long fakebank_port; - char *exchange_payto_uri; - - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, - config_filename)) - return GNUNET_SYSERR; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "BANK", - "HTTP_PORT", - &fakebank_port)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "BANK", - "HTTP_PORT"); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - config_section, - "PAYTO_URI", - &exchange_payto_uri)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - config_section, - "PAYTO_URI"); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - { - char *exchange_xtalerbank_account; - - exchange_xtalerbank_account - = TALER_xtalerbank_account_from_payto (exchange_payto_uri); - if (NULL == exchange_xtalerbank_account) - { - GNUNET_break (0); - GNUNET_free (exchange_payto_uri); - return GNUNET_SYSERR; - } - GNUNET_asprintf (&bc->exchange_auth.wire_gateway_url, - "http://localhost:%u/%s/", - (unsigned int) fakebank_port, - exchange_xtalerbank_account); - GNUNET_free (exchange_xtalerbank_account); - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Using fakebank %s on port %u\n", - bc->exchange_auth.wire_gateway_url, - (unsigned int) fakebank_port); - - GNUNET_CONFIGURATION_destroy (cfg); - if (GNUNET_OK != - TALER_TESTING_url_port_free (bc->exchange_auth.wire_gateway_url)) - { - GNUNET_free (bc->exchange_auth.wire_gateway_url); - bc->exchange_auth.wire_gateway_url = NULL; - GNUNET_free (exchange_payto_uri); - return GNUNET_SYSERR; - } - /* Now we know it's the fake bank, for purpose of authentication, we - * don't have any auth. */ - bc->exchange_auth.method = TALER_BANK_AUTH_NONE; - bc->exchange_payto = exchange_payto_uri; - bc->user42_payto = "payto://x-taler-bank/localhost/42"; - bc->user43_payto = "payto://x-taler-bank/localhost/43"; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "exchange payto: %s\n", - bc->exchange_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "user42_payto: %s\n", - bc->user42_payto); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "user43_payto: %s\n", - bc->user43_payto); - return GNUNET_OK; -} - - -json_t * -TALER_TESTING_make_wire_details (const char *payto) -{ - struct TALER_WireSalt salt; - - /* salt must be constant for aggregation tests! */ - memset (&salt, - 47, - sizeof (salt)); - return GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("payto_uri", - payto), - GNUNET_JSON_pack_data_auto ("salt", - &salt)); -} - - -/* end of testing_api_helpers_bank.c */ diff --git a/src/testing/testing_api_helpers_exchange.c b/src/testing/testing_api_helpers_exchange.c deleted file mode 100644 index a6307ca4c..000000000 --- a/src/testing/testing_api_helpers_exchange.c +++ /dev/null @@ -1,948 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_helpers_exchange.c - * @brief helper functions - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - - -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 @a option directory from @a section in @a cfg. - * - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -remove_dir (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - const char *option) -{ - char *dir; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - section, - option, - &dir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - option); - 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); - return GNUNET_OK; -} - - -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-offline", - "SECM_TOFU_FILE", - &dir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-offline", - "SECM_TOFU_FILE"); - return GNUNET_SYSERR; - } - if ( (0 != unlink (dir)) && - (ENOENT != errno) ) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "unlink", - dir); - GNUNET_free (dir); - return GNUNET_SYSERR; - } - GNUNET_free (dir); - if (GNUNET_OK != - remove_dir (cfg, - "taler-exchange-secmod-eddsa", - "KEY_DIR")) - return GNUNET_SYSERR; - if (GNUNET_OK != - remove_dir (cfg, - "taler-exchange-secmod-rsa", - "KEY_DIR")) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -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_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; -} - - -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_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; -} - - -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_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 -{ - /** - * Members will be set to the exchange configuration. - */ - struct TALER_TESTING_ExchangeConfiguration *ec; - - /** - * Name of the configuration file to use. - */ - const char *config_filename; - - /** - * Did we reset the database? - */ - int db_reset; -}; - - -/** - * Sign the keys for an exchange given configuration @a cfg. - * The information to be signed must be in a file "auditor.in". - * - * @param[in,out] cls a `struct SignInfo` with further parameters - * @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 *exchange_master_pub; - int ret; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "BASE_URL", - &si->ec->exchange_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "exchange", - "BASE_URL"); - si->ec->exchange_url = NULL; - return GNUNET_NO; - } - if (GNUNET_OK != - TALER_TESTING_url_port_free (si->ec->exchange_url)) - { - GNUNET_free (si->ec->exchange_url); - si->ec->exchange_url = NULL; - return GNUNET_NO; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "auditor", - "BASE_URL", - &si->ec->auditor_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "auditor", - "BASE_URL"); - GNUNET_free (si->ec->exchange_url); - si->ec->exchange_url = NULL; - si->ec->auditor_url = NULL; - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_TESTING_url_port_free (si->ec->auditor_url)) - { - ret = GNUNET_NO; - goto fail; - } - 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"); - ret = GNUNET_SYSERR; - goto fail; - } - if ( (GNUNET_OK != - TALER_TESTING_run_auditor_exchange (si->config_filename, - exchange_master_pub, - si->ec->exchange_url, - GNUNET_NO)) && - (GNUNET_YES == si->db_reset) ) - { - ret = GNUNET_NO; - goto fail; - } - GNUNET_free (exchange_master_pub); - return GNUNET_OK; -fail: - GNUNET_free (si->ec->exchange_url); - GNUNET_free (si->ec->auditor_url); - si->ec->exchange_url = NULL; - si->ec->auditor_url = NULL; - return ret; -} - - -/** - * Prepare launching an exchange. Checks that the configured - * port is available, runs taler-exchange-dbinit. Does NOT - * launch the exchange process itself. - * - * @param config_filename configuration file to use - * @param reset_db should we reset the database? - * @param[out] ec will be set to the exchange configuration data - * @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, - int reset_db, - struct TALER_TESTING_ExchangeConfiguration *ec) -{ - struct SignInfo si = { - .config_filename = config_filename, - .ec = ec, - .db_reset = reset_db - }; - - if (GNUNET_YES == reset_db) - { - 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; - 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; i<keys->num_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; i<keys->num_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; -} - - -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 %sseed -o /dev/null -O /dev/null", - base_url); // make sure ends with '/' - /* give child time to start and bind against the socket */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Waiting for `taler-exchange-httpd` service to be ready (check with: %s)\n", - wget_cmd); - iter = 0; - do - { - if (10 == iter) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to launch `taler-exchange-httpd` service (or `wget')\n"); - GNUNET_free (wget_cmd); - return 77; - } - sleep (1); - iter++; - } - while (0 != system (wget_cmd)); - GNUNET_free (wget_cmd); - return 0; -} - - -int -TALER_TESTING_wait_httpd_ready (const char *base_url) -{ - char *wget_cmd; - unsigned int iter; - - GNUNET_asprintf (&wget_cmd, - "wget -q -t 1 -T 1 %s -o /dev/null -O /dev/null", - base_url); // make sure ends with '/' - /* give child time to start and bind against the socket */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Waiting for HTTP service to be ready (check with: %s)\n", - wget_cmd); - iter = 0; - do - { - if (10 == iter) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to launch HTTP service (or `wget')\n"); - GNUNET_free (wget_cmd); - return 77; - } - 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 at %s\n", - base_url); - 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; -} - - -int -TALER_TESTING_setup_with_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 - }; - int result; - - result = - GNUNET_CONFIGURATION_parse_and_run (config_file, - &TALER_TESTING_setup_with_exchange_cfg, - &setup_ctx); - if (GNUNET_OK != result) - return result; - return GNUNET_OK; -} - - -/** - * Stop taler-exchange-crypto helpers. - * - * @param[in] helpers the process handles. - */ -static void -stop_helpers (struct GNUNET_OS_Process *helpers[2]) -{ - for (unsigned int i = 0; i<2; i++) - { - if (NULL == helpers[i]) - continue; - GNUNET_break (0 == - GNUNET_OS_process_kill (helpers[i], - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (helpers[i])); - GNUNET_OS_process_destroy (helpers[i]); - } -} - - -/** - * Start taler-exchange-crypto helpers. - * - * @param config_filename configuration file to use - * @param[out] helpers where to store the process handles. - */ -static int -start_helpers (const char *config_filename, - struct GNUNET_OS_Process *helpers[2]) -{ - char *dir; - const struct GNUNET_OS_ProjectData *pd; - - pd = GNUNET_OS_project_data_get (); - GNUNET_OS_init (TALER_project_data_default ()); - dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); - GNUNET_OS_init (pd); - if (NULL == dir) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - char *fn; - - GNUNET_asprintf (&fn, - "%s/%s", - dir, - "taler-exchange-secmod-eddsa"); - helpers[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - fn, - "taler-exchange-secmod-eddsa", - "-c", config_filename, - "-L", "INFO", - NULL); - GNUNET_free (fn); - } - { - char *fn; - - GNUNET_asprintf (&fn, - "%s/%s", - dir, - "taler-exchange-secmod-rsa"); - helpers[1] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - fn, - "taler-exchange-secmod-rsa", - "-c", config_filename, - "-L", "INFO", - NULL); - GNUNET_free (fn); - } - GNUNET_free (dir); - if ( (NULL == helpers[0]) || - (NULL == helpers[1]) ) - { - stop_helpers (helpers); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -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; - struct GNUNET_OS_Process *helpers[2]; - 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); - if (GNUNET_OK != - start_helpers (setup_ctx->config_filename, - helpers)) - { - GNUNET_break (0); - return 77; - } - exchanged = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-httpd", - "taler-exchange-httpd", - "-a", /* some tests may need timetravel */ - "-c", setup_ctx->config_filename, - NULL); - if (NULL == exchanged) - { - GNUNET_break (0); - stop_helpers (helpers); - return 77; - } - 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"); - stop_helpers (helpers); - return GNUNET_NO; - } - - if (0 != TALER_TESTING_wait_exchange_ready (base_url)) - { - GNUNET_free (base_url); - stop_helpers (helpers); - GNUNET_break (0 == - GNUNET_OS_process_kill (exchanged, - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (exchanged)); - GNUNET_OS_process_destroy (exchanged); - return 77; - } - GNUNET_free (base_url); - - /* NOTE: this call blocks. */ - result = TALER_TESTING_setup (setup_ctx->main_cb, - setup_ctx->main_cb_cls, - cfg, - 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); - stop_helpers (helpers); - return result; -} - - -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_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; -} - - -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); -} - - -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; -} - - -/* end of testing_api_helpers_exchange.c */ diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c index 9e989ca36..0f242f7f1 100644 --- a/src/testing/testing_api_loop.c +++ b/src/testing/testing_api_loop.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018 Taler Systems SA + Copyright (C) 2018-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -26,23 +26,67 @@ #include "platform.h" #include "taler_json_lib.h" #include <gnunet/gnunet_curl_lib.h> +#include "taler_extensions.h" #include "taler_signatures.h" #include "taler_testing_lib.h" -#include "taler_fakebank_lib.h" -/** - * Pipe used to communicate child death via signal. - * Must be global, as used in signal handler! - */ -static struct GNUNET_DISK_PipeHandle *sigpipe; /** - * Lookup command by label. - * - * @param is interpreter state to search - * @param label label to look for - * @return NULL if command was not found + * The interpreter and its state */ +struct TALER_TESTING_Interpreter +{ + + /** + * Commands the interpreter will run. + */ + struct TALER_TESTING_Command *commands; + + /** + * Interpreter task (if one is scheduled). + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Handle for the child management. + */ + struct GNUNET_ChildWaitHandle *cwh; + + /** + * Main execution context for the main loop. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Context for running the CURL event loop. + */ + struct GNUNET_CURL_RescheduleContext *rc; + + /** + * Hash map mapping variable names to commands. + */ + struct GNUNET_CONTAINER_MultiHashMap *vars; + + /** + * Task run on timeout. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Instruction pointer. Tells #interpreter_run() which instruction to run + * next. Need (signed) int because it gets -1 when rewinding the + * interpreter to the first CMD. + */ + int ip; + + /** + * Result of the testcases, #GNUNET_OK on success + */ + enum GNUNET_GenericReturnValue result; + +}; + + const struct TALER_TESTING_Command * TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, const char *label) @@ -66,7 +110,6 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, if (TALER_TESTING_cmd_is_batch (cmd)) { -#define BATCH_INDEX 1 struct TALER_TESTING_Command *batch; struct TALER_TESTING_Command *current; struct TALER_TESTING_Command *icmd; @@ -74,9 +117,8 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, current = TALER_TESTING_cmd_batch_get_current (cmd); GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_cmd (cmd, - BATCH_INDEX, - &batch)); + TALER_TESTING_get_trait_batch_cmds (cmd, + &batch)); /* We must do the loop forward, but we can find the last match */ match = NULL; for (unsigned int j = 0; @@ -98,62 +140,48 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, "Command not found: %s\n", label); return NULL; +} + +const struct TALER_TESTING_Command * +TALER_TESTING_interpreter_get_command (struct TALER_TESTING_Interpreter *is, + const char *name) +{ + const struct TALER_TESTING_Command *cmd; + struct GNUNET_HashCode h_name; + + GNUNET_CRYPTO_hash (name, + strlen (name), + &h_name); + cmd = GNUNET_CONTAINER_multihashmap_get (is->vars, + &h_name); + if (NULL == cmd) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command not found by name: %s\n", + name); + return cmd; } -/** - * Obtain main execution context for the main loop. - */ struct GNUNET_CURL_Context * -TALER_TESTING_interpreter_get_context - (struct TALER_TESTING_Interpreter *is) +TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is) { return is->ctx; } -struct TALER_FAKEBANK_Handle * -TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is) +void +TALER_TESTING_touch_cmd (struct TALER_TESTING_Interpreter *is) { - return is->fakebank; + is->commands[is->ip].last_req_time + = GNUNET_TIME_absolute_get (); } -/** - * Run tests starting the "fakebank" first. The "fakebank" - * is a C minimalist version of the human-oriented Python bank, - * which is also part of the Taler project. - * - * @param is pointer to the interpreter state - * @param commands the list of commands to execute - * @param bank_url the url the fakebank is supposed to run on - */ void -TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is, - struct TALER_TESTING_Command *commands, - const char *bank_url) +TALER_TESTING_inc_tries (struct TALER_TESTING_Interpreter *is) { - char *currency; - - if (GNUNET_OK != - TALER_config_get_currency (is->cfg, - ¤cy)) - { - is->result = GNUNET_SYSERR; - return; - } - is->fakebank = TALER_TESTING_run_fakebank (bank_url, - currency); - GNUNET_free (currency); - if (NULL == is->fakebank) - { - GNUNET_break (0); - is->result = GNUNET_SYSERR; - return; - } - TALER_TESTING_run (is, - commands); + is->commands[is->ip].num_tries++; } @@ -166,9 +194,6 @@ static void interpreter_run (void *cls); -/** - * Current command is done, run the next one. - */ void TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) { @@ -180,7 +205,13 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) return; /* ignore, we already failed! */ if (TALER_TESTING_cmd_is_batch (cmd)) { - TALER_TESTING_cmd_batch_next (is); + if (TALER_TESTING_cmd_batch_next (is, + cmd->cls)) + { + /* batch is done */ + cmd->finish_time = GNUNET_TIME_absolute_get (); + is->ip++; + } } else { @@ -194,7 +225,7 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) "Interpreter executed 1000 instructions in %s\n", GNUNET_STRINGS_relative_time_to_string ( GNUNET_TIME_absolute_get_duration (last_report), - GNUNET_YES)); + true)); last_report = GNUNET_TIME_absolute_get (); } ipc++; @@ -203,11 +234,6 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is) } -/** - * Current command failed, clean up and fail the test case. - * - * @param is interpreter of the test - */ void TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) { @@ -228,27 +254,9 @@ TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is) } -/** - * Create command array terminator. - * - * @return a end-command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_end (void) -{ - static struct TALER_TESTING_Command cmd; - cmd.label = NULL; - - return cmd; -} - - -/** - * Obtain current label. - */ const char * -TALER_TESTING_interpreter_get_current_label (struct - TALER_TESTING_Interpreter *is) +TALER_TESTING_interpreter_get_current_label ( + struct TALER_TESTING_Interpreter *is) { struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; @@ -256,11 +264,6 @@ TALER_TESTING_interpreter_get_current_label (struct } -/** - * Run the main interpreter loop that performs exchange operations. - * - * @param cls contains the `struct TALER_TESTING_Interpreter` - */ static void interpreter_run (void *cls) { @@ -268,7 +271,6 @@ interpreter_run (void *cls) struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; is->task = NULL; - if (NULL == cmd->label) { @@ -286,6 +288,19 @@ interpreter_run (void *cls) = cmd->last_req_time = GNUNET_TIME_absolute_get (); cmd->num_tries = 1; + if (NULL != cmd->name) + { + struct GNUNET_HashCode h_name; + + GNUNET_CRYPTO_hash (cmd->name, + strlen (cmd->name), + &h_name); + (void) GNUNET_CONTAINER_multihashmap_put ( + is->vars, + &h_name, + cmd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } cmd->run (cmd->cls, cmd, is); @@ -308,23 +323,15 @@ do_shutdown (void *cls) label = is->commands[is->ip].label; if (NULL == label) label = "END"; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Executing shutdown at `%s'\n", label); - for (unsigned int j = 0; NULL != (cmd = &is->commands[j])->label; j++) - cmd->cleanup (cmd->cls, - cmd); - - if (NULL != is->exchange) - { - TALER_LOG_DEBUG ("Disconnecting the exchange\n"); - TALER_EXCHANGE_disconnect (is->exchange); - is->exchange = NULL; - } + if (NULL != cmd->cleanup) + cmd->cleanup (cmd->cls, + cmd); if (NULL != is->task) { GNUNET_SCHEDULER_cancel (is->task); @@ -340,20 +347,20 @@ do_shutdown (void *cls) GNUNET_CURL_gnunet_rc_destroy (is->rc); is->rc = NULL; } + if (NULL != is->vars) + { + GNUNET_CONTAINER_multihashmap_destroy (is->vars); + is->vars = NULL; + } if (NULL != is->timeout_task) { GNUNET_SCHEDULER_cancel (is->timeout_task); is->timeout_task = NULL; } - if (NULL != is->child_death_task) + if (NULL != is->cwh) { - GNUNET_SCHEDULER_cancel (is->child_death_task); - is->child_death_task = NULL; - } - if (NULL != is->fakebank) - { - TALER_FAKEBANK_stop (is->fakebank); - is->fakebank = NULL; + GNUNET_wait_child_cancel (is->cwh); + is->cwh = NULL; } GNUNET_free (is->commands); } @@ -362,7 +369,7 @@ do_shutdown (void *cls) /** * Function run when the test terminates (good or bad) with timeout. * - * @param cls NULL + * @param cls the `struct TALER_TESTING_Interpreter *` */ static void do_timeout (void *cls) @@ -380,55 +387,35 @@ do_timeout (void *cls) * Task triggered whenever we receive a SIGCHLD (child * process died). * - * @param cls closure + * @param cls the `struct TALER_TESTING_Interpreter *` + * @param type type of the process + * @param code status code of the process */ static void -maint_child_death (void *cls) +maint_child_death (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int code) { struct TALER_TESTING_Interpreter *is = cls; struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; - const struct GNUNET_DISK_FileHandle *pr; struct GNUNET_OS_Process **processp; - char c[16]; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - - if (TALER_TESTING_cmd_is_batch (cmd)) - { - struct TALER_TESTING_Command *batch_cmd; - - GNUNET_assert (GNUNET_OK == - TALER_TESTING_get_trait_cmd (cmd, - 0, - &batch_cmd)); - cmd = batch_cmd; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + is->cwh = NULL; + while (TALER_TESTING_cmd_is_batch (cmd)) + cmd = TALER_TESTING_cmd_batch_get_current (cmd); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got SIGCHLD for `%s'.\n", cmd->label); - is->child_death_task = NULL; - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - GNUNET_break (0 < - GNUNET_DISK_file_read (pr, - &c, - sizeof (c))); if (GNUNET_OK != TALER_TESTING_get_trait_process (cmd, - 0, &processp)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the dead child process handle, waiting for termination ...\n"); - GNUNET_OS_process_wait_status (*processp, - &type, - &code); GNUNET_OS_process_destroy (*processp); *processp = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -462,45 +449,36 @@ maint_child_death (void *cls) TALER_TESTING_interpreter_fail (is); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Dead child, go on with next command.\n"); TALER_TESTING_interpreter_next (is); } -/** - * Wait until we receive SIGCHLD signal. - * Then obtain the process trait of the current - * command, wait on the the zombie and continue - * with the next command. - */ void TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) { - const struct GNUNET_DISK_FileHandle *pr; + struct GNUNET_OS_Process **processp; + struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; - GNUNET_assert (NULL == is->child_death_task); - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - is->child_death_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, - &maint_child_death, - is); + while (TALER_TESTING_cmd_is_batch (cmd)) + cmd = TALER_TESTING_cmd_batch_get_current (cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_process (cmd, + &processp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_assert (NULL == is->cwh); + is->cwh + = GNUNET_wait_child (*processp, + &maint_child_death, + is); } -/** - * Run the testsuite. Note, CMDs are copied into - * the interpreter state because they are _usually_ - * defined into the "run" method that returns after - * having scheduled the test interpreter. - * - * @param is the interpreter state - * @param commands the list of command to execute - * @param timeout how long to wait - */ void TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is, struct TALER_TESTING_Command *commands, @@ -516,37 +494,36 @@ TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is, /* get the number of commands */ for (i = 0; NULL != commands[i].label; i++) ; - is->commands = GNUNET_new_array (i + 1, - struct TALER_TESTING_Command); - memcpy (is->commands, - commands, - sizeof (struct TALER_TESTING_Command) * i); - is->timeout_task = GNUNET_SCHEDULER_add_delayed - (timeout, - &do_timeout, - is); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is); + is->commands = GNUNET_malloc_large ( (i + 1) + * sizeof (struct TALER_TESTING_Command)); + GNUNET_assert (NULL != is->commands); + GNUNET_memcpy (is->commands, + commands, + sizeof (struct TALER_TESTING_Command) * i); + is->timeout_task = GNUNET_SCHEDULER_add_delayed ( + timeout, + &do_timeout, + is); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + is); + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); } -/** - * Run the testsuite. Note, CMDs are copied into - * the interpreter state because they are _usually_ - * defined into the "run" method that returns after - * having scheduled the test interpreter. - * - * @param is the interpreter state - * @param commands the list of command to execute - */ +#include "valgrind.h" + void TALER_TESTING_run (struct TALER_TESTING_Interpreter *is, struct TALER_TESTING_Command *commands) { TALER_TESTING_run2 (is, commands, - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, - 5)); + 0 == RUNNING_ON_VALGRIND + ? GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, + 5) + : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, + 50)); } @@ -580,376 +557,446 @@ struct MainContext /** - * Signal handler called for SIGCHLD. Triggers the - * respective handler by writing to the trigger pipe. + * Initialize scheduler loop and curl context for the testcase, + * and responsible to run the "run" method. + * + * @param cls closure, typically the "run" method, the + * interpreter state and a closure for "run". */ static void -sighandler_child_death (void) +main_wrapper (void *cls) { - static char c; - int old_errno = errno; /* back-up errno */ + struct MainContext *main_ctx = cls; - GNUNET_break (1 == GNUNET_DISK_file_write - (GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_WRITE), - &c, sizeof (c))); - errno = old_errno; /* restore errno */ + main_ctx->main_cb (main_ctx->main_cb_cls, + main_ctx->is); } -/** - * "Canonical" cert_cb used when we are connecting to the - * Exchange. - * - * @param cls closure, typically, the "run" method containing - * all the commands to be run, and a closure for it. - * @param hr HTTP response details - * @param keys the exchange's keys. - * @param compat protocol compatibility information. - */ +enum GNUNET_GenericReturnValue +TALER_TESTING_loop (TALER_TESTING_Main main_cb, + void *main_cb_cls) +{ + struct TALER_TESTING_Interpreter is; + struct MainContext main_ctx = { + .main_cb = main_cb, + .main_cb_cls = main_cb_cls, + /* needed to init the curl ctx */ + .is = &is, + }; + + memset (&is, + 0, + sizeof (is)); + is.ctx = GNUNET_CURL_init ( + &GNUNET_CURL_gnunet_scheduler_reschedule, + &is.rc); + GNUNET_CURL_enable_async_scope_header (is.ctx, + "Taler-Correlation-Id"); + GNUNET_assert (NULL != is.ctx); + is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx); + is.vars = GNUNET_CONTAINER_multihashmap_create (1024, + false); + /* Blocking */ + GNUNET_SCHEDULER_run (&main_wrapper, + &main_ctx); + return is.result; +} + + +int +TALER_TESTING_main (char *const *argv, + const char *loglevel, + const char *cfg_file, + const char *exchange_account_section, + enum TALER_TESTING_BankSystem bs, + struct TALER_TESTING_Credentials *cred, + TALER_TESTING_Main main_cb, + void *main_cb_cls) +{ + enum GNUNET_GenericReturnValue ret; + + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup (argv[0], + loglevel, + NULL); + if (GNUNET_OK != + TALER_TESTING_get_credentials (cfg_file, + exchange_account_section, + bs, + cred)) + { + GNUNET_break (0); + return 77; + } + if (GNUNET_OK != + TALER_TESTING_cleanup_files_cfg (NULL, + cred->cfg)) + { + GNUNET_break (0); + return 77; + } + if (GNUNET_OK != + TALER_extensions_init (cred->cfg)) + { + GNUNET_break (0); + return 77; + } + ret = TALER_TESTING_loop (main_cb, + main_cb_cls); + /* TODO: should we free 'cred' resources here? */ + return (GNUNET_OK == ret) ? 0 : 1; +} + + +/* ************** iterate over commands ********* */ + + void -TALER_TESTING_cert_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_EXCHANGE_Keys *keys, - enum TALER_EXCHANGE_VersionCompatibility compat) +TALER_TESTING_iterate (struct TALER_TESTING_Interpreter *is, + bool asc, + TALER_TESTING_CommandIterator cb, + void *cb_cls) { - struct MainContext *main_ctx = cls; - struct TALER_TESTING_Interpreter *is = main_ctx->is; + unsigned int start; + unsigned int end; + int inc; - (void) compat; - if (NULL == keys) + if (asc) { - if (GNUNET_NO == is->working) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got NULL response for /keys during startup (%u/%d), retrying!\n", - hr->http_status, - (int) hr->ec); - TALER_EXCHANGE_disconnect (is->exchange); - GNUNET_assert (NULL != (is->exchange - = TALER_EXCHANGE_connect (is->ctx, - main_ctx->exchange_url, - &TALER_TESTING_cert_cb, - main_ctx, - TALER_EXCHANGE_OPTION_END))); - return; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Got NULL response for /keys during execution (%u/%d)!\n", - hr->http_status, - (int) hr->ec); - } + inc = 1; + start = 0; + end = is->ip; } else { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got %d DK from /keys in generation %u\n", - keys->num_denom_keys, - is->key_generation + 1); + inc = -1; + start = is->ip; + end = 0; } - is->key_generation++; - is->keys = keys; + for (unsigned int off = start; off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *cmd = &is->commands[off]; - /* /keys has been called for some reason and - * the interpreter is already running. */ - if (GNUNET_YES == is->working) - return; - is->working = GNUNET_YES; - /* Trigger the next command. */ - TALER_LOG_DEBUG ("Cert_cb, scheduling CMD (ip: %d)\n", - is->ip); - GNUNET_SCHEDULER_add_now (&interpreter_run, - is); + cb (cb_cls, + cmd); + } +} + + +/* ************** special commands ********* */ + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_end (void) +{ + static struct TALER_TESTING_Command cmd; + cmd.label = NULL; + + return cmd; +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_set_var (const char *name, + struct TALER_TESTING_Command cmd) +{ + cmd.name = name; + return cmd; } /** - * Initialize scheduler loop and curl context for the testcase, - * and responsible to run the "run" method. - * - * @param cls closure, typically the "run" method, the - * interpreter state and a closure for "run". + * State for a "rewind" CMD. */ -static void -main_wrapper_exchange_agnostic (void *cls) +struct RewindIpState { - struct MainContext *main_ctx = cls; + /** + * Instruction pointer to set into the interpreter. + */ + const char *target_label; - main_ctx->main_cb (main_ctx->main_cb_cls, - main_ctx->is); + /** + * How many times this set should take place. However, this value lives at + * the calling process, and this CMD is only in charge of checking and + * decremeting it. + */ + unsigned int counter; +}; + + +/** + * Seek for the @a target command in @a batch (and rewind to it + * if successful). + * + * @param is the interpreter state (for failures) + * @param cmd batch to search for @a target + * @param target command to search for + * @return #GNUNET_OK on success, #GNUNET_NO if target was not found, + * #GNUNET_SYSERR if target is in the future and we failed + */ +static enum GNUNET_GenericReturnValue +seek_batch (struct TALER_TESTING_Interpreter *is, + const struct TALER_TESTING_Command *cmd, + const struct TALER_TESTING_Command *target) +{ + unsigned int new_ip; + struct TALER_TESTING_Command *batch; + struct TALER_TESTING_Command *current; + struct TALER_TESTING_Command *icmd; + struct TALER_TESTING_Command *match; + + current = TALER_TESTING_cmd_batch_get_current (cmd); + GNUNET_assert (GNUNET_OK == + TALER_TESTING_get_trait_batch_cmds (cmd, + &batch)); + match = NULL; + for (new_ip = 0; + NULL != (icmd = &batch[new_ip]); + new_ip++) + { + if (current == target) + current = NULL; + if (icmd == target) + { + match = icmd; + break; + } + if (TALER_TESTING_cmd_is_batch (icmd)) + { + int ret = seek_batch (is, + icmd, + target); + if (GNUNET_SYSERR == ret) + return GNUNET_SYSERR; /* failure! */ + if (GNUNET_OK == ret) + { + match = icmd; + break; + } + } + } + if (NULL == current) + { + /* refuse to jump forward */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return GNUNET_SYSERR; + } + if (NULL == match) + return GNUNET_NO; /* not found */ + TALER_TESTING_cmd_batch_set_current (cmd, + new_ip); + return GNUNET_OK; } /** - * Function run when the test is aborted before we launch the actual - * interpreter. Cleans up our state. + * Run the "rewind" CMD. * - * @param cls the main context + * @param cls closure. + * @param cmd command being executed now. + * @param is the interpreter state. */ static void -do_abort (void *cls) +rewind_ip_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) { - struct MainContext *main_ctx = cls; - struct TALER_TESTING_Interpreter *is = main_ctx->is; + struct RewindIpState *ris = cls; + const struct TALER_TESTING_Command *target; + unsigned int new_ip; - is->timeout_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Executing abort prior to interpreter launch\n"); - if (NULL != is->exchange) + (void) cmd; + if (0 == ris->counter) + { + TALER_TESTING_interpreter_next (is); + return; + } + target + = TALER_TESTING_interpreter_lookup_command (is, + ris->target_label); + if (NULL == target) { - TALER_EXCHANGE_disconnect (is->exchange); - is->exchange = NULL; + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; } - if (NULL != is->ctx) + ris->counter--; + for (new_ip = 0; + NULL != is->commands[new_ip].label; + new_ip++) { - GNUNET_CURL_fini (is->ctx); - is->ctx = NULL; + const struct TALER_TESTING_Command *cmd = &is->commands[new_ip]; + + if (cmd == target) + break; + if (TALER_TESTING_cmd_is_batch (cmd)) + { + int ret = seek_batch (is, + cmd, + target); + if (GNUNET_SYSERR == ret) + return; /* failure! */ + if (GNUNET_OK == ret) + break; + } } - if (NULL != is->rc) + if (new_ip > (unsigned int) is->ip) { - GNUNET_CURL_gnunet_rc_destroy (is->rc); - is->rc = NULL; + /* refuse to jump forward */ + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; } + is->ip = new_ip - 1; /* -1 because the next function will advance by one */ + TALER_TESTING_interpreter_next (is); } -/** - * Initialize scheduler loop and curl context for the testcase, - * and responsible to run the "run" method. - * - * @param cls a `struct MainContext *` - */ -static void -main_wrapper_exchange_connect (void *cls) +struct TALER_TESTING_Command +TALER_TESTING_cmd_rewind_ip (const char *label, + const char *target_label, + unsigned int counter) { - struct MainContext *main_ctx = cls; - struct TALER_TESTING_Interpreter *is = main_ctx->is; - char *exchange_url; + struct RewindIpState *ris; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (is->cfg, - "exchange", - "BASE_URL", - &exchange_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "BASE_URL"); - return; + ris = GNUNET_new (struct RewindIpState); + ris->target_label = target_label; + ris->counter = counter; + { + struct TALER_TESTING_Command cmd = { + .cls = ris, + .label = label, + .run = &rewind_ip_run + }; + + return cmd; } - main_ctx->exchange_url = exchange_url; - is->timeout_task = GNUNET_SCHEDULER_add_shutdown (&do_abort, - main_ctx); - is->working = GNUNET_YES; - GNUNET_break - (NULL != (is->exchange = - TALER_EXCHANGE_connect (is->ctx, - exchange_url, - &TALER_TESTING_cert_cb, - main_ctx, - TALER_EXCHANGE_OPTION_END))); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting main test loop\n"); - main_ctx->main_cb (main_ctx->main_cb_cls, - is); } /** - * Load the exchange and auditor key material into @a is. + * State for a "authchange" CMD. + */ +struct AuthchangeState +{ + + /** + * What is the new authorization token to send? + */ + const char *auth_token; + + /** + * Old context, clean up on termination. + */ + struct GNUNET_CURL_Context *old_ctx; +}; + + +/** + * Run the command. * - * @param[in,out] is state to initialize + * @param cls closure. + * @param cmd the command to execute. + * @param is the interpreter state. */ -static int -load_keys (struct TALER_TESTING_Interpreter *is) +static void +authchange_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) { - char *fn; + struct AuthchangeState *ss = cls; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (is->cfg, - "exchange-offline", - "MASTER_PRIV_FILE", - &fn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-offline", - "MASTER_PRIV_FILE"); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_DISK_directory_create_for_file (fn)) + (void) cmd; + ss->old_ctx = is->ctx; + if (NULL != is->rc) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not setup directory for master private key file `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; + GNUNET_CURL_gnunet_rc_destroy (is->rc); + is->rc = NULL; } - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_key_from_file (fn, - GNUNET_YES, - &is->master_priv.eddsa_priv)) + is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &is->rc); + GNUNET_CURL_enable_async_scope_header (is->ctx, + "Taler-Correlation-Id"); + GNUNET_assert (NULL != is->ctx); + is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); + if (NULL != ss->auth_token) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not load master private key from `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; - } - GNUNET_free (fn); - GNUNET_CRYPTO_eddsa_key_get_public (&is->master_priv.eddsa_priv, - &is->master_pub.eddsa_pub); + char *authorization; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (is->cfg, - "auditor", - "AUDITOR_PRIV_FILE", - &fn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "AUDITOR_PRIV_FILE"); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_DISK_directory_create_for_file (fn)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not setup directory for auditor private key file `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_key_from_file (fn, - GNUNET_YES, - &is->auditor_priv.eddsa_priv)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not load auditor private key from `%s'\n", - fn); - GNUNET_free (fn); - return GNUNET_SYSERR; + GNUNET_asprintf (&authorization, + "%s: %s", + MHD_HTTP_HEADER_AUTHORIZATION, + ss->auth_token); + GNUNET_assert (GNUNET_OK == + GNUNET_CURL_append_header (is->ctx, + authorization)); + GNUNET_free (authorization); } - GNUNET_free (fn); - GNUNET_CRYPTO_eddsa_key_get_public (&is->auditor_priv.eddsa_priv, - &is->auditor_pub.eddsa_pub); - return GNUNET_OK; + TALER_TESTING_interpreter_next (is); } /** - * Load the exchange and auditor URLs from the configuration into @a is. + * Call GNUNET_CURL_fini(). Done as a separate task to + * ensure that all of the command's cleanups have been + * executed first. See #7151. * - * @param[in,out] is state to initialize + * @param cls a `struct GNUNET_CURL_Context *` to clean up. */ -static int -load_urls (struct TALER_TESTING_Interpreter *is) +static void +deferred_cleanup_cb (void *cls) { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (is->cfg, - "auditor", - "BASE_URL", - &is->auditor_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "auditor", - "BASE_URL"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (is->cfg, - "exchange", - "BASE_URL", - &is->exchange_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "BASE_URL"); - GNUNET_free (is->auditor_url); - return GNUNET_SYSERR; - } - return GNUNET_OK; + struct GNUNET_CURL_Context *ctx = cls; + + GNUNET_CURL_fini (ctx); } /** - * Install signal handlers plus schedules the main wrapper - * around the "run" method. + * Cleanup the state from a "authchange" CMD. * - * @param main_cb the "run" method which contains all the - * commands. - * @param main_cb_cls a closure for "run", typically NULL. - * @param cfg configuration to use - * @param exchanged exchange process handle: will be put in the - * state as some commands - e.g. revoke - need to send - * signal to it, for example to let it know to reload the - * key state.. if NULL, the interpreter will run without - * trying to connect to the exchange first. - * @param exchange_connect #GNUNET_YES if the test should connect - * to the exchange, #GNUNET_NO otherwise - * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise. - * non-GNUNET_OK codes are #GNUNET_SYSERR most of the - * times. + * @param cls closure. + * @param cmd the command which is being cleaned up. */ -int -TALER_TESTING_setup (TALER_TESTING_Main main_cb, - void *main_cb_cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_OS_Process *exchanged, - int exchange_connect) +static void +authchange_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) { - struct TALER_TESTING_Interpreter is; - struct MainContext main_ctx = { - .main_cb = main_cb, - .main_cb_cls = main_cb_cls, - /* needed to init the curl ctx */ - .is = &is, - }; - struct GNUNET_SIGNAL_Context *shc_chld; + struct AuthchangeState *ss = cls; - memset (&is, - 0, - sizeof (is)); - is.exchanged = exchanged; - is.cfg = cfg; - if (GNUNET_OK != - load_keys (&is)) - return GNUNET_SYSERR; - if (GNUNET_OK != - load_urls (&is)) - return GNUNET_SYSERR; - sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); - GNUNET_assert (NULL != sigpipe); - shc_chld = GNUNET_SIGNAL_handler_install - (GNUNET_SIGCHLD, - &sighandler_child_death); - is.ctx = GNUNET_CURL_init - (&GNUNET_CURL_gnunet_scheduler_reschedule, - &is.rc); - GNUNET_CURL_enable_async_scope_header (is.ctx, "Taler-Correlation-Id"); - GNUNET_assert (NULL != is.ctx); - is.rc = GNUNET_CURL_gnunet_rc_create (is.ctx); + (void) cmd; + if (NULL != ss->old_ctx) + { + (void) GNUNET_SCHEDULER_add_now (&deferred_cleanup_cb, + ss->old_ctx); + ss->old_ctx = NULL; + } + GNUNET_free (ss); +} - /* Blocking */ - if (GNUNET_YES == exchange_connect) - GNUNET_SCHEDULER_run (&main_wrapper_exchange_connect, - &main_ctx); - else - GNUNET_SCHEDULER_run (&main_wrapper_exchange_agnostic, - &main_ctx); - if (NULL != is.final_cleanup_cb) - is.final_cleanup_cb (is.final_cleanup_cb_cls); - GNUNET_free (main_ctx.exchange_url); - GNUNET_SIGNAL_handler_uninstall (shc_chld); - GNUNET_DISK_pipe_close (sigpipe); - sigpipe = NULL; - GNUNET_free (is.auditor_url); - GNUNET_free (is.exchange_url); - return is.result; +struct TALER_TESTING_Command +TALER_TESTING_cmd_set_authorization (const char *label, + const char *auth_token) +{ + struct AuthchangeState *ss; + + ss = GNUNET_new (struct AuthchangeState); + ss->auth_token = auth_token; + + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &authchange_run, + .cleanup = &authchange_cleanup + }; + + return cmd; + } } diff --git a/src/testing/testing_api_misc.c b/src/testing/testing_api_misc.c new file mode 100644 index 000000000..80ff0b6c8 --- /dev/null +++ b/src/testing/testing_api_misc.c @@ -0,0 +1,394 @@ +/* + This file is part of TALER + Copyright (C) 2018-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing/testing_api_misc.c + * @brief non-command functions useful for writing tests + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_testing_lib.h" +#include "taler_fakebank_lib.h" + + +bool +TALER_TESTING_has_in_name (const char *prog, + const char *marker) +{ + size_t name_pos; + size_t pos; + + if (! prog || ! marker) + return false; + + pos = 0; + name_pos = 0; + while (prog[pos]) + { + if ('/' == prog[pos]) + name_pos = pos + 1; + pos++; + } + if (name_pos == pos) + return true; + return (NULL != strstr (prog + name_pos, + marker)); +} + + +enum GNUNET_GenericReturnValue +TALER_TESTING_get_credentials ( + const char *cfg_file, + const char *exchange_account_section, + enum TALER_TESTING_BankSystem bs, + struct TALER_TESTING_Credentials *ua) +{ + unsigned long long port; + char *exchange_payto_uri; + + ua->cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (ua->cfg, + cfg_file)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (ua->cfg); + return GNUNET_SYSERR; + } + if (0 != + strncasecmp (exchange_account_section, + "exchange-account-", + strlen ("exchange-account-"))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ua->cfg, + exchange_account_section, + "PAYTO_URI", + &exchange_payto_uri)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + exchange_account_section, + "PAYTO_URI"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (ua->cfg, + "bank", + "HTTP_PORT", + &port)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "bank", + "HTTP_PORT"); + return GNUNET_SYSERR; + } + { + char *csn; + + GNUNET_asprintf (&csn, + "exchange-accountcredentials-%s", + &exchange_account_section[strlen ("exchange-account-")]); + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (ua->cfg, + csn, + &ua->ba)) + { + GNUNET_break (0); + GNUNET_free (csn); + return GNUNET_SYSERR; + } + GNUNET_free (csn); + } + { + char *csn; + + GNUNET_asprintf (&csn, + "admin-accountcredentials-%s", + &exchange_account_section[strlen ("exchange-account-")]); + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (ua->cfg, + csn, + &ua->ba_admin)) + { + GNUNET_break (0); + GNUNET_free (csn); + return GNUNET_SYSERR; + } + GNUNET_free (csn); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ua->cfg, + "exchange", + "BASE_URL", + &ua->exchange_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "BASE_URL"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ua->cfg, + "auditor", + "BASE_URL", + &ua->auditor_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "auditor", + "BASE_URL"); + return GNUNET_SYSERR; + } + + switch (bs) + { + case TALER_TESTING_BS_FAKEBANK: + ua->exchange_payto + = exchange_payto_uri; + ua->user42_payto + = GNUNET_strdup ("payto://x-taler-bank/localhost/42?receiver-name=42"); + ua->user43_payto + = GNUNET_strdup ("payto://x-taler-bank/localhost/43?receiver-name=43"); + break; + case TALER_TESTING_BS_IBAN: + ua->exchange_payto + = exchange_payto_uri; + ua->user42_payto + = GNUNET_strdup ( + "payto://iban/SANDBOXX/FR7630006000011234567890189?receiver-name=User42"); + ua->user43_payto + = GNUNET_strdup ( + "payto://iban/SANDBOXX/GB33BUKB20201555555555?receiver-name=User43"); + break; + } + return GNUNET_OK; +} + + +json_t * +TALER_TESTING_make_wire_details (const char *payto) +{ + struct TALER_WireSaltP salt; + + /* salt must be constant for aggregation tests! */ + memset (&salt, + 47, + sizeof (salt)); + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("payto_uri", + payto), + GNUNET_JSON_pack_data_auto ("salt", + &salt)); +} + + +/** + * Remove @a option directory from @a section in @a cfg. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +remove_dir (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + const char *option) +{ + char *dir; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + option, + &dir)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + option); + 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); + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_TESTING_cleanup_files_cfg ( + void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *dir; + + (void) cls; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "exchange-offline", + "SECM_TOFU_FILE", + &dir)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange-offline", + "SECM_TOFU_FILE"); + return GNUNET_SYSERR; + } + if ( (0 != unlink (dir)) && + (ENOENT != errno) ) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "unlink", + dir); + GNUNET_free (dir); + return GNUNET_SYSERR; + } + GNUNET_free (dir); + if (GNUNET_OK != + remove_dir (cfg, + "taler-exchange-secmod-eddsa", + "KEY_DIR")) + return GNUNET_SYSERR; + if (GNUNET_OK != + remove_dir (cfg, + "taler-exchange-secmod-rsa", + "KEY_DIR")) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +const struct TALER_EXCHANGE_DenomPublicKey * +TALER_TESTING_find_pk ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_Amount *amount, + bool age_restricted) +{ + struct GNUNET_TIME_Timestamp now; + struct TALER_EXCHANGE_DenomPublicKey *pk; + char *str; + + now = GNUNET_TIME_timestamp_get (); + for (unsigned int i = 0; i<keys->num_denom_keys; i++) + { + pk = &keys->denom_keys[i]; + if ( (0 == TALER_amount_cmp (amount, + &pk->value)) && + (GNUNET_TIME_timestamp_cmp (now, + >=, + pk->valid_from)) && + (GNUNET_TIME_timestamp_cmp (now, + <, + pk->withdraw_valid_until)) && + (age_restricted == (0 != pk->key.age_mask.bits)) ) + 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; i<keys->num_denom_keys; i++) + { + pk = &keys->denom_keys[i]; + if ( (0 == TALER_amount_cmp (amount, + &pk->value)) && + (GNUNET_TIME_timestamp_cmp (now, + <, + pk->valid_from) || + GNUNET_TIME_timestamp_cmp (now, + >, + pk->withdraw_valid_until) ) && + (age_restricted == (0 != pk->key.age_mask.bits)) ) + { + 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_time.abs_value_us, + (unsigned long long) pk->valid_from.abs_time.abs_value_us, + (unsigned long long) pk->withdraw_valid_until.abs_time.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; +} + + +int +TALER_TESTING_wait_httpd_ready (const char *base_url) +{ + char *wget_cmd; + unsigned int iter; + + GNUNET_asprintf (&wget_cmd, + "wget -q -t 1 -T 1 %s -o /dev/null -O /dev/null", + base_url); // make sure ends with '/' + /* give child time to start and bind against the socket */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Waiting for HTTP service to be ready (check with: %s)\n", + wget_cmd); + iter = 0; + do + { + if (10 == iter) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to launch HTTP service (or `wget')\n"); + GNUNET_free (wget_cmd); + return 77; + } + sleep (1); + iter++; + } + while (0 != system (wget_cmd)); + GNUNET_free (wget_cmd); + return 0; +} + + +enum GNUNET_GenericReturnValue +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; +} diff --git a/src/testing/testing_api_trait_amount.c b/src/testing/testing_api_trait_amount.c deleted file mode 100644 index 96698b49e..000000000 --- a/src/testing/testing_api_trait_amount.c +++ /dev/null @@ -1,76 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_amount.c - * @brief offer amounts as traits. - * @author Marcello Stanisci - */ - -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_AMOUNT "amount" - -/** - * Obtain an amount from a @a cmd. - * - * @param cmd command to extract the amount from. - * @param index which amount to pick if @a cmd has multiple - * on offer - * @param[out] amount set to the amount. - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_get_trait_amount_obj (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_Amount **amount) -{ - return cmd->traits (cmd->cls, - (const void **) amount, - TALER_TESTING_TRAIT_AMOUNT, - index); -} - - -/** - * Offer amount. - * - * @param index which amount to offer, in case there are - * multiple available. - * @param amount the amount to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_amount_obj (unsigned int index, - const struct TALER_Amount *amount) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_AMOUNT, - .ptr = (const void *) amount - }; - - return ret; -} - - -/* end of testing_api_trait_amount.c */ diff --git a/src/testing/testing_api_trait_blinding_key.c b/src/testing/testing_api_trait_blinding_key.c deleted file mode 100644 index ae1889a1f..000000000 --- a/src/testing/testing_api_trait_blinding_key.c +++ /dev/null @@ -1,77 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_blinding_key.c - * @brief offer blinding keys as traits. - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_BLINDING_KEY "blinding-key" - - -/** - * Obtain a blinding key from a @a cmd. - * - * @param cmd command to extract trait from - * @param index which coin to pick if @a cmd has multiple on offer. - * @param[out] blinding_key set to the offered blinding key. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_blinding_key - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_DenominationBlindingKeyP **blinding_key) -{ - return cmd->traits (cmd->cls, - (const void **) blinding_key, - TALER_TESTING_TRAIT_BLINDING_KEY, - index); -} - - -/** - * Offer blinding key. - * - * @param index index number to associate to the offered key. - * @param blinding_key blinding key to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_blinding_key - (unsigned int index, - const struct TALER_DenominationBlindingKeyP *blinding_key) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_BLINDING_KEY, - .ptr = (const void *) blinding_key - }; - - return ret; -} - - -/* end of testing_api_trait_blinding_key.c */ diff --git a/src/testing/testing_api_trait_cmd.c b/src/testing/testing_api_trait_cmd.c deleted file mode 100644 index f24054713..000000000 --- a/src/testing/testing_api_trait_cmd.c +++ /dev/null @@ -1,80 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_cmd.c - * @brief offers CMDs as traits. - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_CMD "cmd" - - -/** - * Obtain a command from @a cmd. - * - * @param cmd command to extract the command from. - * @param index always zero. Commands offering this - * kind of traits do not need this index. For - * example, a "batch" CMD returns always the - * CMD currently being executed. - * @param[out] _cmd where to write the wire details. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_cmd (const struct TALER_TESTING_Command *cmd, - unsigned int index, - struct TALER_TESTING_Command **_cmd) -{ - return cmd->traits (cmd->cls, - (const void **) _cmd, - TALER_TESTING_TRAIT_CMD, - index); -} - - -/** - * Offer a command in a trait. - * - * @param index always zero. Commands offering this - * kind of traits do not need this index. For - * example, a "meta" CMD returns always the - * CMD currently being executed. - * @param cmd wire details to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_cmd (unsigned int index, - const struct TALER_TESTING_Command *cmd) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_CMD, - .ptr = (const struct TALER_TESTING_Command *) cmd - }; - return ret; -} - - -/* end of testing_api_trait_cmd.c */ diff --git a/src/testing/testing_api_trait_coin_priv.c b/src/testing/testing_api_trait_coin_priv.c deleted file mode 100644 index 61a770cf6..000000000 --- a/src/testing/testing_api_trait_coin_priv.c +++ /dev/null @@ -1,78 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_coin_priv.c - * @brief coin priv traits. - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_COIN_PRIVATE_KEY "coin-private-key" - - -/** - * Obtain a coin private key from a @a cmd. - * - * @param cmd command to extract trait from. - * @param index index of the coin priv to obtain. - * @param[out] coin_priv set to the private key of the coin. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_coin_priv - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_CoinSpendPrivateKeyP **coin_priv) -{ - return cmd->traits (cmd->cls, - (const void **) coin_priv, - TALER_TESTING_TRAIT_COIN_PRIVATE_KEY, - index); -} - - -/** - * Offer coin private key. - * - * @param index index number to associate with offered coin priv. - * @param coin_priv coin private key to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_coin_priv - (unsigned int index, - const struct TALER_CoinSpendPrivateKeyP *coin_priv) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY, - .ptr = (const void *) coin_priv - }; - - return ret; -} - - -/* end of testing_api_trait_coin_priv.c */ diff --git a/src/testing/testing_api_trait_contract.c b/src/testing/testing_api_trait_contract.c deleted file mode 100644 index 1e88cb86f..000000000 --- a/src/testing/testing_api_trait_contract.c +++ /dev/null @@ -1,74 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_contract.c - * @brief offers contract term trait. - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_testing_lib.h" - - -/** - * Contains a contract terms object as a json_t. - */ -#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms" - - -/** - * Obtain contract terms from @a cmd. - * - * @param cmd command to extract the contract terms from. - * @param index contract terms index number. - * @param[out] contract_terms where to write the contract terms. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_contract_terms (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const json_t **contract_terms) -{ - return cmd->traits (cmd->cls, - (const void **) contract_terms, - TALER_TESTING_TRAIT_CONTRACT_TERMS, - index); -} - - -/** - * Offer contract terms. - * - * @param index contract terms index number. - * @param contract_terms contract terms to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_contract_terms (unsigned int index, - const json_t *contract_terms) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_CONTRACT_TERMS, - .ptr = (const void *) contract_terms - }; - return ret; -} diff --git a/src/testing/testing_api_trait_denom_pub.c b/src/testing/testing_api_trait_denom_pub.c deleted file mode 100644 index f866588db..000000000 --- a/src/testing/testing_api_trait_denom_pub.c +++ /dev/null @@ -1,77 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_denom_pub.c - * @brief denom pub traits. - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_DENOM_PUB "denomination-public-key" - - -/** - * Obtain a denomination public key from a @a cmd. - * - * @param cmd command to extract trait from - * @param index index number of the denom to obtain. - * @param[out] denom_pub set to the offered denom pub. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct - TALER_EXCHANGE_DenomPublicKey **denom_pub) -{ - return cmd->traits (cmd->cls, - (const void **) denom_pub, - TALER_TESTING_TRAIT_DENOM_PUB, - index); -} - - -/** - * Make a trait for a denomination public key. - * - * @param index index number to associate to the offered denom pub. - * @param denom_pub denom pub to offer with this trait. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_denom_pub (unsigned int index, - const struct - TALER_EXCHANGE_DenomPublicKey *denom_pub) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_DENOM_PUB, - .ptr = (const void *) denom_pub - }; - - return ret; -} - - -/* end of testing_api_trait_denom_pub.c */ diff --git a/src/testing/testing_api_trait_denom_sig.c b/src/testing/testing_api_trait_denom_sig.c deleted file mode 100644 index 07e89440e..000000000 --- a/src/testing/testing_api_trait_denom_sig.c +++ /dev/null @@ -1,79 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_denom_sig.c - * @brief offer denomination signatures as traits - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_DENOM_SIG "denomination-signature" - - -/** - * Obtain a denomination signature from a @a cmd. - * - * @param cmd command to extract the denom sig from. - * @param index index number associated with the denom sig. - * @param[out] denom_sig set to the offered signature. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_denom_sig - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_DenominationSignature **denom_sig) -{ - return cmd->traits (cmd->cls, - (const void **) denom_sig, - TALER_TESTING_TRAIT_DENOM_SIG, - index); -} - - -/** - * Offer denom sig. - * - * @param index index number to associate to the signature on - * offer. - * @param denom_sig the denom sig on offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_denom_sig - (unsigned int index, - const struct TALER_DenominationSignature *denom_sig) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_DENOM_SIG, - .ptr = (const void *) denom_sig - }; - - return ret; -} - - -/* end of testing_api_trait_denom_sig.c */ diff --git a/src/testing/testing_api_trait_exchange_pub.c b/src/testing/testing_api_trait_exchange_pub.c deleted file mode 100644 index 8c7027260..000000000 --- a/src/testing/testing_api_trait_exchange_pub.c +++ /dev/null @@ -1,77 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_exchange_pub.c - * @brief exchange pub traits. - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_EXCHANGE_PUB "exchange-public-key" - - -/** - * Obtain a exchange public key from a @a cmd. - * - * @param cmd command to extract trait from - * @param index index number of the exchange to obtain. - * @param[out] exchange_pub set to the offered exchange pub. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_exchange_pub - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_ExchangePublicKeyP **exchange_pub) -{ - return cmd->traits (cmd->cls, - (const void **) exchange_pub, - TALER_TESTING_TRAIT_EXCHANGE_PUB, - index); -} - - -/** - * Make a trait for a exchange public key. - * - * @param index index number to associate to the offered exchange pub. - * @param exchange_pub exchange pub to offer with this trait. - * - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_exchange_pub - (unsigned int index, - const struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_EXCHANGE_PUB, - .ptr = (const void *) exchange_pub - }; - - return ret; -} - - -/* end of testing_api_trait_exchange_pub.c */ diff --git a/src/testing/testing_api_trait_exchange_sig.c b/src/testing/testing_api_trait_exchange_sig.c deleted file mode 100644 index 349454ae7..000000000 --- a/src/testing/testing_api_trait_exchange_sig.c +++ /dev/null @@ -1,77 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_exchange_sig.c - * @brief exchange pub traits. - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_EXCHANGE_SIG "exchange-online-signature" - - -/** - * Obtain a exchange signature (online sig) from a @a cmd. - * - * @param cmd command to extract trait from - * @param index index number of the exchange to obtain. - * @param[out] exchange_sig set to the offered exchange signature. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_exchange_sig - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_ExchangeSignatureP **exchange_sig) -{ - return cmd->traits (cmd->cls, - (const void **) exchange_sig, - TALER_TESTING_TRAIT_EXCHANGE_SIG, - index); -} - - -/** - * Make a trait for a exchange signature. - * - * @param index index number to associate to the offered exchange pub. - * @param exchange_sig exchange signature to offer with this trait. - * - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_exchange_sig - (unsigned int index, - const struct TALER_ExchangeSignatureP *exchange_sig) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_EXCHANGE_SIG, - .ptr = (const void *) exchange_sig - }; - - return ret; -} - - -/* end of testing_api_trait_exchange_sig.c */ diff --git a/src/testing/testing_api_trait_fresh_coin.c b/src/testing/testing_api_trait_fresh_coin.c deleted file mode 100644 index e5f1c6827..000000000 --- a/src/testing/testing_api_trait_fresh_coin.c +++ /dev/null @@ -1,77 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_fresh_coin.c - * @brief traits to offer fresh conins (after "melt" operations) - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_FRESH_COINS "fresh-coins" - -/** - * Get a array of fresh coins. - * - * @param cmd command to extract the fresh coin from. - * @param index which array to pick if @a cmd has multiple - * on offer. - * @param[out] fresh_coins will point to the offered array. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_fresh_coins - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_TESTING_FreshCoinData **fresh_coins) -{ - return cmd->traits (cmd->cls, - (const void **) fresh_coins, - TALER_TESTING_TRAIT_FRESH_COINS, - index); -} - - -/** - * Offer a _array_ of fresh coins. - * - * @param index which array of fresh coins to offer, - * if there are multiple on offer. Typically passed as - * zero. - * @param fresh_coins the array of fresh coins to offer - * @return the trait, - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_fresh_coins - (unsigned int index, - const struct TALER_TESTING_FreshCoinData *fresh_coins) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_FRESH_COINS, - .ptr = (const void *) fresh_coins - }; - return ret; -} - - -/* end of testing_api_trait_fresh_coin.c */ diff --git a/src/testing/testing_api_trait_json.c b/src/testing/testing_api_trait_json.c deleted file mode 100644 index e12f1e9a1..000000000 --- a/src/testing/testing_api_trait_json.c +++ /dev/null @@ -1,165 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_json.c - * @brief offers JSON traits. - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details" -#define TALER_TESTING_TRAIT_EXCHANGE_KEYS "exchange-keys" -#define TALER_TESTING_TRAIT_JSON "json" - -/** - * Obtain serialized exchange keys from @a cmd. - * - * @param cmd command to extract the keys from. - * @param index index number associate with the keys on offer. - * @param[out] keys where to write the serialized keys. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_exchange_keys - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const json_t **keys) -{ - return cmd->traits (cmd->cls, - (const void **) keys, - TALER_TESTING_TRAIT_EXCHANGE_KEYS, - index); -} - - -/** - * Offer serialized keys in a trait. - * - * @param index index number associate with the serial keys - * on offer. - * @param keys serialized keys to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_exchange_keys - (unsigned int index, - const json_t *keys) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_EXCHANGE_KEYS, - .ptr = (const json_t *) keys - }; - return ret; -} - - -/** - * Obtain wire details from @a cmd. - * - * @param cmd command to extract the wire details from. - * @param index index number associate with the wire details - * on offer; usually zero, as one command sticks to - * one bank account. - * @param[out] wire_details where to write the wire details. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_wire_details - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const json_t **wire_details) -{ - return cmd->traits (cmd->cls, - (const void **) wire_details, - TALER_TESTING_TRAIT_WIRE_DETAILS, - index); -} - - -/** - * Offer wire details in a trait. - * - * @param index index number associate with the wire details - * on offer; usually zero, as one command sticks to - * one bank account. - * @param wire_details wire details to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_wire_details - (unsigned int index, - const json_t *wire_details) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS, - .ptr = (const json_t *) wire_details - }; - return ret; -} - - -/** - * Obtain json from @a cmd. - * - * @param cmd command to extract the json from. - * @param index index number associate with the json on offer. - * @param[out] json where to write the json. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_json (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const json_t **json) -{ - return cmd->traits (cmd->cls, - (const void **) json, - TALER_TESTING_TRAIT_JSON, - index); -} - - -/** - * Offer json in a trait. - * - * @param index index number associate with the json - * on offer. - * @param json json to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_json (unsigned int index, - const json_t *json) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_JSON, - .ptr = (const json_t *) json - }; - return ret; -} - - -/* end of testing_api_trait_json.c */ diff --git a/src/testing/testing_api_trait_merchant_key.c b/src/testing/testing_api_trait_merchant_key.c deleted file mode 100644 index 41b6b8883..000000000 --- a/src/testing/testing_api_trait_merchant_key.c +++ /dev/null @@ -1,127 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_merchant_key.c - * @brief traits to offer peer's (private) keys - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_MERCHANT_PRIV "merchant-priv" -#define TALER_TESTING_TRAIT_MERCHANT_PUB "merchant-pub-pub" - -/** - * Obtain a private key from a "peer". Used e.g. to obtain - * a merchant's priv to sign a /track request. - * - * @param cmd command that is offering the key. - * @param index (typically zero) which key to return if there - * are multiple on offer. - * @param[out] priv set to the key coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_merchant_priv - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_MerchantPrivateKeyP **priv) -{ - return cmd->traits (cmd->cls, - (const void **) priv, - TALER_TESTING_TRAIT_MERCHANT_PRIV, - index); -} - - -/** - * Offer private key, typically done when CMD_1 needs it to - * sign a request. - * - * @param index (typically zero) which key to return if there are - * multiple on offer. - * @param priv which object should be offered. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_merchant_priv (unsigned int index, - const struct - TALER_MerchantPrivateKeyP *priv) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_MERCHANT_PRIV, - .ptr = (const void *) priv - }; - - return ret; -} - - -/** - * Obtain a public key from a "peer". Used e.g. to obtain - * a merchant's public key to use backend's API. - * - * @param cmd command offering the key. - * @param index (typically zero) which key to return if there - * are multiple on offer. - * @param[out] pub set to the key coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_merchant_pub - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_MerchantPublicKeyP **pub) -{ - return cmd->traits (cmd->cls, - (const void **) pub, - TALER_TESTING_TRAIT_MERCHANT_PUB, - index); -} - - -/** - * Offer public key. - * - * @param index (typically zero) which key to return if there - * are multiple on offer. NOTE: if one key is offered, it - * is mandatory to set this as zero. - * @param pub which object should be returned. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_merchant_pub (unsigned int index, - const struct - TALER_MerchantPublicKeyP *pub) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_MERCHANT_PUB, - .ptr = (const void *) pub - }; - - return ret; -} - - -/* end of testing_api_trait_merchant_key.c */ diff --git a/src/testing/testing_api_trait_number.c b/src/testing/testing_api_trait_number.c deleted file mode 100644 index ed25593ef..000000000 --- a/src/testing/testing_api_trait_number.c +++ /dev/null @@ -1,229 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_number.c - * @brief traits to offer numbers - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_UINT "uint" -#define TALER_TESTING_TRAIT_UINT32 "uint-32" -#define TALER_TESTING_TRAIT_UINT64 "uint-64" -#define TALER_TESTING_TRAIT_INT64 "int-64" -#define TALER_TESTING_TRAIT_BANK_ROW "bank-transaction-row" - - -/** - * Obtain a number from @a cmd. - * - * @param cmd command to extract the number from. - * @param index the number's index number. - * @param[out] n set to the number coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_uint (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const unsigned int **n) -{ - return cmd->traits (cmd->cls, - (const void **) n, - TALER_TESTING_TRAIT_UINT, - index); -} - - -/** - * Offer a number. - * - * @param index the number's index number. - * @param n the number to offer. - * @return #GNUNET_OK on success. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_uint (unsigned int index, - const unsigned int *n) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_UINT, - .ptr = (const void *) n - }; - return ret; -} - - -/** - * Obtain a "number" value from @a cmd, 32-bit version. - * - * @param cmd command to extract the number from. - * @param index the number's index number. - * @param[out] n set to the number coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_uint32 (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const uint32_t **n) -{ - return cmd->traits (cmd->cls, - (const void **) n, - TALER_TESTING_TRAIT_UINT32, - index); -} - - -/** - * Offer number trait, 32-bit version. - * - * @param index the number's index number. - * @param n number to offer. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_uint32 (unsigned int index, - const uint32_t *n) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_UINT32, - .ptr = (const void *) n - }; - return ret; -} - - -/** - * Obtain a "number" value from @a cmd, 64-bit version. - * - * @param cmd command to extract the number from. - * @param index the number's index number. - * @param[out] n set to the number coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_uint64 (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const uint64_t **n) -{ - return cmd->traits (cmd->cls, - (const void **) n, - TALER_TESTING_TRAIT_UINT64, - index); -} - - -/** - * Offer number trait, 64-bit version. - * - * @param index the number's index number. - * @param n number to offer. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_uint64 (unsigned int index, - const uint64_t *n) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_UINT64, - .ptr = (const void *) n - }; - return ret; -} - - -/** - * Obtain a "number" value from @a cmd, 64-bit signed version. - * - * @param cmd command to extract the number from. - * @param index the number's index number. - * @param[out] n set to the number coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_int64 (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const int64_t **n) -{ - return cmd->traits (cmd->cls, - (const void **) n, - TALER_TESTING_TRAIT_INT64, - index); -} - - -/** - * Offer number trait, 64-bit signed version. - * - * @param index the number's index number. - * @param n number to offer. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_int64 (unsigned int index, - const int64_t *n) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_INT64, - .ptr = (const void *) n - }; - return ret; -} - - -/** - * Obtain a bank transaction row value from @a cmd. - * - * @param cmd command to extract the number from. - * @param[out] row set to the number coming from @a cmd. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_bank_row (const struct TALER_TESTING_Command *cmd, - const uint64_t **row) -{ - return cmd->traits (cmd->cls, - (const void **) row, - TALER_TESTING_TRAIT_BANK_ROW, - 0); -} - - -/** - * Offer bank transaction row trait. - * - * @param row number to offer. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_bank_row (const uint64_t *row) -{ - struct TALER_TESTING_Trait ret = { - .index = 0, - .trait_name = TALER_TESTING_TRAIT_BANK_ROW, - .ptr = (const void *) row - }; - return ret; -} - - -/* end of testing_api_trait_number.c */ diff --git a/src/testing/testing_api_trait_process.c b/src/testing/testing_api_trait_process.c deleted file mode 100644 index 3d2af31fd..000000000 --- a/src/testing/testing_api_trait_process.c +++ /dev/null @@ -1,82 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_process.c - * @brief trait offering process handles. - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_PROCESS "process" - - -/** - * Obtain location where a command stores a pointer to a process. - * - * @param cmd command to extract trait from. - * @param index which process to pick if @a cmd - * has multiple on offer. - * @param[out] processp set to the address of the pointer to the - * process. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_process - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - struct GNUNET_OS_Process ***processp) -{ - return cmd->traits (cmd->cls, - (const void **) processp, - TALER_TESTING_TRAIT_PROCESS, - index); -} - - -/** - * Offer location where a command stores a pointer to a process. - * - * @param index offered location index number, in case there are - * multiple on offer. - * @param processp process location to offer. - * - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_process - (unsigned int index, - struct GNUNET_OS_Process **processp) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_PROCESS, - .ptr = (const void *) processp - }; - - return ret; -} - - -/* end of testing_api_trait_process.c */ diff --git a/src/testing/testing_api_trait_reserve_history.c b/src/testing/testing_api_trait_reserve_history.c deleted file mode 100644 index b458dbd66..000000000 --- a/src/testing/testing_api_trait_reserve_history.c +++ /dev/null @@ -1,76 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_reserve_history.c - * @brief implements reserve hostry trait - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_RESERVE_HISTORY \ - "reserve-history-entry" - - -/** - * Obtain a reserve history entry from a @a cmd. - * - * @param cmd command to extract the reserve history from. - * @param index reserve history's index number. - * @param[out] rhp set to the reserve history. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_reserve_history ( - const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_EXCHANGE_ReserveHistory **rhp) -{ - return cmd->traits (cmd->cls, - (const void **) rhp, - TALER_TESTING_TRAIT_RESERVE_HISTORY, - index); -} - - -/** - * Offer a reserve history entry. - * - * @param index reserve pubs's index number. - * @param rh reserve history entry to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_reserve_history ( - unsigned int index, - const struct TALER_EXCHANGE_ReserveHistory *rh) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_RESERVE_HISTORY, - .ptr = (const void *) rh - }; - return ret; -} - - -/* end of testing_api_trait_reserve_history.c */ diff --git a/src/testing/testing_api_trait_reserve_priv.c b/src/testing/testing_api_trait_reserve_priv.c deleted file mode 100644 index f4a4ef500..000000000 --- a/src/testing/testing_api_trait_reserve_priv.c +++ /dev/null @@ -1,76 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_reserve_priv.c - * @brief implements reserve private key trait - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY \ - "reserve-private-key" - -/** - * Obtain a reserve private key from a @a cmd. - * - * @param cmd command to extract the reserve priv from. - * @param index reserve priv's index number. - * @param[out] reserve_priv set to the reserve priv. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_reserve_priv - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_ReservePrivateKeyP **reserve_priv) -{ - return cmd->traits (cmd->cls, - (const void **) reserve_priv, - TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY, - index); -} - - -/** - * Offer a reserve private key. - * - * @param index reserve priv's index number. - * @param reserve_priv reserve private key to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_reserve_priv - (unsigned int index, - const struct TALER_ReservePrivateKeyP *reserve_priv) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY, - .ptr = (const void *) reserve_priv - }; - return ret; -} - - -/* end of testing_api_trait_reserve_priv.c */ diff --git a/src/testing/testing_api_trait_reserve_pub.c b/src/testing/testing_api_trait_reserve_pub.c deleted file mode 100644 index 743a10e96..000000000 --- a/src/testing/testing_api_trait_reserve_pub.c +++ /dev/null @@ -1,78 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_reserve_pub.c - * @brief implements reserve public key trait - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY \ - "reserve-public-key" - -/** - * Obtain a reserve public key from a @a cmd. - * - * @param cmd command to extract the reserve pub from. - * @param index reserve pub's index number. - * @param[out] reserve_pub set to the reserve pub. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_reserve_pub - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_ReservePublicKeyP **reserve_pub) -{ - if (NULL == cmd->traits) - return GNUNET_SYSERR; - return cmd->traits (cmd->cls, - (const void **) reserve_pub, - TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY, - index); -} - - -/** - * Offer a reserve public key. - * - * @param index reserve pub's index number. - * @param reserve_pub reserve public key to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_reserve_pub - (unsigned int index, - const struct TALER_ReservePublicKeyP *reserve_pub) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY, - .ptr = (const void *) reserve_pub - }; - return ret; -} - - -/* end of testing_api_trait_reserve_pub.c */ diff --git a/src/testing/testing_api_trait_string.c b/src/testing/testing_api_trait_string.c deleted file mode 100644 index b102265e8..000000000 --- a/src/testing/testing_api_trait_string.c +++ /dev/null @@ -1,231 +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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_string.c - * @brief offers strings traits. - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -/** - * Some string. Avoid, use something more precise! - */ -#define TALER_TESTING_TRAIT_STRING "string" - -/** - * An HTTP-URL. - */ -#define TALER_TESTING_TRAIT_URL "url" - -/** - * A PAYTO-URL. - */ -#define TALER_TESTING_TRAIT_PAYTO "payto" - -/** - * String identifying an order. - */ -#define TALER_TESTING_TRAIT_ORDER_ID "order-id" - - -/** - * Obtain a string from @a cmd. - * - * @param cmd command to extract the subject from. - * @param index index number associated with the transfer - * subject to offer. - * @param[out] s where to write the offered - * string - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_string (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const char **s) -{ - return cmd->traits (cmd->cls, - (const void **) s, - TALER_TESTING_TRAIT_STRING, - index); -} - - -/** - * Offer string. - * - * @param index index number associated with the transfer - * subject being offered. - * @param s transfer subject to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_string (unsigned int index, - const char *s) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_STRING, - .ptr = (const void *) s - }; - return ret; -} - - -/** - * Obtain a HTTP url from @a cmd. - * - * @param cmd command to extract the url from. - * @param index which url is to be picked, in case - * multiple are offered. - * @param[out] url where to write the url. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_url (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const char **url) -{ - return cmd->traits (cmd->cls, - (const void **) url, - TALER_TESTING_TRAIT_URL, - index); -} - - -/** - * Offer HTTP url in a trait. - * - * @param index which url is to be picked, - * in case multiple are offered. - * @param url the url to offer. - * - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_url (unsigned int index, - const char *url) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_URL, - .ptr = (const void *) url - }; - - GNUNET_assert ( (NULL == url) || - (0 != strncasecmp (url, - "payto://", - strlen ("payto://"))) ); - return ret; -} - - -/** - * Obtain a order id from @a cmd. - * - * @param cmd command to extract the order id from. - * @param index which order id is to be picked, in case - * multiple are offered. - * @param[out] order_id where to write the order id. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_order_id (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const char **order_id) -{ - return cmd->traits (cmd->cls, - (const void **) order_id, - TALER_TESTING_TRAIT_ORDER_ID, - index); -} - - -/** - * Offer order id in a trait. - * - * @param index which order id is to be offered, - * in case multiple are offered. - * @param order_id the order id to offer. - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_order_id (unsigned int index, - const char *order_id) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_ORDER_ID, - .ptr = (const void *) order_id - }; - return ret; -} - - -/** - * Obtain a PAYTO-url from @a cmd. - * - * @param cmd command to extract the url from. - * @param pt which url is to be picked, in case - * multiple are offered. - * @param[out] url where to write the url. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_payto (const struct TALER_TESTING_Command *cmd, - enum TALER_TESTING_PaytoType pt, - const char **url) -{ - return cmd->traits (cmd->cls, - (const void **) url, - TALER_TESTING_TRAIT_PAYTO, - (unsigned int) pt); -} - - -/** - * Offer a "payto" URL reference. - * - * @param pt which reference is to be offered, - * in case multiple are offered. - * @param payto_uri the payto URI - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_payto (enum TALER_TESTING_PaytoType pt, - const char *payto_uri) -{ - struct TALER_TESTING_Trait ret = { - .index = (unsigned int) pt, - .trait_name = TALER_TESTING_TRAIT_PAYTO, - .ptr = (const void *) payto_uri, - }; - - GNUNET_assert (0 == strncasecmp (payto_uri, - "payto://", - strlen ("payto://"))); - return ret; -} - - -/* end of testing_api_trait_string.c */ diff --git a/src/testing/testing_api_trait_time.c b/src/testing/testing_api_trait_time.c deleted file mode 100644 index 207246bed..000000000 --- a/src/testing/testing_api_trait_time.c +++ /dev/null @@ -1,120 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_time.c - * @brief traits to offer time stamps. - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_TIME_ABS "time-abs" -#define TALER_TESTING_TRAIT_TIME_REL "time-rel" - -/** - * Obtain a absolute time from @a cmd. - * - * @param cmd command to extract trait from - * @param index which time stamp to pick if - * @a cmd has multiple on offer. - * @param[out] time set to the wanted WTID. - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_get_trait_absolute_time - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct GNUNET_TIME_Absolute **time) -{ - return cmd->traits (cmd->cls, - (const void **) time, - TALER_TESTING_TRAIT_TIME_ABS, - index); -} - - -/** - * Offer a absolute time. - * - * @param index associate the object with this index - * @param time which object should be returned - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_absolute_time - (unsigned int index, - const struct GNUNET_TIME_Absolute *time) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_TIME_ABS, - .ptr = (const void *) time - }; - return ret; -} - - -/** - * Obtain a relative time from @a cmd. - * - * @param cmd command to extract trait from - * @param index which time to pick if - * @a cmd has multiple on offer. - * @param[out] time set to the wanted WTID. - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_get_trait_relative_time ( - const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct GNUNET_TIME_Relative **time) -{ - return cmd->traits (cmd->cls, - (const void **) time, - TALER_TESTING_TRAIT_TIME_REL, - index); -} - - -/** - * Offer a relative time. - * - * @param index associate the object with this index - * @param time which object should be returned - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_relative_time ( - unsigned int index, - const struct GNUNET_TIME_Relative *time) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_TIME_REL, - .ptr = (const void *) time - }; - return ret; -} - - -/* end of testing_api_trait_time.c */ diff --git a/src/testing/testing_api_trait_uuid.c b/src/testing/testing_api_trait_uuid.c deleted file mode 100644 index 4d5003ecd..000000000 --- a/src/testing/testing_api_trait_uuid.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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 - <http://www.gnu.org/licenses/> -*/ -/** - * @file testing/testing_api_trait_uuid.c - * @brief offer any trait that is passed over as a uuid. - * @author Jonathan Buchanan - */ -#include "platform.h" -#include "taler_signatures.h" -#include "taler_exchange_service.h" -#include "taler_testing_lib.h" - - -#define TALER_TESTING_TRAIT_UUID "uuid" -#define TALER_TESTING_TRAIT_CLAIM_TOKEN "claim_token" - - -/** - * Obtain a uuid from @a cmd. - * - * @param cmd command to extract the uuid from. - * @param index which amount to pick if @a cmd has multiple - * on offer - * @param[out] uuid where to write the uuid. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_uuid (const struct TALER_TESTING_Command *cmd, - unsigned int index, - struct GNUNET_Uuid **uuid) -{ - return cmd->traits (cmd->cls, - (const void **) uuid, - TALER_TESTING_TRAIT_UUID, - index); -} - - -/** - * Offer a uuid in a trait. - * - * @param index which uuid to offer, in case there are - * multiple available. - * @param uuid the uuid to offer. - * - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_uuid (unsigned int index, - const struct GNUNET_Uuid *uuid) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_UUID, - .ptr = (const void *) uuid - }; - return ret; -} - - -/** - * Obtain a claim token from @a cmd. - * - * @param cmd command to extract the token from. - * @param index which amount to pick if @a cmd has multiple - * on offer - * @param[out] ct where to write the token. - * @return #GNUNET_OK on success. - */ -int -TALER_TESTING_get_trait_claim_token (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_ClaimTokenP **ct) -{ - return cmd->traits (cmd->cls, - (const void **) ct, - TALER_TESTING_TRAIT_CLAIM_TOKEN, - index); -} - - -/** - * Offer a claim token in a trait. - * - * @param index which token to offer, in case there are - * multiple available. - * @param ct the token to offer. - * - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_claim_token (unsigned int index, - const struct TALER_ClaimTokenP *ct) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_CLAIM_TOKEN, - .ptr = (const void *) ct - }; - return ret; -} diff --git a/src/testing/testing_api_trait_wtid.c b/src/testing/testing_api_trait_wtid.c deleted file mode 100644 index 5c7e7060e..000000000 --- a/src/testing/testing_api_trait_wtid.c +++ /dev/null @@ -1,76 +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 - <http://www.gnu.org/licenses/> -*/ - -/** - * @file testing/testing_api_trait_number.c - * @brief traits to offer numbers - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "taler_signatures.h" -#include "taler_testing_lib.h" - -#define TALER_TESTING_TRAIT_WTID "wtid" - -/** - * Obtain a WTID value from @a cmd. - * - * @param cmd command to extract trait from - * @param index which WTID to pick if @a cmd has multiple on - * offer - * @param[out] wtid set to the wanted WTID. - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_get_trait_wtid - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const struct TALER_WireTransferIdentifierRawP **wtid) -{ - return cmd->traits (cmd->cls, - (const void **) wtid, - TALER_TESTING_TRAIT_WTID, - index); -} - - -/** - * Offer a WTID. - * - * @param index associate the object with this index - * @param wtid which object should be returned - * @return the trait. - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_wtid - (unsigned int index, - const struct TALER_WireTransferIdentifierRawP *wtid) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_WTID, - .ptr = (const void *) wtid - }; - return ret; -} - - -/* end of testing_api_trait_number.c */ diff --git a/src/testing/testing_api_traits.c b/src/testing/testing_api_traits.c index 6d623af7a..799ae6718 100644 --- a/src/testing/testing_api_traits.c +++ b/src/testing/testing_api_traits.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2018 Taler Systems SA + Copyright (C) 2018, 2021 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 @@ -29,6 +29,11 @@ #include "taler_testing_lib.h" +TALER_TESTING_SIMPLE_TRAITS (TALER_TESTING_MAKE_IMPL_SIMPLE_TRAIT) + +TALER_TESTING_INDEXED_TRAITS (TALER_TESTING_MAKE_IMPL_INDEXED_TRAIT) + + /** * End a trait array. Usually, commands offer several traits, * and put them in arrays. @@ -46,16 +51,7 @@ TALER_TESTING_trait_end () } -/** - * Pick the chosen trait from the traits array. - * - * @param traits the traits array. - * @param ret where to store the result. - * @param trait type of the trait to extract. - * @param index index number of the object to extract. - * @return #GNUNET_OK if no error occurred, #GNUNET_SYSERR otherwise. - */ -int +enum GNUNET_GenericReturnValue TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits, const void **ret, const char *trait, @@ -63,19 +59,75 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits, { for (unsigned int i = 0; NULL != traits[i].trait_name; i++) { - if ( (0 == strcmp (trait, traits[i].trait_name)) && + if ( (0 == strcmp (trait, + traits[i].trait_name)) && (index == traits[i].index) ) { *ret = (void *) traits[i].ptr; return GNUNET_OK; } } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trait %s/%u not found.\n", - trait, index); - + trait, + index); return GNUNET_SYSERR; } +const char * +TALER_TESTING_get_exchange_url (struct TALER_TESTING_Interpreter *is) +{ + const char *exchange_url; + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd + = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return NULL; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_exchange_url (exchange_cmd, + &exchange_url)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return NULL; + } + return exchange_url; +} + + +struct TALER_EXCHANGE_Keys * +TALER_TESTING_get_keys ( + struct TALER_TESTING_Interpreter *is) +{ + struct TALER_EXCHANGE_Keys *keys; + const struct TALER_TESTING_Command *exchange_cmd; + + exchange_cmd + = TALER_TESTING_interpreter_get_command (is, + "exchange"); + if (NULL == exchange_cmd) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return NULL; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_keys (exchange_cmd, + &keys)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return NULL; + } + return keys; +} + + /* end of testing_api_traits.c */ diff --git a/src/testing/testing_api_twister_helpers.c b/src/testing/testing_api_twister_helpers.c index 935d8bcc1..68fbf0082 100644 --- a/src/testing/testing_api_twister_helpers.c +++ b/src/testing/testing_api_twister_helpers.c @@ -116,7 +116,9 @@ TALER_TWISTER_run_twister (const char *config_filename) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not start the taler-twister client\n"); - GNUNET_OS_process_kill (proc, SIGTERM); + GNUNET_break (0 == + GNUNET_OS_process_kill (proc, + SIGTERM)); GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); TWISTER_FAIL (); @@ -129,7 +131,9 @@ TALER_TWISTER_run_twister (const char *config_filename) &code)) { GNUNET_OS_process_destroy (client_proc); - GNUNET_OS_process_kill (proc, SIGTERM); + GNUNET_break (0 == + GNUNET_OS_process_kill (proc, + SIGTERM)); GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); TWISTER_FAIL (); @@ -140,7 +144,9 @@ TALER_TWISTER_run_twister (const char *config_filename) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to check twister works.\n"); GNUNET_OS_process_destroy (client_proc); - GNUNET_OS_process_kill (proc, SIGTERM); + GNUNET_break (0 == + GNUNET_OS_process_kill (proc, + SIGTERM)); GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); TWISTER_FAIL (); @@ -151,7 +157,9 @@ TALER_TWISTER_run_twister (const char *config_filename) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected error running `taler-twister'!\n"); GNUNET_OS_process_destroy (client_proc); - GNUNET_OS_process_kill (proc, SIGTERM); + GNUNET_break (0 == + GNUNET_OS_process_kill (proc, + SIGTERM)); GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); TWISTER_FAIL (); diff --git a/src/testing/valgrind.h b/src/testing/valgrind.h new file mode 100644 index 000000000..eaf1632e1 --- /dev/null +++ b/src/testing/valgrind.h @@ -0,0 +1,7165 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2017 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 19 + + +#include <stdarg.h> + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_freebsd +#undef PLAT_amd64_freebsd +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_arm64_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux +#undef PLAT_nanomips_linux +#undef PLAT_x86_solaris +#undef PLAT_amd64_solaris + + +#if defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif defined(__FreeBSD__) && defined(__i386__) +# define PLAT_x86_freebsd 1 +#elif defined(__FreeBSD__) && defined(__amd64__) +# define PLAT_amd64_freebsd 1 +#elif (defined(__MINGW32__) && defined(__i386__)) \ + || defined(__CYGWIN32__) \ + || (defined(_WIN32) && defined(_M_IX86)) +# define PLAT_x86_win32 1 +#elif (defined(__MINGW32__) && defined(__x86_64__)) \ + || (defined(_WIN32) && defined(_M_X64)) +/* __MINGW32__ and _WIN32 are defined in 64 bit mode as well. */ +# define PLAT_amd64_win64 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) && !defined(__ILP32__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 +/* Big Endian uses ELF version 1 */ +# define PLAT_ppc64be_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 +/* Little Endian uses ELF version 2 */ +# define PLAT_ppc64le_linux 1 +#elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) +# define PLAT_arm_linux 1 +#elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) +# define PLAT_arm64_linux 1 +#elif defined(__linux__) && defined(__s390__) && defined(__s390x__) +# define PLAT_s390x_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips==64) +# define PLAT_mips64_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips==32) +# define PLAT_mips32_linux 1 +#elif defined(__linux__) && defined(__nanomips__) +# define PLAT_nanomips_linux 1 +#elif defined(__sun) && defined(__i386__) +# define PLAT_x86_solaris 1 +#elif defined(__sun) && defined(__x86_64__) +# define PLAT_amd64_solaris 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +/* + * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client + * request. Accepts both pointers and integers as arguments. + * + * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind + * client request that does not return a value. + + * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind + * client request and whose value equals the client request result. Accepts + * both pointers and integers as arguments. Note that such calls are not + * necessarily pure functions -- they may have side effects. + */ + +#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ + _zzq_request, _zzq_arg1, _zzq_arg2, \ + _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ + _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (_zzq_default) + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ----------------- x86-{linux,darwin,solaris} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || (defined(PLAT_x86_win32) && defined(__GNUC__)) \ + || defined(PLAT_x86_solaris) || defined(PLAT_x86_freebsd) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgl %%edi,%%edi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) + || PLAT_x86_solaris */ + +/* ------------------------- x86-Win32 ------------------------- */ + +#if defined(PLAT_x86_win32) && !defined(__GNUC__) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#if defined(_MSC_VER) + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm rol edi, 3 __asm rol edi, 13 \ + __asm rol edi, 29 __asm rol edi, 19 + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ + (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ + (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ + (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) + +static __inline uintptr_t +valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, + uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, + uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, + uintptr_t _zzq_arg5) +{ + volatile uintptr_t _zzq_args[6]; + volatile unsigned int _zzq_result; + _zzq_args[0] = (uintptr_t)(_zzq_request); + _zzq_args[1] = (uintptr_t)(_zzq_arg1); + _zzq_args[2] = (uintptr_t)(_zzq_arg2); + _zzq_args[3] = (uintptr_t)(_zzq_arg3); + _zzq_args[4] = (uintptr_t)(_zzq_arg4); + _zzq_args[5] = (uintptr_t)(_zzq_arg5); + __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default + __SPECIAL_INSTRUCTION_PREAMBLE + /* %EDX = client_request ( %EAX ) */ + __asm xchg ebx,ebx + __asm mov _zzq_result, edx + } + return _zzq_result; +} + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + __asm xchg ecx,ecx \ + __asm mov __addr, eax \ + } \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX ERROR + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm xchg edi,edi \ + } \ + } while (0) + +#else +#error Unsupported compiler. +#endif + +#endif /* PLAT_x86_win32 */ + +/* ----------------- amd64-{linux,darwin,solaris} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ + || defined(PLAT_amd64_solaris) \ + || defined(PLAT_amd64_freebsd) \ + || (defined(PLAT_amd64_win64) && defined(__GNUC__)) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgq %%rdi,%%rdi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ + +/* ------------------------- amd64-Win64 ------------------------- */ + +#if defined(PLAT_amd64_win64) && !defined(__GNUC__) + +#error Unsupported compiler. + +#endif /* PLAT_amd64_win64 */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ + "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + unsigned long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long int _zzq_args[6]; \ + unsigned long int _zzq_result; \ + unsigned long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +#if defined(PLAT_ppc64le_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + unsigned long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long int _zzq_args[6]; \ + unsigned long int _zzq_result; \ + unsigned long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R12 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr r9, r9, r9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------- */ + +#if defined(PLAT_arm64_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ + "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile("mov x3, %1\n\t" /*default*/ \ + "mov x4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = client_request ( X4 ) */ \ + "orr x10, x10, x10\n\t" \ + "mov %0, x3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" ((unsigned long int)(_zzq_default)), \ + "r" (&_zzq_args[0]) \ + : "cc","memory", "x3", "x4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = guest_NRADDR */ \ + "orr x11, x11, x11\n\t" \ + "mov %0, x3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "x3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir X8 */ \ + "orr x12, x12, x12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr x9, x9, x9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------ s390x-linux ------------------------ */ + +#if defined(PLAT_s390x_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific + * code. This detection is implemented in platform specific toIR.c + * (e.g. VEX/priv/guest_s390_decoder.c). + */ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "lr 15,15\n\t" \ + "lr 1,1\n\t" \ + "lr 2,2\n\t" \ + "lr 3,3\n\t" + +#define __CLIENT_REQUEST_CODE "lr 2,2\n\t" +#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" +#define __CALL_NO_REDIR_CODE "lr 4,4\n\t" +#define __VEX_INJECT_IR_CODE "lr 5,5\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile(/* r2 = args */ \ + "lgr 2,%1\n\t" \ + /* r3 = default */ \ + "lgr 3,%2\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CLIENT_REQUEST_CODE \ + /* results = r3 */ \ + "lgr %0, 3\n\t" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), \ + "0" ((unsigned long int)_zzq_default) \ + : "cc", "2", "3", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __GET_NR_CONTEXT_CODE \ + "lgr %0, 3\n\t" \ + : "=a" (__addr) \ + : \ + : "cc", "3", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_R1 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CALL_NO_REDIR_CODE + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __VEX_INJECT_IR_CODE); \ + } while (0) + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ---------------- */ + +#if defined(PLAT_mips32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +/* .word 0x342 + * .word 0x742 + * .word 0xC2 + * .word 0x4C2*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "srl $0, $0, 13\n\t" \ + "srl $0, $0, 29\n\t" \ + "srl $0, $0, 3\n\t" \ + "srl $0, $0, 19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* T3 = client_request ( T4 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12", "memory"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %t9 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%t9 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ---------------- */ + +#if defined(PLAT_mips64_linux) + +typedef + struct { + unsigned long nraddr; /* where's the code? */ + } + OrigFn; + +/* dsll $0,$0, 3 + * dsll $0,$0, 13 + * dsll $0,$0, 29 + * dsll $0,$0, 19*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ + "dsll $0,$0,29 ; dsll $0,$0,19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = client_request ( $12 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12", "memory"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11"); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir $25 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_mips64_linux */ + +#if defined(PLAT_nanomips_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; +/* + 8000 c04d srl zero, zero, 13 + 8000 c05d srl zero, zero, 29 + 8000 c043 srl zero, zero, 3 + 8000 c053 srl zero, zero, 19 +*/ + +#define __SPECIAL_INSTRUCTION_PREAMBLE "srl[32] $zero, $zero, 13 \n\t" \ + "srl[32] $zero, $zero, 29 \n\t" \ + "srl[32] $zero, $zero, 3 \n\t" \ + "srl[32] $zero, $zero, 19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("move $a7, %1\n\t" /* default */ \ + "move $t0, %2\n\t" /* ptr */ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* $a7 = client_request( $t0 ) */ \ + "or[32] $t0, $t0, $t0\n\t" \ + "move %0, $a7\n\t" /* result */ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$a7", "$t0", "memory"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* $a7 = guest_NRADDR */ \ + "or[32] $t1, $t1, $t1\n\t" \ + "move %0, $a7" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$a7"); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir $25 */ \ + "or[32] $t2, $t2, $t2\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or[32] $t3, $t3, $t3\n\t" \ + ); \ + } while (0) + +#endif +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts + the default behaviour equivalance class tag "0000" into the name. + See pub_tool_redir.h for details -- normally you don't need to + think about this, though. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Also provide end-user facilities for function replacement, rather + than wrapping. A replacement function differs from a wrapper in + that it has no way to get hold of the original function being + called, and hence no way to call onwards to it. In a replacement + function, VALGRIND_GET_ORIG_FN always returns zero. */ + +#define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) + +#define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ----------------- x86-{linux,darwin,solaris} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || defined(PLAT_x86_solaris) || defined(PLAT_x86_freebsd) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movl %%esp,%%edi\n\t" \ + "andl $0xfffffff0,%%esp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movl %%edi,%%esp\n\t" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin || PLAT_x86_solaris */ + +/* ---------------- amd64-{linux,darwin,solaris} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ + || defined(PLAT_amd64_solaris) || defined(PLAT_amd64_freebsd) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* This is all pretty complex. It's so as to make stack unwinding + work reliably. See bug 243270. The basic problem is the sub and + add of 128 of %rsp in all of the following macros. If gcc believes + the CFA is in %rsp, then unwinding may fail, because what's at the + CFA is not what gcc "expected" when it constructs the CFIs for the + places where the macros are instantiated. + + But we can't just add a CFI annotation to increase the CFA offset + by 128, to match the sub of 128 from %rsp, because we don't know + whether gcc has chosen %rsp as the CFA at that point, or whether it + has chosen some other register (eg, %rbp). In the latter case, + adding a CFI annotation to change the CFA offset is simply wrong. + + So the solution is to get hold of the CFA using + __builtin_dwarf_cfa(), put it in a known register, and add a + CFI annotation to say what the register is. We choose %rbp for + this (perhaps perversely), because: + + (1) %rbp is already subject to unwinding. If a new register was + chosen then the unwinder would have to unwind it in all stack + traces, which is expensive, and + + (2) %rbp is already subject to precise exception updates in the + JIT. If a new register was chosen, we'd have to have precise + exceptions for it too, which reduces performance of the + generated code. + + However .. one extra complication. We can't just whack the result + of __builtin_dwarf_cfa() into %rbp and then add %rbp to the + list of trashed registers at the end of the inline assembly + fragments; gcc won't allow %rbp to appear in that list. Hence + instead we need to stash %rbp in %r15 for the duration of the asm, + and say that %r15 is trashed instead. gcc seems happy to go with + that. + + Oh .. and this all needs to be conditionalised so that it is + unchanged from before this commit, when compiled with older gccs + that don't support __builtin_dwarf_cfa. Furthermore, since + this header file is freestanding, it has to be independent of + config.h, and so the following conditionalisation cannot depend on + configure time checks. + + Although it's not clear from + 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', + this expression excludes Darwin. + .cfi directives in Darwin assembly appear to be completely + different and I haven't investigated how they work. + + For even more entertainment value, note we have to use the + completely undocumented __builtin_dwarf_cfa(), which appears to + really compute the CFA, whereas __builtin_frame_address(0) claims + to but actually doesn't. See + https://bugs.kde.org/show_bug.cgi?id=243270#c47 +*/ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"r"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + "movq %%rbp, %%r15\n\t" \ + "movq %2, %%rbp\n\t" \ + ".cfi_remember_state\n\t" \ + ".cfi_def_cfa rbp, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "movq %%r15, %%rbp\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movq %%rsp,%%r14\n\t" \ + "andq $0xfffffffffffffff0,%%rsp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movq %%r14,%%rsp\n\t" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rlwinm 1,1,0,0,27\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +/* ------------------------- ppc64le-linux ----------------------- */ +#if defined(PLAT_ppc64le_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(12)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4", "r12", "r14" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +/* This is a bit tricky. We store the original stack pointer in r10 + as it is callee-saves. gcc doesn't allow the use of r11 for some + reason. Also, we can't directly "bic" the stack pointer in thumb + mode since r13 isn't an allowed register number in that context. + So use r4 as a temporary, since that is about to get trashed + anyway, just after each use of this macro. Side effect is we need + to be very careful about any future changes, since + VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ +#define VALGRIND_ALIGN_STACK \ + "mov r10, sp\n\t" \ + "mov r4, sp\n\t" \ + "bic r4, r4, #7\n\t" \ + "mov sp, r4\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, r10\n\t" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------ */ + +#if defined(PLAT_arm64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ + "x18", "x19", "x20", "x30", \ + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ + "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ + "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ + "v26", "v27", "v28", "v29", "v30", "v31" + +/* x21 is callee-saved, so we can use it to save and restore SP around + the hidden call. */ +#define VALGRIND_ALIGN_STACK \ + "mov x21, sp\n\t" \ + "bic sp, x21, #15\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, x21\n\t" + +/* These CALL_FN_ macros assume that on arm64-linux, + sizeof(unsigned long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11, \ + arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1, #96] \n\t" \ + "str x8, [sp, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------- s390x-linux ------------------------- */ + +#if defined(PLAT_s390x_linux) + +/* Similar workaround as amd64 (see above), but we use r11 as frame + pointer and save the old r11 in r7. r11 might be used for + argvec, therefore we copy argvec in r1 since r1 is clobbered + after the call anyway. */ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"d"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + ".cfi_remember_state\n\t" \ + "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ + "lgr 7,11\n\t" \ + "lgr 11,%2\n\t" \ + ".cfi_def_cfa r11, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "lgr 11, 7\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE \ + "lgr 1,%1\n\t" +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Nb: On s390 the stack pointer is properly aligned *at all times* + according to the s390 GCC maintainer. (The ABI specification is not + precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and + VALGRIND_RESTORE_STACK are not defined here. */ + +/* These regs are trashed by the hidden call. Note that we overwrite + r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the + function a proper return address. All others are ABI defined call + clobbers. */ +#if defined(__VX__) || defined(__S390_VX__) +#define __CALLER_SAVED_REGS "0", "1", "2", "3", "4", "5", "14", \ + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", \ + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", \ + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", \ + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" +#else +#define __CALLER_SAVED_REGS "0", "1", "2", "3", "4", "5", "14", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" +#endif + +/* Nb: Although r11 is modified in the asm snippets below (inside + VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for + two reasons: + (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not + modified + (2) GCC will complain that r11 cannot appear inside a clobber section, + when compiled with -O -fno-omit-frame-pointer + */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 1, 0(1)\n\t" /* target->r1 */ \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +/* The call abi has the arguments in r2-r6 and stack */ +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1, arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-168\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,168\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-176\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,176\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-184\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,184\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-192\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,192\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-200\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,200\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-208\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,208\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-216\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "mvc 208(8,15), 96(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "aghi 15,216\n\t" \ + VALGRIND_CFI_EPILOGUE \ + "lgr %0, 2\n\t" \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ----------------------- */ + +#if defined(PLAT_mips32_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16\n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" /* arg1*/ \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 24\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 24 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "nop\n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 56\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 48(%1) \n\t" \ + "sw $4, 44($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 56 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- nanomips-linux -------------------- */ + +#if defined(PLAT_nanomips_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$t4", "$t5", "$a0", "$a1", "$a2", \ +"$a3", "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3", \ +"$t8","$t9", "$at" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + "lw $a2,12(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + "lw $a2,12(%1)\n\t" \ + "lw $a3,16(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + "lw $a2,12(%1)\n\t" \ + "lw $a3,16(%1)\n\t" \ + "lw $a4,20(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + "lw $a2,12(%1)\n\t" \ + "lw $a3,16(%1)\n\t" \ + "lw $a4,20(%1)\n\t" \ + "lw $a5,24(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + "lw $a2,12(%1)\n\t" \ + "lw $a3,16(%1)\n\t" \ + "lw $a4,20(%1)\n\t" \ + "lw $a5,24(%1)\n\t" \ + "lw $a6,28(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "lw $t9, 0(%1)\n\t" \ + "lw $a0, 4(%1)\n\t" \ + "lw $a1, 8(%1)\n\t" \ + "lw $a2,12(%1)\n\t" \ + "lw $a3,16(%1)\n\t" \ + "lw $a4,20(%1)\n\t" \ + "lw $a5,24(%1)\n\t" \ + "lw $a6,28(%1)\n\t" \ + "lw $a7,32(%1)\n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "addiu $sp, $sp, -16 \n\t" \ + "lw $t9,36(%1) \n\t" \ + "sw $t9, 0($sp) \n\t" \ + "lw $t9, 0(%1) \n\t" \ + "lw $a0, 4(%1) \n\t" \ + "lw $a1, 8(%1) \n\t" \ + "lw $a2,12(%1) \n\t" \ + "lw $a3,16(%1) \n\t" \ + "lw $a4,20(%1) \n\t" \ + "lw $a5,24(%1) \n\t" \ + "lw $a6,28(%1) \n\t" \ + "lw $a7,32(%1) \n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0 \n\t" \ + "addiu $sp, $sp, 16 \n\t" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "addiu $sp, $sp, -16 \n\t" \ + "lw $t9,36(%1) \n\t" \ + "sw $t9, 0($sp) \n\t" \ + "lw $t9,40(%1) \n\t" \ + "sw $t9, 4($sp) \n\t" \ + "lw $t9, 0(%1) \n\t" \ + "lw $a0, 4(%1) \n\t" \ + "lw $a1, 8(%1) \n\t" \ + "lw $a2,12(%1) \n\t" \ + "lw $a3,16(%1) \n\t" \ + "lw $a4,20(%1) \n\t" \ + "lw $a5,24(%1) \n\t" \ + "lw $a6,28(%1) \n\t" \ + "lw $a7,32(%1) \n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0 \n\t" \ + "addiu $sp, $sp, 16 \n\t" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "addiu $sp, $sp, -16 \n\t" \ + "lw $t9,36(%1) \n\t" \ + "sw $t9, 0($sp) \n\t" \ + "lw $t9,40(%1) \n\t" \ + "sw $t9, 4($sp) \n\t" \ + "lw $t9,44(%1) \n\t" \ + "sw $t9, 8($sp) \n\t" \ + "lw $t9, 0(%1) \n\t" \ + "lw $a0, 4(%1) \n\t" \ + "lw $a1, 8(%1) \n\t" \ + "lw $a2,12(%1) \n\t" \ + "lw $a3,16(%1) \n\t" \ + "lw $a4,20(%1) \n\t" \ + "lw $a5,24(%1) \n\t" \ + "lw $a6,28(%1) \n\t" \ + "lw $a7,32(%1) \n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0 \n\t" \ + "addiu $sp, $sp, 16 \n\t" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "addiu $sp, $sp, -16 \n\t" \ + "lw $t9,36(%1) \n\t" \ + "sw $t9, 0($sp) \n\t" \ + "lw $t9,40(%1) \n\t" \ + "sw $t9, 4($sp) \n\t" \ + "lw $t9,44(%1) \n\t" \ + "sw $t9, 8($sp) \n\t" \ + "lw $t9,48(%1) \n\t" \ + "sw $t9,12($sp) \n\t" \ + "lw $t9, 0(%1) \n\t" \ + "lw $a0, 4(%1) \n\t" \ + "lw $a1, 8(%1) \n\t" \ + "lw $a2,12(%1) \n\t" \ + "lw $a3,16(%1) \n\t" \ + "lw $a4,20(%1) \n\t" \ + "lw $a5,24(%1) \n\t" \ + "lw $a6,28(%1) \n\t" \ + "lw $a7,32(%1) \n\t" \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $a0 \n\t" \ + "addiu $sp, $sp, 16 \n\t" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_nanomips_linux */ + +/* ------------------------- mips64-linux ------------------------- */ + +#if defined(PLAT_mips64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips64-linux, + sizeof(long long) == 8. */ + +#define MIPS64_LONG2REG_CAST(x) ((long long)(long)x) + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[1]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + __asm__ volatile( \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[2]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" /* arg1*/ \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[3]; \ + volatile unsigned long long _res; \ + _argvec[0] = _orig.nraddr; \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[4]; \ + volatile unsigned long long _res; \ + _argvec[0] = _orig.nraddr; \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[5]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[6]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[7]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[8]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[9]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[10]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + __asm__ volatile( \ + "dsubu $29, $29, 8\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 8\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[11]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ + __asm__ volatile( \ + "dsubu $29, $29, 16\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 16\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[12]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ + _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ + __asm__ volatile( \ + "dsubu $29, $29, 24\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 24\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long long _argvec[13]; \ + volatile unsigned long long _res; \ + _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ + _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ + _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ + _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ + _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ + _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ + _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ + _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ + _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ + _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ + _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ + _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ + _argvec[12] = MIPS64_LONG2REG_CAST(arg12); \ + __asm__ volatile( \ + "dsubu $29, $29, 32\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 96(%1)\n\t" \ + "sd $4, 24($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 32\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) (long)_res; \ + } while (0) + +#endif /* PLAT_mips64_linux */ + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE NUMERIC VALUES OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end of the most + relevant group. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* Allows the client program and/or gdbserver to execute a monitor + command. */ + VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, + + /* Allows the client program to change a dynamic command line + option. */ + VG_USERREQ__CLO_CHANGE = 0x1203, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, + + /* Querying of debug info. */ + VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, + + /* Disable/enable error reporting level. Takes a single + Word arg which is the delta to this thread's error + disablement indicator. Hence 1 disables or further + disables errors, and -1 moves back towards enablement. + Other values are not allowed. */ + VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, + + /* Some requests used for Valgrind internal, such as + self-test or self-hosting. */ + /* Initialise IR injection */ + VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901, + /* Used by Inner Valgrind to inform Outer Valgrind where to + find the list of inner guest threads */ + VG_USERREQ__INNER_THREADS = 0x1902 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0) \ + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0) + +#define VALGRIND_INNER_THREADS(_qzz_addr) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__INNER_THREADS, \ + _qzz_addr, 0, 0, 0, 0) + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**<pid>** " part at the + start and the backtrace (if present). */ + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF(const char *format, ...) +{ +#if defined(NVALGRIND) + (void)format; + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ +#if defined(NVALGRIND) + (void)format; + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitrary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For + Memcheck, it does four things: + + - It records that the size of a block has been changed. This assumes that + the block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - If the block shrunk, it marks the freed memory as being unaddressable. + + - If the block grew, it marks the new area as undefined and defines a red + zone past the end of the new block. + + - The V-bits of the overlap between the old and the new block are preserved. + + VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block + and before deallocation of the old block. + + In many cases, these three client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ + addr, oldSizeB, newSizeB, rzB, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0) + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0) + +/* Create a memory pool with some flags specifying extended behaviour. + When flags is zero, the behaviour is identical to VALGRIND_CREATE_MEMPOOL. + + The flag VALGRIND_MEMPOOL_METAPOOL specifies that the pieces of memory + associated with the pool using VALGRIND_MEMPOOL_ALLOC will be used + by the application as superblocks to dole out MALLOC_LIKE blocks using + VALGRIND_MALLOCLIKE_BLOCK. In other words, a meta pool is a "2 levels" + pool : first level is the blocks described by VALGRIND_MEMPOOL_ALLOC. + The second level blocks are described using VALGRIND_MALLOCLIKE_BLOCK. + Note that the association between the pool and the second level blocks + is implicit : second level blocks will be located inside first level + blocks. It is necessary to use the VALGRIND_MEMPOOL_METAPOOL flag + for such 2 levels pools, as otherwise valgrind will detect overlapping + memory blocks, and will abort execution (e.g. during leak search). + + Such a meta pool can also be marked as an 'auto free' pool using the flag + VALGRIND_MEMPOOL_AUTO_FREE, which must be OR-ed together with the + VALGRIND_MEMPOOL_METAPOOL. For an 'auto free' pool, VALGRIND_MEMPOOL_FREE + will automatically free the second level blocks that are contained + inside the first level block freed with VALGRIND_MEMPOOL_FREE. + In other words, calling VALGRIND_MEMPOOL_FREE will cause implicit calls + to VALGRIND_FREELIKE_BLOCK for all the second level blocks included + in the first level block. + Note: it is an error to use the VALGRIND_MEMPOOL_AUTO_FREE flag + without the VALGRIND_MEMPOOL_METAPOOL flag. +*/ +#define VALGRIND_MEMPOOL_AUTO_FREE 1 +#define VALGRIND_MEMPOOL_METAPOOL 2 +#define VALGRIND_CREATE_MEMPOOL_EXT(pool, rzB, is_zeroed, flags) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, flags, 0) + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0) + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0) + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0) + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0) + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0) + +/* Mark a piece of memory as being a stack. Returns a stack id. + start is the lowest addressable stack byte, end is the highest + addressable stack byte. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0) + +/* Change the start and end address of the stack id. + start is the new lowest addressable stack byte, end is the new highest + addressable stack byte. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0) + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0) + +/* Map a code address to a source file name and line number. buf64 + must point to a 64-byte buffer in the caller's address space. The + result will be dumped in there and is guaranteed to be zero + terminated. If no info is found, the first byte is set to zero. */ +#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MAP_IP_TO_SRCLOC, \ + addr, buf64, 0, 0, 0) + +/* Disable error reporting for this thread. Behaves in a stack like + way, so you can safely call this multiple times provided that + VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times + to re-enable reporting. The first call of this macro disables + reporting. Subsequent calls have no effect except to increase the + number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable + reporting. Child threads do not inherit this setting from their + parents -- they are always created with reporting enabled. */ +#define VALGRIND_DISABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + 1, 0, 0, 0, 0) + +/* Re-enable error reporting, as per comments on + VALGRIND_DISABLE_ERROR_REPORTING. */ +#define VALGRIND_ENABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + -1, 0, 0, 0, 0) + +/* Execute a monitor command from the client program. + If a connection is opened with GDB, the output will be sent + according to the output mode set for vgdb. + If no connection is opened, output will go to the log output. + Returns 1 if command not recognised, 0 otherwise. */ +#define VALGRIND_MONITOR_COMMAND(command) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ + command, 0, 0, 0, 0) + + +/* Change the value of a dynamic command line option. + Note that unknown or not dynamically changeable options + will cause a warning message to be output. */ +#define VALGRIND_CLO_CHANGE(option) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CLO_CHANGE, \ + option, 0, 0, 0, 0) + + +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux +#undef PLAT_nanomips_linux +#undef PLAT_x86_solaris +#undef PLAT_amd64_solaris + +#endif /* __VALGRIND_H */ |