diff options
| author | Marcello Stanisci <stanisci.m@gmail.com> | 2018-01-23 10:28:24 +0100 | 
|---|---|---|
| committer | Marcello Stanisci <stanisci.m@gmail.com> | 2018-02-12 16:12:07 +0100 | 
| commit | fe6960cce854cd4c665a27c4368e4397c8e7bcfb (patch) | |
| tree | 162a95572079b3e7a014cb845ee028329a06f5f8 | |
| parent | b198bb3867b6a15c65a8860af12d7a634de906a0 (diff) | |
Implement new traits-based tests.
38 files changed, 7429 insertions, 454 deletions
| @@ -36,6 +36,7 @@ src/auditor/taler-auditor  src/auditor/taler-auditor-sign  src/bank-lib/test_bank_api  src/bank-lib/test_bank_api_with_fakebank +src/exchange-lib/test_exchange_api_new  src/exchange-lib/test_exchange_api  src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/live-keys/  src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/wirefees/ @@ -86,6 +87,7 @@ doc/manual/manual.vr  contrib/taler-exchange.tag  doxygen-doc/  src/exchange-lib/test_exchange_api_keys_cherry_picking +src/exchange-lib/test_exchange_api_keys_cherry_picking_new  src/auditor/taler-wire-auditor  contrib/auditor-report.aux  contrib/auditor-report.log diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am index 64a5f964..cf80a1d4 100644 --- a/src/exchange-lib/Makefile.am +++ b/src/exchange-lib/Makefile.am @@ -34,14 +34,26 @@ libtalerexchange_la_LIBADD = \    -ljansson \    $(XLIB) -  libtalertesting_la_LDFLAGS = \    -version-info 0:0:0 \    -no-undefined  libtalertesting_la_SOURCES = \ +  testing_api_cmd_exec_aggregator.c \    testing_api_cmd_exec_wirewatch.c \ +  testing_api_cmd_exec_keyup.c \ +  testing_api_cmd_exec_auditor-sign.c \    testing_api_cmd_fakebank_transfer.c \    testing_api_cmd_withdraw.c \ +  testing_api_cmd_wire.c \ +  testing_api_cmd_refund.c \ +  testing_api_cmd_status.c \ +  testing_api_cmd_deposit.c \ +  testing_api_cmd_refresh.c \ +  testing_api_cmd_track.c \ +  testing_api_cmd_bank_check.c \ +  testing_api_cmd_payback.c \ +  testing_api_cmd_signal.c \ +  testing_api_cmd_check_keys.c \    testing_api_helpers.c \    testing_api_loop.c \    testing_api_traits.c \ @@ -50,7 +62,12 @@ libtalertesting_la_SOURCES = \    testing_api_trait_denom_pub.c \    testing_api_trait_denom_sig.c \    testing_api_trait_process.c \ -  testing_api_trait_reserve_priv.c +  testing_api_trait_reserve_priv.c \ +  testing_api_trait_number.c \ +  testing_api_trait_fresh_coin.c \ +  testing_api_trait_string.c \ +  testing_api_trait_key_peer.c \ +  testing_api_trait_wtid.c  libtalertesting_la_LIBADD = \    $(top_builddir)/src/json/libtalerjson.la \ @@ -71,13 +88,17 @@ endif  endif  check_PROGRAMS = \ +  test_exchange_api_keys_cherry_picking_new \ +  test_exchange_api_new \    test_exchange_api \ -  test_exchange_api_keys_cherry_picking \ -  test_exchange_api_new +  test_exchange_api_keys_cherry_picking  AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; +# FIXME: uncomment those.  TESTS = \ +  test_exchange_api_keys_cherry_picking_new \ +  test_exchange_api_new \    test_exchange_api \    test_exchange_api_keys_cherry_picking @@ -108,6 +129,19 @@ test_exchange_api_new_LDADD = \    -lgnunetutil \    -ljansson +test_exchange_api_keys_cherry_picking_new_SOURCES = \ +  test_exchange_api_keys_cherry_picking_new.c +test_exchange_api_keys_cherry_picking_new_LDADD = \ +  libtalertesting.la \ +  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 +  test_exchange_api_keys_cherry_picking_SOURCES = \    test_exchange_api_keys_cherry_picking.c  test_exchange_api_keys_cherry_picking_LDADD = \ diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf index 2b94dba6..b2d2fbc5 100644 --- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf +++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf @@ -1,7 +1,7 @@  # This file is in the public domain.  #  [PATHS] -# Persistant data storage for the testcase +# Persistent data storage for the testcase  TALER_TEST_HOME = test_exchange_api_home/  [taler] diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c new file mode 100644 index 00000000..c32d6424 --- /dev/null +++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c @@ -0,0 +1,135 @@ +/* +  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 exchange-lib/test_exchange_api_keys_cherry_picking_new.c + * @brief testcase to test exchange's /keys cherry picking ability + * @author Marcello Stanisci + * @author Christian Grothoff + */ + +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include <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_exchange_api_keys_cherry_picking.conf" + +/** + * Used to increase the number of denomination keys. + */ +#define CONFIG_FILE_EXTENDED \ +  "test_exchange_api_keys_cherry_picking_extended.conf" + + +/** + * Main function that will tell the interpreter what commands to + * run. + * + * @param cls closure + */ +static void +run (void *cls, +     struct TALER_TESTING_Interpreter *is) +{ +  struct TALER_TESTING_Command commands[] = { + +    /* Send signal to the exchange to see if it reacts */ +    TALER_TESTING_cmd_signal ("signal-reaction-1", +                              is->exchanged, +                              SIGUSR1), + +    TALER_TESTING_cmd_check_keys ("check-keys-1", +                                  1, 4, +                                  is->exchange), + +    TALER_TESTING_cmd_exec_keyup ("keyup-2", /* 1st keyup happens at start-up */ +                                  CONFIG_FILE_EXTENDED), + +    TALER_TESTING_cmd_exec_auditor_sign ("sign-keys-1", +                                         CONFIG_FILE), + +    TALER_TESTING_cmd_signal ("trigger-keys-reload-1", +                              is->exchanged, +                              SIGUSR1), + +    TALER_TESTING_cmd_check_keys ("check-keys-2", +                                  2, 8, +                                  is->exchange), + + +    /** +     * End the suite.  Fixme: better to have a label for this +     * too, as it shows "(null)" in logs. +     */ +    TALER_TESTING_cmd_end () +  }; + +  TALER_TESTING_run (is, commands); +} + +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-new", +                    "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)) +  { +  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; +  } +  return 0; +} + +/* end of test_exchange_api_keys_cherry_picking_new.c */ diff --git a/src/exchange-lib/test_exchange_api_new.c b/src/exchange-lib/test_exchange_api_new.c index f9d60249..580c4e97 100644 --- a/src/exchange-lib/test_exchange_api_new.c +++ b/src/exchange-lib/test_exchange_api_new.c @@ -2,23 +2,29 @@    This file is part of TALER    Copyright (C) 2014-2018 Taler Systems SA -  TALER is free software; you can redistribute it and/or modify it under the -  terms of the GNU General Public License as published by the Free Software -  Foundation; either version 3, or (at your option) any later version. +  TALER is 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. +  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/> +  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 exchange/test_exchange_api_new.c   * @brief testcase to test exchange's HTTP API interface   * @author Sree Harsha Totakura <sreeharsha@totakura.in>   * @author Christian Grothoff + * @author Marcello Stanisci   */ +  #include "platform.h"  #include "taler_util.h"  #include "taler_signatures.h" @@ -37,10 +43,17 @@  #define CONFIG_FILE "test_exchange_api.conf"  /** - * URL of the fakebank.  Obtained from CONFIG_FILE's "exchange-wire-test:BANK_URL" option. + * URL of the fakebank.  Obtained from CONFIG_FILE's + * "exchange-wire-test:BANK_URI" option.   */  static char *fakebank_url; +/** + * FIXME: what about putting exchange_url also global + * here?  Right now, the exchange port is being "bounced" + * between functions before exchange_url is constructed + * and TALER_EXCHANGE_connect() is called with that. + */  /**   * Account number of the exchange at the bank. @@ -72,6 +85,15 @@ static char *fakebank_url;     TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE)  /** + * Execute the taler-exchange-aggregator command with + * our configuration file. + * + * @param label label to use for the command. + */ +#define CMD_EXEC_AGGREGATOR(label) \ +   TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE) + +/**   * Run wire transfer of funds from some user's account to the   * exchange.   * @@ -83,9 +105,22 @@ static char *fakebank_url;       fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \       USER_LOGIN_NAME, USER_LOGIN_PASS) +/** + * 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_SUBJECT(label,amount,subject) \ +   TALER_TESTING_cmd_fakebank_transfer_with_subject \ +     (label, amount, fakebank_url, USER_ACCOUNT_NO, \ +      EXCHANGE_ACCOUNT_NO, USER_LOGIN_NAME, USER_LOGIN_PASS, \ +      subject)  /** - * Main function that will tell the interpreter what commands to run. + * Main function that will tell the interpreter what commands to + * run.   *   * @param cls closure   */ @@ -94,9 +129,609 @@ run (void *cls,       struct TALER_TESTING_Interpreter *is)  {    struct TALER_TESTING_Command commands[] = { + +    /****** Start of "wire" testing ******/ + +    /** +     * Move money to the exchange's bank account. +     */      CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1",                                "EUR:5.01"), -    CMD_EXEC_WIREWATCH ("exec-wirewatch-1"), + +    /** +     * Make a reserve exist, according to the previous +     * transfer. +     */ +    CMD_EXEC_WIREWATCH ("wirewatch-1"), + +    /** +     * Check if 'test' wire method is offered by the exchange. +     */ +    TALER_TESTING_cmd_wire ("wire-test-1", +                            is->exchange, +                            "test", +                            NULL, +                            MHD_HTTP_OK), + +    /** +     * Check if 'sepa' wire method is offered by the exchange. +     */ +    TALER_TESTING_cmd_wire ("wire-sepa-1", +                            is->exchange, +                            "sepa", +                            NULL, +                            MHD_HTTP_OK), + +    /****** End of "wire" testing ******/ + +    /******  Start of withdraw and spend testing ******/ + +    /** +     * Withdraw EUR:5. +     */ +    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", +                                       is->exchange, +                                       "create-reserve-1", +                                       "EUR:5", +                                       MHD_HTTP_OK), + +    /** +     * Check the reserve is depleted. +     */ +    TALER_TESTING_cmd_status ("status-1", +                              is->exchange, +                              "create-reserve-1", +                              "EUR:0", +                              MHD_HTTP_OK), +    /** +     * Spend the coin. +     */ +    TALER_TESTING_cmd_deposit +      ("deposit-simple", is->exchange, "withdraw-coin-1", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK), + +    /** +     * Try to overdraw. +     */ +    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2", +                                       is->exchange, +                                       "create-reserve-1", +                                       "EUR:5", +                                       MHD_HTTP_FORBIDDEN), + +    /** +     * Try to double spend using different wire details. +     */ +    TALER_TESTING_cmd_deposit +      ("deposit-double-1", is->exchange, "withdraw-coin-1", 0, +       TALER_TESTING_make_wire_details +         ("{\"type\":\"test\",\"account_number\":43}", +          fakebank_url), +       "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN), + +    /** +     * Try to double spend using a different transaction id. +     * (copied verbatim from old exchange-lib tests.) +     * FIXME: how can it get a different transaction id?  There +     * isn't such a thing actually, the exchange only knows about +     * contract terms' hashes.  So since the contract terms are +     * exactly the same as the previous command, how can a different +     * id be generated? +     */ +    TALER_TESTING_cmd_deposit +      ("deposit-double-1", is->exchange, "withdraw-coin-1", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\", \"account_number\":43}", +          fakebank_url), +       "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN), + +    /** +     * Try to double spend with different proposal. +     */ +    TALER_TESTING_cmd_deposit +      ("deposit-double-2", is->exchange, "withdraw-coin-1", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\", \"account_number\":43}", +          fakebank_url), +       "{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN), +     +    /******  End of withdraw and spend testing ******/ + +    /******  Start of refresh testing ******/ + +    /** +     * Fill reserve with EUR:5, 1ct is for fees.  NOTE: the old +     * test-suite gave a account number of _424_ to the user at +     * this step; to type less, here the _42_ number is reused. +     * Does this change the tests semantics? +     */ +    CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1", +                              "EUR:5.01"), +  +    /** +     * Make previous command effective. +     */ +    CMD_EXEC_WIREWATCH ("wirewatch-2"), + +    /** +     * Withdraw EUR:5. +     */ +    TALER_TESTING_cmd_withdraw_amount +      ("refresh-withdraw-coin-1", +       is->exchange, +       "refresh-create-reserve-1", +       "EUR:5", +       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", is->exchange, +       "refresh-withdraw-coin-1", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":42}", +          fakebank_url), +       "{\"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_refresh_melt +      ("refresh-melt-1", is->exchange, "EUR:4", +       "refresh-withdraw-coin-1", MHD_HTTP_OK), +    /** +     * Complete (successful) melt operation, and +     * withdraw the coins +     */ +    TALER_TESTING_cmd_refresh_reveal +      ("refresh-reveal-1", is->exchange, +       "refresh-melt-1", MHD_HTTP_OK), +     +    /** +     * Do it again to check idempotency +     */ +    TALER_TESTING_cmd_refresh_reveal +      ("refresh-reveal-1-idempotency", +       is->exchange, "refresh-melt-1", MHD_HTTP_OK), + +    /** +     * Test that /refresh/link works +     */ +    TALER_TESTING_cmd_refresh_link +      ("refresh-link-1", is->exchange, +       "refresh-reveal-1", MHD_HTTP_OK), + +    /** +     * Try to spend a refreshed EUR:1 coin +     */ +    TALER_TESTING_cmd_deposit +      ("refresh-deposit-refreshed-1a", is->exchange, +       "refresh-reveal-1-idempotency", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":42}", +          fakebank_url), +       "{\"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-1b", is->exchange, +       "refresh-reveal-1", 4, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":43}", +          fakebank_url), +       "{\"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_refresh_melt +      ("refresh-melt-failing", is->exchange, "EUR:4", +       "refresh-withdraw-coin-1", MHD_HTTP_FORBIDDEN), + +    /* FIXME: also test with coin that was already melted +     * (signature differs from coin that was deposited...) */ + +    /******  End of refresh testing ******/ + +    /* **** Test tracking API ***** */ + +    /** +     * 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", is->exchange, +     "deposit-simple", 0, MHD_HTTP_ACCEPTED, NULL), + +    /** +     * 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", is->exchange, +     "deposit-double-2", 0, MHD_HTTP_NOT_FOUND, NULL), + +    /** +     * 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", is->exchange, +       NULL, 0, MHD_HTTP_NOT_FOUND), + +    /** +     * Run transfers. Note that _actual_ aggregation will NOT +     * 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! +     */ +    CMD_EXEC_AGGREGATOR ("run-aggregator"), + +    /** +     * Check all the transfers took place. +     */ +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-499c", "https://exchange.com/", +       "EUR:4.98", 2, 42), + +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-99c1", "https://exchange.com/", +       "EUR:0.98", 2, 42), + +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-99c2", "https://exchange.com/", +       "EUR:0.98", 2, 42), + +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-99c", "https://exchange.com/", +       "EUR:0.08", 2, 43), + +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-aai-1", "https://exchange.com/", +       "EUR:5.01", 42, 2), + +    /** +     * NOTE: the old test-suite had this "check bank transfer" +     * command with debit account == 424. +     */ +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-aai-2", "https://exchange.com/", +       "EUR:5.01", 42, 2), + +    TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"), + +    TALER_TESTING_cmd_track_transaction +    ("deposit-wtid-ok", is->exchange, +     "deposit-simple", 0, MHD_HTTP_OK, "check_bank_transfer-499c"), + +    TALER_TESTING_cmd_track_transfer +      ("wire-deposit-success-bank", is->exchange, +       "check_bank_transfer-99c1", 0, MHD_HTTP_OK, "EUR:0.98", +       "EUR:0.01"), + +    TALER_TESTING_cmd_track_transfer +      ("wire-deposits-success-wtid", is->exchange, +       "deposit-wtid-ok", 0, MHD_HTTP_OK, "EUR:4.98", +       "EUR:0.01"), + +    /* **** End of test tracking API ***** */ + +    /* **** test /refund API ***** */ + +    /** +     * Fill reserve with EUR:5.01, as withdraw fee is 1 ct per +     * config. +     */ +    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r1", +                              "EUR:5.01"), + + +    /** +     * Run wire-watch to trigger the reserve creation. +     */ +    CMD_EXEC_WIREWATCH ("wirewatch-3"), + +    /* Withdraw a 5 EUR coin, at fee of 1 ct */ +    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1", +                                       is->exchange, +                                       "create-reserve-r1", +                                       "EUR:5", +                                       MHD_HTTP_OK), +    /** +     * Spend 5 EUR of the 5 EUR coin (in full) (merchant would +     * receive EUR:4.99 due to 1 ct deposit fee) +     */ +    TALER_TESTING_cmd_deposit +      ("deposit-refund-1", is->exchange, "withdraw-coin-r1", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\", \"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"ice cream\"," +                    "\"value\":\"EUR:5\"}]}", +       GNUNET_TIME_UNIT_MINUTES, "EUR:5", MHD_HTTP_OK), + + +    /** +     * Run transfers. Should do nothing as refund deadline blocks +     * it +     */ +    CMD_EXEC_AGGREGATOR ("run-aggregator-refund"), + +    /** +     * Check that aggregator didn't do anything, as expected. +     * 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. +     */ + +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-pre-refund", "https://exchange.com/", +       "EUR:5.01", 42, 2), + +    TALER_TESTING_cmd_check_bank_empty +      ("check_bank_transfer-pre-refund"), + +    TALER_TESTING_cmd_refund +      ("refund-ok", MHD_HTTP_OK, +       "EUR:5", "EUR:0.01", "deposit-refund-1"), + +    /** +     * Spend 4.99 EUR of the refunded 4.99 EUR coin (1ct gone +     * due to refund) (merchant would receive EUR:4.98 due to +     * 1 ct deposit fee) */ +    TALER_TESTING_cmd_deposit +      ("deposit-refund-2", is->exchange, "withdraw-coin-r1", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\", \"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"more ice cream\"," +                    "\"value\":\"EUR:5\"}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:4.99", MHD_HTTP_OK), + + +    /** +     * Run transfers. This will do the transfer as refund deadline +     * was 0 +     */ +    CMD_EXEC_AGGREGATOR ("run-aggregator-3"), + +    /** +     * Check that deposit did run. +     */ +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-pre-refund", "https://exchange.com/", +       "EUR:4.97", 2, 42), + +    /** +     * Run failing refund, as past deadline & aggregation. +     */ +    TALER_TESTING_cmd_refund +      ("refund-fail", MHD_HTTP_GONE, +       "EUR:4.99", "EUR:0.01", "deposit-refund-2"), + +    TALER_TESTING_cmd_check_bank_empty +      ("check-empty-after-refund"), + +    /** +     * Test refunded coins are never executed, even past +     * refund deadline +     */ +    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-rb", +                              "EUR:5.01"), + +    CMD_EXEC_WIREWATCH ("wirewatch-rb"), + +    TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb", +                                       is->exchange, +                                       "create-reserve-rb", +                                       "EUR:5", +                                       MHD_HTTP_OK), + +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-aai-3b", "https://exchange.com/", +       "EUR:5.01", 42, 2), + + +    TALER_TESTING_cmd_deposit +      ("deposit-refund-1b", is->exchange, "withdraw-coin-rb", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\", \"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"ice cream\"," +                    "\"value\":\"EUR:5\"}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK), + +    /** +     * Trigger refund (before aggregator had a chance to execute +     * deposit, even though refund deadline was zero). +     */ +    TALER_TESTING_cmd_refund +      ("refund-ok-fast", MHD_HTTP_OK, +       "EUR:5", "EUR:0.01", "deposit-refund-1b"), + +    /** +     * 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"), + +    /* ************** End of refund API testing ************* */ + +    /* ************** Test /payback API  ************* */ + +    /** +     * Fill reserve with EUR:5.01, as withdraw fee is 1 ct per +     * config. +     */ +    CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-1", +                              "EUR:5.01"), + +    /** +     * Run wire-watch to trigger the reserve creation. +     */ +    CMD_EXEC_WIREWATCH ("wirewatch-4"), + +    /* Withdraw a 5 EUR coin, at fee of 1 ct */ +    TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-1", +                                       is->exchange, +                                       "payback-create-reserve-1", +                                       "EUR:5", +                                       MHD_HTTP_OK),  + +    TALER_TESTING_cmd_revoke ("revoke-1", MHD_HTTP_OK, +                              "payback-withdraw-coin-1", +                              CONFIG_FILE), + +    TALER_TESTING_cmd_payback ("payback-1", MHD_HTTP_OK, +                               "payback-withdraw-coin-1", "EUR:5"), + +    /* Check the money is back with the reserve */ +    TALER_TESTING_cmd_status ("payback-reserve-status-1", +                              is->exchange, +                              "payback-create-reserve-1", +                              "EUR:5.0", +                              MHD_HTTP_OK), + +    /** +     * 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...) +     */ +    CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-2", +                              "EUR:2.02"), + +    /* Make previous command effective. */ +    CMD_EXEC_WIREWATCH ("wirewatch-5"), + +    /* Withdraw a 1 EUR coin, at fee of 1 ct */ +    TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2a", +                                       is->exchange, +                                       "payback-create-reserve-2", +                                       "EUR:1", +                                       MHD_HTTP_OK), + +    /* Withdraw a 1 EUR coin, at fee of 1 ct */ +    TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2b", +                                       is->exchange, +                                       "payback-create-reserve-2", +                                       "EUR:1", +                                       MHD_HTTP_OK), + +    TALER_TESTING_cmd_deposit +      ("payback-deposit-partial", is->exchange, +       "payback-withdraw-coin-2a", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_OK), + + +    TALER_TESTING_cmd_revoke ("revoke-2", MHD_HTTP_OK, +                              "payback-withdraw-coin-2a", +                              CONFIG_FILE), + +    TALER_TESTING_cmd_payback ("payback-2", MHD_HTTP_OK, +                               "payback-withdraw-coin-2a", +                               "EUR:0.5"), + +    TALER_TESTING_cmd_payback ("payback-2b", MHD_HTTP_FORBIDDEN, +                               "payback-withdraw-coin-2a", +                               "EUR:0.5"), + +    TALER_TESTING_cmd_deposit +      ("payback-deposit-revoked", is->exchange, +       "payback-withdraw-coin-2b", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_NOT_FOUND), + + +    /* Test deposit fails after payback, with proof in payback */ + +    /* FIXME: #3887: right now, the exchange will never return the +     * coin's transaction history with payback data, as we get a +     * 404 on the DK! */ +    TALER_TESTING_cmd_deposit +      ("payback-deposit-partial-after-payback", is->exchange, +       "payback-withdraw-coin-2a", 0, +       TALER_TESTING_make_wire_details +         ("{ \"type\":\"test\",\"account_number\":42}", +          fakebank_url), +       "{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}", +       GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_NOT_FOUND), + +    /* Test that revoked coins cannot be withdrawn */ +    CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-3", +                              "EUR:1.01"), + +    CMD_EXEC_WIREWATCH ("wirewatch-6"), + +    TALER_TESTING_cmd_withdraw_amount +      ("payback-withdraw-coin-3-revoked", +       is->exchange, +       "payback-create-reserve-3", +       "EUR:1", +       MHD_HTTP_NOT_FOUND), + +    /* check that we are empty before the rejection test */ +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-pr1", "https://exchange.com/", +       "EUR:5.01", 42, 2), +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-pr2", "https://exchange.com/", +       "EUR:2.02", 42, 2), +    TALER_TESTING_cmd_check_bank_transfer +      ("check_bank_transfer-pr3", "https://exchange.com/", +       "EUR:1.01", 42, 2), + +    TALER_TESTING_cmd_check_bank_empty ("check-empty-again"), + +    /* Test rejection of bogus wire transfers */ +    CMD_TRANSFER_TO_EXCHANGE_SUBJECT ("bogus-subject", +                                      "EUR:1.01", +                                      "not a reserve public key"), + +    CMD_EXEC_WIREWATCH ("wirewatch-7"), + +    TALER_TESTING_cmd_check_bank_empty ("check-empty-from-reject"), + +    /* ************** End of /payback API  ************* */ + +    /** +     * End the suite.  Fixme: better to have a label for this +     * too, as it shows a "(null)" token on logs. +     */      TALER_TESTING_cmd_end ()    }; @@ -105,7 +740,6 @@ run (void *cls,                                     fakebank_url);  } -  int  main (int argc,        char * const *argv) @@ -116,9 +750,15 @@ main (int argc,    GNUNET_log_setup ("test-exchange-api-new",                      "INFO",                      NULL); -  if (NULL == (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE))) +  if (NULL == (fakebank_url +       /* Check fakebank port is available and config cares +        * about bank url. */ +       = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))      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))    {    case GNUNET_SYSERR: @@ -128,10 +768,15 @@ main (int argc,      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; diff --git a/src/exchange-lib/testing_api_cmd_bank_check.c b/src/exchange-lib/testing_api_cmd_bank_check.c new file mode 100644 index 00000000..ab552244 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_bank_check.c @@ -0,0 +1,268 @@ +/* +  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 exchange-lib/testing_api_cmd_bank_check.c + * @brief command to check if a particular wire transfer took + *        place. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" +#include "taler_fakebank_lib.h" + +struct BankCheckState +{ + +  /** +   * Exchange base URL (Fixme: why?) +   */ +  const char *exchange_base_url; + +  /** +   * Expected transferred amount. +   */ +  const char *amount; + +  /** +   * Expected account number that gave money +   */ +  unsigned int debit_account; +  +  /** +   * Expected account number that received money +   */ +  unsigned int credit_account; + +  /** +   * Wire transfer subject (set by fakebank-lib). +   */ +  char *subject; + +  /** +   * Binary form of the transfer subject.  Some commands expect +   * it - via appropriate traits - to be in binary form. +   */ +  struct TALER_WireTransferIdentifierRawP wtid; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; +}; + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +check_bank_transfer_run (void *cls, +                         const struct TALER_TESTING_Command *cmd, +                         struct TALER_TESTING_Interpreter *is) +{ +  struct BankCheckState *bcs = cls; +  struct TALER_Amount amount; + + +  if (GNUNET_OK != +      TALER_string_to_amount (bcs->amount, +                              &amount)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at %u\n", +                bcs->amount, +                is->ip); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  if (GNUNET_OK != +      TALER_FAKEBANK_check (is->fakebank, +                            &amount, +                            bcs->debit_account, +                            bcs->credit_account, +                            bcs->exchange_base_url, +                            &bcs->subject)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  TALER_TESTING_interpreter_next (is); +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +check_bank_transfer_cleanup +  (void *cls, +   const struct TALER_TESTING_Command *cmd) +{ +  struct BankCheckState *bcs = cls; +  +  GNUNET_free_non_null (bcs->subject); +  GNUNET_free (bcs); +} + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +check_bank_transfer_traits (void *cls, +                            void **ret, +                            const char *trait, +                            unsigned int index) +{ + + +  struct BankCheckState *bcs = cls;  + +  GNUNET_assert (GNUNET_OK ==  +    GNUNET_STRINGS_string_to_data +      (bcs->subject, +       strlen (bcs->subject), +       &bcs->wtid, +       sizeof (struct TALER_WireTransferIdentifierRawP))); + +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_transfer_subject (0, bcs->subject), +    TALER_TESTING_make_trait_wtid (0, &bcs->wtid), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +}                            + + + +/** + * Command to check whether a particular wire transfer has been + * made or not. + * + * @param label the command label + * @param exchange_base_url base url of the exchange (Fixme: why?) + * @param amount the amount expected to be transferred + * @param debit_account the account that gave money + * @param credit_account 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, +   const char *amount, +   unsigned int debit_account, +   unsigned int credit_account) +{ +  struct BankCheckState *bcs; +  struct TALER_TESTING_Command cmd; + +  bcs = GNUNET_new (struct BankCheckState); +  bcs->exchange_base_url = exchange_base_url; +  bcs->amount = amount; +  bcs->debit_account = debit_account; +  bcs->credit_account = credit_account; + +  cmd.label = label; +  cmd.cls = bcs; +  cmd.run = &check_bank_transfer_run; +  cmd.cleanup = &check_bank_transfer_cleanup; +  // traits? +  cmd.traits = &check_bank_transfer_traits; + +  return cmd; +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +check_bank_empty_cleanup +  (void *cls, +   const struct TALER_TESTING_Command *cmd) +{ +  return; +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +check_bank_empty_run (void *cls, +                      const struct TALER_TESTING_Command *cmd, +                      struct TALER_TESTING_Interpreter *is) +{ + +  if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;   +  } +  TALER_TESTING_interpreter_next (is); +} + +/** + * Check bank's balance is zero. + * + * @param credit_account the account that received money + * + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_bank_empty (const char *label) +{ +  struct TALER_TESTING_Command cmd; + +  cmd.label = label; +  cmd.run = &check_bank_empty_run; +  cmd.cleanup = &check_bank_empty_cleanup; +   +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_check_keys.c b/src/exchange-lib/testing_api_cmd_check_keys.c new file mode 100644 index 00000000..8f77a83b --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_check_keys.c @@ -0,0 +1,153 @@ +/* +  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 exchange-lib/testing_api_cmd_check_keys.c + * @brief Implementation of "check keys" test command. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct CheckKeysState +{ +  /** +   * FIXME +   */ +  unsigned int generation; + +  /** +   * FIXME +   */ +  unsigned int num_denom_keys; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +}; + +/** + * Run the command. + * + * @param cls closure, typically a #struct SignalState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +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', key generation: %d\n", +              cmd->label, is->key_generation); + +  if (is->key_generation < cks->generation) +  { +    /* Go back to waiting for /keys signal! */ +    is->working = GNUNET_NO; +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Triggering /keys dl, cmd `%s'\n", +                cmd->label); +    GNUNET_break (0 == TALER_EXCHANGE_check_keys_current +      (cks->exchange, GNUNET_YES).abs_value_us); +    return; +  } +  if (is->key_generation > cks->generation) +  { +    /* We got /keys too often, strange. Fatal. May theoretically +       happen if somehow we were really unlucky and /keys expired +       "naturally", but obviously with a sane configuration this +       should also not be. */ +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  /* /keys was updated, let's check they were OK! */ +  if (cks->num_denom_keys != is->keys->num_denom_keys) +  { +    /* Did not get the expected number of denomination keys! */ +    GNUNET_break (0); +    fprintf (stderr, "Got %u keys in step %s\n", +             is->keys->num_denom_keys, cmd->label); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  TALER_TESTING_interpreter_next (is); +} + + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct SignalState. + * @param cmd the command which is being cleaned up. + */ +void +check_keys_cleanup (void *cls, +                    const struct TALER_TESTING_Command *cmd) +{ +  struct CheckKeysState *cks = cls; + +  GNUNET_free (cks); +} + +/** + * Make a "check keys" command. + * + * @param label command label + * @param generation FIXME + * @param num_denom_keys FIXME + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_keys +  (const char *label, +   unsigned int generation, +   unsigned int num_denom_keys, +   struct TALER_EXCHANGE_Handle *exchange) +{ + +  struct CheckKeysState *cks; +  struct TALER_TESTING_Command cmd; + +  cks = GNUNET_new (struct CheckKeysState); + +  cks->generation = generation; +  cks->num_denom_keys = num_denom_keys; +  cks->exchange = exchange; + +  cmd.cls = cks; +  cmd.label = label; +  cmd.run = &check_keys_run; +  cmd.cleanup = &check_keys_cleanup; + +  return cmd; + + +} diff --git a/src/exchange-lib/testing_api_cmd_deposit.c b/src/exchange-lib/testing_api_cmd_deposit.c new file mode 100644 index 00000000..f7951595 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_deposit.c @@ -0,0 +1,448 @@ +/* +  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 exchange-lib/testing_api_cmd_deposit.c + * @brief command for testing /deposit. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" +#include "taler_signatures.h" + +struct DepositState +{ + +  /** +   * Amount to deposit. +   */ +  const char *amount; + +  /** +   * Reference to any command that is able to provide a coin. +   */ +  const char *coin_reference; + +  /** +   * If this @e coin_ref refers to an operation that generated +   * an array of coins, this value determines which coin to pick. +   */ +  unsigned int coin_index; + +  /** +   * JSON string describing the merchant's "wire details". +   */ +  char *wire_details; + +  /** +   * JSON string describing what a proposal is about. +   */ +  const char *contract_terms; + +  /** +   * Relative time (to add to 'now') to compute the refund +   * deadline.  Zero for no refunds. +   */ +  struct GNUNET_TIME_Relative refund_deadline; + +  /** +   * Set (by the interpreter) to a fresh private key. +   */ +  struct TALER_MerchantPrivateKeyP merchant_priv; + +  /** +   * Deposit handle while operation is running. +   */ +  struct TALER_EXCHANGE_DepositHandle *dh; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Exchange connection. +   */ +  struct TALER_EXCHANGE_Handle *exchange; +}; + +/** + * Function called with the result of a /deposit operation. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for + *        successful deposit; 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 exchange_pub public key the exchange used for signing + * @param obj the received JSON reply, should be kept as proof + *        (and, in case of errors, be forwarded to the customer) + */ +static void +deposit_cb (void *cls, +            unsigned int http_status, +            enum TALER_ErrorCode ec, +            const struct TALER_ExchangePublicKeyP *exchange_pub, +            const json_t *obj) +{ +  struct DepositState *ds = cls; + +  ds->dh = NULL; +  if (ds->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                ds->is->commands[ds->is->ip].label); +    json_dumpf (obj, stderr, 0); +    TALER_TESTING_interpreter_fail (ds->is); +    return; +  } +  TALER_TESTING_interpreter_next (ds->is); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param i the interpreter state. + */ +void +deposit_run (void *cls, +             const struct TALER_TESTING_Command *cmd, +             struct TALER_TESTING_Interpreter *is) +{ +  struct DepositState *ds = cls; +  const struct TALER_TESTING_Command *coin_cmd; +  struct TALER_TESTING_Command *this_cmd; +  struct TALER_CoinSpendPrivateKeyP *coin_priv; +  struct TALER_CoinSpendPublicKeyP coin_pub; +  struct TALER_EXCHANGE_DenomPublicKey *denom_pub; +  struct TALER_DenominationSignature *denom_pub_sig; +  struct TALER_CoinSpendSignatureP coin_sig; +  struct GNUNET_TIME_Absolute refund_deadline; +  struct GNUNET_TIME_Absolute wire_deadline; +  struct GNUNET_TIME_Absolute timestamp; +  struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv; +  struct TALER_MerchantPublicKeyP merchant_pub; +  struct GNUNET_HashCode h_contract_terms; +  json_t *contract_terms; +  json_t *wire; +  struct TALER_Amount amount; + +  ds->is = is; +  this_cmd = &is->commands[is->ip]; + +  GNUNET_assert (ds->coin_reference); +  coin_cmd = TALER_TESTING_interpreter_lookup_command +    (is, +     ds->coin_reference); +  if (NULL == coin_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is);   +    return; +  } + +  /* Fixme: do prefer "interpreter fail" over assertions, +   * as the former takes care of shutting down processes  too */ +  GNUNET_assert (NULL != coin_cmd); + +  GNUNET_assert (GNUNET_OK +    == TALER_TESTING_get_trait_coin_priv (coin_cmd, +                                          ds->coin_index, +                                          &coin_priv)); + +  GNUNET_assert (GNUNET_OK +    == TALER_TESTING_get_trait_denom_pub (coin_cmd, +                                          ds->coin_index, +                                          &denom_pub)); + +  GNUNET_assert (GNUNET_OK +    == TALER_TESTING_get_trait_denom_sig (coin_cmd, +                                          ds->coin_index, +                                          &denom_pub_sig)); +  if (GNUNET_OK != +      TALER_string_to_amount (ds->amount, +                              &amount)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at '%u/%s'\n", +                 ds->amount, is->ip, this_cmd->label); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  contract_terms = json_loads (ds->contract_terms, +                               JSON_REJECT_DUPLICATES, +                               NULL); +  if (NULL == contract_terms) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse proposal data `%s' at %u/%s\n", +                ds->contract_terms, is->ip, this_cmd->label); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  GNUNET_assert (GNUNET_OK == +                 TALER_JSON_hash (contract_terms, +                                  &h_contract_terms)); +  json_decref (contract_terms); + +  wire = json_loads (ds->wire_details, +                     JSON_REJECT_DUPLICATES, +                     NULL); +  if (NULL == wire) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse wire details `%s' at %u/%s\n", +                ds->wire_details, +                is->ip, +                this_cmd->label); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +   +  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, +                                      &coin_pub.eddsa_pub); + +  merchant_priv = GNUNET_CRYPTO_eddsa_key_create (); +  ds->merchant_priv.eddsa_priv = *merchant_priv; +  GNUNET_free (merchant_priv); + +  if (0 != ds->refund_deadline.rel_value_us) +  { +    refund_deadline = GNUNET_TIME_relative_to_absolute +      (ds->refund_deadline); +    wire_deadline = GNUNET_TIME_relative_to_absolute +    (GNUNET_TIME_relative_multiply +      (ds->refund_deadline, 2)); +  } +  else +  { +    refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS; +    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); + +  timestamp = GNUNET_TIME_absolute_get (); +  GNUNET_TIME_round_abs (×tamp); +  GNUNET_TIME_round_abs (&refund_deadline); +  GNUNET_TIME_round_abs (&wire_deadline); + +  { +    struct TALER_DepositRequestPS dr; + +    memset (&dr, 0, sizeof (dr)); +    dr.purpose.size = htonl +      (sizeof (struct TALER_DepositRequestPS)); +    dr.purpose.purpose = htonl +      (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); +    dr.h_contract_terms = h_contract_terms; +    GNUNET_assert (GNUNET_OK == TALER_JSON_hash +      (wire, &dr.h_wire)); +    dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); +    dr.refund_deadline = GNUNET_TIME_absolute_hton +      (refund_deadline); +    TALER_amount_hton (&dr.amount_with_fee, &amount); +    TALER_amount_hton +      (&dr.deposit_fee, &denom_pub->fee_deposit); +    dr.merchant = merchant_pub; +    dr.coin_pub = coin_pub; +    GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign +      (&coin_priv->eddsa_priv, +       &dr.purpose, +       &coin_sig.eddsa_signature)); +  } +  ds->dh = TALER_EXCHANGE_deposit +    (ds->exchange, +     &amount, +     wire_deadline, +     wire, +     &h_contract_terms, +     &coin_pub, +     denom_pub_sig, +     &denom_pub->key, +     timestamp, +     &merchant_pub, +     refund_deadline, +     &coin_sig, +     &deposit_cb, +     ds); + +  if (NULL == ds->dh) +  { +    GNUNET_break (0); +    json_decref (wire); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  json_decref (wire); +  return; +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +deposit_cleanup (void *cls, +                 const struct TALER_TESTING_Command *cmd) +{ +  struct DepositState *ds = 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); +    ds->dh = NULL; +  } + +  GNUNET_free (ds->wire_details); +  GNUNET_free (ds); +} + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +deposit_traits (void *cls, +                void **ret, +                const char *trait, +                unsigned int index) +{ +  struct DepositState *ds = cls; +  const struct TALER_TESTING_Command *coin_cmd;  +  /* Will point to coin cmd internals. */ +  struct TALER_CoinSpendPrivateKeyP *coin_spent_priv; + +  coin_cmd = TALER_TESTING_interpreter_lookup_command +    (ds->is, ds->coin_reference); + +  if (NULL == coin_cmd) +  { +    GNUNET_break (0); +    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)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (ds->is); +    return GNUNET_NO; +  } + +  struct TALER_TESTING_Trait traits[] = { +    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_peer_key +      (0, &ds->merchant_priv.eddsa_priv), +    TALER_TESTING_trait_end ()   +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + +/** + * Create a deposit command. + * + * @param label command label + * @param exchange exchange connection + * @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 wire_details bank details of the merchant performing the + *        deposit + * @param contract_terms contract terms to be signed over by the + *        coin + * @param refund_deadline refund deadline, zero means 'no refunds' + * @param amount how much is going to be deposited + * @param expected_response_code which HTTP status code we expect + *        in the response + * + * @return the deposit command to be run by the interpreter + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_deposit +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *coin_reference, +   unsigned int coin_index, +   char *wire_details, +   const char *contract_terms, +   struct GNUNET_TIME_Relative refund_deadline, +   const char *amount, +   unsigned int expected_response_code) +{ +  struct TALER_TESTING_Command cmd; +  struct DepositState *ds; +   +  ds = GNUNET_new (struct DepositState); +  ds->exchange = exchange; +  ds->coin_reference = coin_reference; +  ds->coin_index = coin_index; +  ds->wire_details = wire_details; +  ds->contract_terms = contract_terms; +  ds->refund_deadline = refund_deadline; +  ds->amount = amount; +  ds->expected_response_code = expected_response_code; + +  cmd.cls = ds; +  cmd.label = label; +  cmd.run = &deposit_run; +  cmd.cleanup = &deposit_cleanup; +  cmd.traits = &deposit_traits; + +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_exec_aggregator.c b/src/exchange-lib/testing_api_cmd_exec_aggregator.c new file mode 100644 index 00000000..8d2131b1 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_exec_aggregator.c @@ -0,0 +1,162 @@ +/* +  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 exchange-lib/testing_api_cmd_exec_aggregator.c + * @brief run the taler-exchange-aggregator command + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +struct AggregatorState +{ + +  /** +   * Process for the aggregator. +   */ +  struct GNUNET_OS_Process *aggregator_proc; + +  /** +   * Which configuration file should we pass to the process? +   */ +  const char *config_filename; + +}; + + +/** + * Runs the command.  Note that upon return, the interpreter + * will not automatically run the next command, as the command + * may continue asynchronously in other scheduler tasks.  Thus, + * the command must ensure to eventually call + * #TALER_TESTING_interpreter_next() or + * #TALER_TESTING_interpreter_fail(). + * + * @param is interpreter state + */ +static void +aggregator_run (void *cls, +                const struct TALER_TESTING_Command *cmd, +                struct TALER_TESTING_Interpreter *is) +{ +  struct AggregatorState *as = cls; + +  as->aggregator_proc +    = GNUNET_OS_start_process (GNUNET_NO, +                               GNUNET_OS_INHERIT_STD_ALL, +                               NULL, NULL, NULL, +                               "taler-exchange-aggregator", +                               "taler-exchange-aggregator", +                               "-c", as->config_filename, +                               "-t", /* exit when done */ +                               NULL); +  if (NULL == as->aggregator_proc) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Clean up after the command.  Run during forced termination + * (CTRL-C) or test failure or test success. + * + * @param cls closure + */ +static void +aggregator_cleanup (void *cls, +                    const struct TALER_TESTING_Command *cmd) +{ +  struct AggregatorState *as = cls; + +  if (NULL != as->aggregator_proc) +  { +    GNUNET_break (0 == +                  GNUNET_OS_process_kill (as->aggregator_proc, +                                          SIGKILL)); +    GNUNET_OS_process_wait (as->aggregator_proc); +    GNUNET_OS_process_destroy (as->aggregator_proc); +    as->aggregator_proc = NULL; +  } +  GNUNET_free (as); +} + + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +aggregator_traits (void *cls, +                   void **ret, +                   const char *trait, +                   unsigned int index) +{ +  struct AggregatorState *as = cls; +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_process (0, &as->aggregator_proc), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +/** + * Execute taler-exchange-wirewatch process. + * + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_aggregator (const char *label, +                                   const char *config_filename) +{ +  struct TALER_TESTING_Command cmd; +  struct AggregatorState *as; + +  as = GNUNET_new (struct AggregatorState); +  as->config_filename = config_filename; +  cmd.cls = as; +  cmd.label = label; +  cmd.run = &aggregator_run; +  cmd.cleanup = &aggregator_cleanup; +  cmd.traits = &aggregator_traits; +  return cmd; +} + +/* end of testing_api_cmd_exec_aggregator.c */ diff --git a/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c b/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c new file mode 100644 index 00000000..bae0c076 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c @@ -0,0 +1,221 @@ +/* +  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 exchange-lib/testing_api_cmd_exec_auditor-sign.c + * @brief run the taler-exchange-aggregator command + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +struct AuditorSignState +{ + +  /** +   * Process for the "auditor sign" command. +   */ +  struct GNUNET_OS_Process *auditor_sign_proc; + +  /** +   * Which configuration file should we pass to the process? +   */ +  const char *config_filename; + +}; + + +/** + * Runs the command.  Note that upon return, the interpreter + * will not automatically run the next command, as the command + * may continue asynchronously in other scheduler tasks.  Thus, + * the command must ensure to eventually call + * #TALER_TESTING_interpreter_next() or + * #TALER_TESTING_interpreter_fail(). + * + * @param is interpreter state + */ +static void +auditor_sign_run (void *cls, +                  const struct TALER_TESTING_Command *cmd, +                  struct TALER_TESTING_Interpreter *is) +{ +  struct AuditorSignState *ass = cls; + +  struct GNUNET_CONFIGURATION_Handle *cfg; +  char *test_home_dir; +  char *signed_keys_out; +  char *exchange_master_pub; + +  cfg = GNUNET_CONFIGURATION_create (); +  if (GNUNET_OK != GNUNET_CONFIGURATION_load +    (cfg, ass->config_filename)) +  { +    GNUNET_break (0);  +    TALER_TESTING_interpreter_fail (is);  +    return; +  } + +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_string (cfg, +                                             "paths", +                                             "TALER_TEST_HOME", +                                             &test_home_dir)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "paths", +                               "TALER_TEST_HOME"); +    GNUNET_CONFIGURATION_destroy (cfg); +    GNUNET_break (0);  +    TALER_TESTING_interpreter_fail (is);  +    return; +  } + +  GNUNET_asprintf (&signed_keys_out, +                   "%s/.local/share/taler/auditors/auditor.out", +                   test_home_dir); + + +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_string (cfg, +                                             "exchange", +                                             "MASTER_PUBLIC_KEY", +                                             &exchange_master_pub)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "exchange", +                               "MASTER_PUBLIC_KEY"); +    GNUNET_CONFIGURATION_destroy (cfg); + +    GNUNET_break (0);  +    TALER_TESTING_interpreter_fail (is);  +    return; +  } + +  GNUNET_CONFIGURATION_destroy (cfg); + +  ass->auditor_sign_proc = GNUNET_OS_start_process +    (GNUNET_NO, +     GNUNET_OS_INHERIT_STD_ALL, +     NULL, NULL, NULL, +     "taler-auditor-sign", +     "taler-auditor-sign", +     "-c", ass->config_filename, +     "-u", "http://auditor/", +     "-m", exchange_master_pub, +     "-r", "auditor.in", +     "-o", signed_keys_out, +     NULL); + +  if (NULL == ass->auditor_sign_proc) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Clean up after the command.  Run during forced termination + * (CTRL-C) or test failure or test success. + * + * @param cls closure + */ +static void +auditor_sign_cleanup (void *cls, +                    const struct TALER_TESTING_Command *cmd) +{ +  struct AuditorSignState *ass = cls; + +  if (NULL != ass->auditor_sign_proc) +  { +    GNUNET_break (0 == GNUNET_OS_process_kill +      (ass->auditor_sign_proc, SIGKILL)); +    GNUNET_OS_process_wait (ass->auditor_sign_proc); +    GNUNET_OS_process_destroy (ass->auditor_sign_proc); +    ass->auditor_sign_proc = NULL; +  } +  GNUNET_free (ass); +} + + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +auditor_sign_traits (void *cls, +                     void **ret, +                     const char *trait, +                     unsigned int index) +{ +  struct AuditorSignState *ass = cls; +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_process (0, &ass->auditor_sign_proc), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +/** + * Execute taler-auditor-sign process. + * + * @param label command label + * @param config_filename configuration filename + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_auditor_sign (const char *label, +                                     const char *config_filename) +{ +  struct TALER_TESTING_Command cmd; +  struct AuditorSignState *ass; + +  ass = GNUNET_new (struct AuditorSignState); +  ass->config_filename = config_filename; +  cmd.cls = ass; +  cmd.label = label; +  cmd.run = &auditor_sign_run; +  cmd.cleanup = &auditor_sign_cleanup; +  cmd.traits = &auditor_sign_traits; +  return cmd; +} + +/* end of testing_api_cmd_exec_auditor-sign.c */ diff --git a/src/exchange-lib/testing_api_cmd_exec_keyup.c b/src/exchange-lib/testing_api_cmd_exec_keyup.c new file mode 100644 index 00000000..9398dd4f --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_exec_keyup.c @@ -0,0 +1,168 @@ +/* +  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 exchange-lib/testing_api_cmd_exec_keyup.c + * @brief run the taler-exchange-keyup command + * @author Marcello Stanisci + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + + +struct KeyupState +{ + +  /** +   * Process for the "keyup" command. +   */ +  struct GNUNET_OS_Process *keyup_proc; + +  /** +   * Which configuration file should we pass to the process? +   */ +  const char *config_filename; + +}; + + +/** + * Runs the command.  Note that upon return, the interpreter + * will not automatically run the next command, as the command + * may continue asynchronously in other scheduler tasks.  Thus, + * the command must ensure to eventually call + * #TALER_TESTING_interpreter_next() or + * #TALER_TESTING_interpreter_fail(). + * + * @param is interpreter state + */ +static void +keyup_run (void *cls, +           const struct TALER_TESTING_Command *cmd, +           struct TALER_TESTING_Interpreter *is) +{ +  struct KeyupState *ks = cls; + +  ks->keyup_proc = GNUNET_OS_start_process +    (GNUNET_NO, +     GNUNET_OS_INHERIT_STD_ALL, +     NULL, NULL, NULL, +     "taler-exchange-keyup", +     "taler-exchange-keyup", +     "-c", ks->config_filename, +     "-o", "auditor.in", +     NULL); + +  if (NULL == ks->keyup_proc) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Clean up after the command.  Run during forced termination + * (CTRL-C) or test failure or test success. + * + * @param cls closure + */ +static void +keyup_cleanup (void *cls, +               const struct TALER_TESTING_Command *cmd) +{ +  struct KeyupState *ks = cls; + +  if (NULL != ks->keyup_proc) +  { +    GNUNET_break (0 == +                  GNUNET_OS_process_kill (ks->keyup_proc, +                                          SIGKILL)); +    GNUNET_OS_process_wait (ks->keyup_proc); +    GNUNET_OS_process_destroy (ks->keyup_proc); +    ks->keyup_proc = NULL; +  } +  GNUNET_free (ks); +} + + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +keyup_traits (void *cls, +              void **ret, +              const char *trait, +              unsigned int index) +{ +  struct KeyupState *ks = cls; +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_process (0, &ks->keyup_proc), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +/** + * Execute taler-exchange-keyup process. + * + * @param label command label + * @param config_filename configuration filename + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_keyup (const char *label, +                              const char *config_filename) +{ +  struct TALER_TESTING_Command cmd; +  struct KeyupState *ks; + +  ks = GNUNET_new (struct KeyupState); +  ks->config_filename = config_filename; +  cmd.cls = ks; +  cmd.label = label; +  cmd.run = &keyup_run; +  cmd.cleanup = &keyup_cleanup; +  cmd.traits = &keyup_traits; +  return cmd; +} + +/* end of testing_api_cmd_exec_keyup.c */ diff --git a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c index 7b95afbc..107ac55a 100644 --- a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c +++ b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c @@ -122,11 +122,11 @@ static int  wirewatch_traits (void *cls,                    void **ret,                    const char *trait, -                  const char *selector) +                  unsigned int index)  {    struct WirewatchState *ws = cls;    struct TALER_TESTING_Trait traits[] = { -    TALER_TESTING_make_trait_process (NULL, +    TALER_TESTING_make_trait_process (0,                                        &ws->wirewatch_proc),      TALER_TESTING_trait_end ()    }; @@ -134,7 +134,7 @@ wirewatch_traits (void *cls,    return TALER_TESTING_get_trait (traits,                                    ret,                                    trait, -                                  selector); +                                  index);  } diff --git a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c index 6b1a99dc..dfbaadde 100644 --- a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c +++ b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c @@ -2,18 +2,21 @@    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 +  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 exchange-lib/testing_api_cmd_fakebank_transfer.c   * @brief implementation of a fakebank wire transfer command @@ -104,14 +107,17 @@ struct FakebankTransferState  /** - * Function called upon completion of our /admin/add/incoming request. + * Function called upon completion of our /admin/add/incoming + * request.   *   * @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 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 full_response full response from the exchange (for logging, in case of errors) + * @param full_response full response from the exchange (for + *        logging, in case of errors)   */  static void  add_incoming_cb (void *cls, @@ -167,8 +173,8 @@ fakebank_transfer_run (void *cls,        const struct TALER_TESTING_Command *ref;        struct TALER_ReservePrivateKeyP *reserve_priv; -      ref = TALER_TESTING_interpreter_lookup_command (is, -                                                      fts->reserve_reference); +      ref = TALER_TESTING_interpreter_lookup_command +        (is, fts->reserve_reference);        if (NULL == ref)        {          GNUNET_break (0); @@ -177,7 +183,7 @@ fakebank_transfer_run (void *cls,        }        if (GNUNET_OK !=            TALER_TESTING_get_trait_reserve_priv (ref, -                                                NULL, +                                                0,                                                  &reserve_priv))        {          GNUNET_break (0); @@ -193,27 +199,27 @@ fakebank_transfer_run (void *cls,        fts->reserve_priv.eddsa_priv = *priv;        GNUNET_free (priv);      } -    GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv, -                                        &reserve_pub.eddsa_pub); -    subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub, -                                                   sizeof (reserve_pub)); +    GNUNET_CRYPTO_eddsa_key_get_public +      (&fts->reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub); +    subject = GNUNET_STRINGS_data_to_string_alloc +      (&reserve_pub, sizeof (reserve_pub));    }    auth.method = TALER_BANK_AUTH_BASIC;    auth.details.basic.username = (char *) fts->auth_username;    auth.details.basic.password = (char *) fts->auth_password;    fts->is = is; -  fts->aih -    = TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context (is), -                                     fts->bank_url, -                                     &auth, -                                     "https://exchange.com/", /* exchange URL: FIXME */ -                                     subject, -                                     &fts->amount, -                                     fts->debit_account_no, -                                     fts->credit_account_no, -                                     &add_incoming_cb, -                                     fts); +  fts->aih = TALER_BANK_admin_add_incoming +    (TALER_TESTING_interpreter_get_context (is), +     fts->bank_url, +     &auth, +     "https://exchange.com/", /* exchange URL: FIXME */ +     subject, +     &fts->amount, +     fts->debit_account_no, +     fts->credit_account_no, +     &add_incoming_cb, +     fts);    GNUNET_free (subject);    if (NULL == fts->aih)    { @@ -263,11 +269,11 @@ static int  fakebank_transfer_traits (void *cls,                            void **ret,                            const char *trait, -                          const char *selector) +                          unsigned int index)  {    struct FakebankTransferState *fts = cls;    struct TALER_TESTING_Trait traits[] = { -    TALER_TESTING_make_trait_reserve_priv (NULL, +    TALER_TESTING_make_trait_reserve_priv (0,                                             &fts->reserve_priv),      TALER_TESTING_trait_end ()    }; @@ -275,12 +281,13 @@ fakebank_transfer_traits (void *cls,    if (NULL != fts->subject)    {      GNUNET_break (0); -    return GNUNET_SYSERR; /* we do NOT create a reserve private key */ +    /* we do NOT create a reserve private key */ +    return GNUNET_SYSERR;     }    return TALER_TESTING_get_trait (traits,                                    ret,                                    trait, -                                  selector); +                                  index);  } @@ -330,14 +337,15 @@ TALER_TESTING_cmd_fakebank_transfer (const char *label,   *   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label, -                                                  const char *amount, -                                                  const char *bank_url, -                                                  uint64_t debit_account_no, -                                                  uint64_t credit_account_no, -                                                  const char *auth_username, -                                                  const char *auth_password, -                                                  const char *subject) +TALER_TESTING_cmd_fakebank_transfer_with_subject +  (const char *label, +   const char *amount, +   const char *bank_url, +   uint64_t debit_account_no, +   uint64_t credit_account_no, +   const char *auth_username, +   const char *auth_password, +   const char *subject)  {    struct TALER_TESTING_Command cmd;    struct FakebankTransferState *fts; @@ -373,14 +381,15 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,   *   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, -                                              const char *amount, -                                              const char *bank_url, -                                              uint64_t debit_account_no, -                                              uint64_t credit_account_no, -                                              const char *auth_username, -                                              const char *auth_password, -                                              const char *ref) +TALER_TESTING_cmd_fakebank_transfer_with_ref +  (const char *label, +   const char *amount, +   const char *bank_url, +   uint64_t debit_account_no, +   uint64_t credit_account_no, +   const char *auth_username, +   const char *auth_password, +   const char *ref)  {    struct TALER_TESTING_Command cmd;    struct FakebankTransferState *fts; diff --git a/src/exchange-lib/testing_api_cmd_payback.c b/src/exchange-lib/testing_api_cmd_payback.c new file mode 100644 index 00000000..6c791e56 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_payback.c @@ -0,0 +1,491 @@ +/* +  This file is part of TALER +  Copyright (C) 2014-2018 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as +  published by the Free Software Foundation; either version 3, or +  (at your option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ + +/** + * @file exchange/testing_api_cmd_payback.c + * @brief Implement the /revoke and /payback test commands. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct RevokeState +{ +  /**  +   * Expected HTTP status code. +   */ +  unsigned int expected_response_code; + +  /** +   * Command that offers a denomination to revoke. +   */ +  const char *coin_reference; + +  /** +   * The interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * The revoke process handle. +   */ +  struct GNUNET_OS_Process *revoke_proc; + +  /** +   * Configuration filename. +   */ +  const char *config_filename; + +  /** +   * Encoding of the denomination (to revoke) public key hash. +   */ +  char *dhks; + +}; + +struct PaybackState +{ +  /**  +   * 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; + +  /** +   * The interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Amount expected to be paid back. +   */ +  const char *amount; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Handle to the ongoing operation. +   */ +  struct TALER_EXCHANGE_PaybackHandle *ph; + +}; + +/** + * Check the result of the payback request. + * + * @param cls closure + * @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 amount amount the exchange will wire back for this coin + * @param timestamp what time did the exchange receive the + *        /payback request + * @param reserve_pub public key of the reserve receiving the + *        payback + * @param full_response full response from the exchange (for + *        logging, in case of errors) + */ +static void +payback_cb (void *cls, +            unsigned int http_status, +            enum TALER_ErrorCode ec, +            const struct TALER_Amount *amount, +            struct GNUNET_TIME_Absolute timestamp, +            const struct TALER_ReservePublicKeyP *reserve_pub, +            const json_t *full_response) +{ + +  struct PaybackState *ps = cls; +  struct TALER_TESTING_Interpreter *is = ps->is; +  struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; +  const struct TALER_TESTING_Command *reserve_cmd; +  struct TALER_ReservePrivateKeyP *reserve_priv; +  struct TALER_ReservePublicKeyP rp; +  struct TALER_Amount expected_amount; + +  ps->ph = NULL; +  if (ps->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                cmd->label); +    json_dumpf (full_response, stderr, 0); +    fprintf (stderr, "\n"); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  reserve_cmd = TALER_TESTING_interpreter_lookup_command +    (is, ps->coin_reference); + +  if (NULL == reserve_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +   +  if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv +    (reserve_cmd, 0, &reserve_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, +                                      &rp.eddsa_pub); + +  switch (http_status) +  { +  case MHD_HTTP_OK: +    if (GNUNET_OK != TALER_string_to_amount +      (ps->amount, &expected_amount)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } +    if (0 != TALER_amount_cmp (amount, &expected_amount)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Total amount missmatch to command %s\n", +                  cmd->label); +      json_dumpf (full_response, stderr, 0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } +    if (0 != memcmp (reserve_pub, &rp, +                     sizeof (struct TALER_ReservePublicKeyP))) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } +    break; +  default: +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Unmanaged HTTP status code.\n"); +    break; +  } +  TALER_TESTING_interpreter_next (is); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +payback_run (void *cls, +             const struct TALER_TESTING_Command *cmd, +             struct TALER_TESTING_Interpreter *is) +{ +  struct PaybackState *ps = cls; +  const struct TALER_TESTING_Command *coin_cmd; +  struct TALER_CoinSpendPrivateKeyP *coin_priv; +  struct TALER_DenominationBlindingKeyP *blinding_key; +  struct TALER_EXCHANGE_DenomPublicKey *denom_pub; +  struct TALER_DenominationSignature *coin_sig; +  struct TALER_PlanchetSecretsP planchet; + +  ps->is = is; +  ps->exchange = is->exchange; +  coin_cmd = TALER_TESTING_interpreter_lookup_command +    (is, ps->coin_reference); + +  if (NULL == coin_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;   +  } + +  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv +    (coin_cmd, 0, &coin_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;  +  } + +  if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key +    (coin_cmd, 0, &blinding_key)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;  +  } +  planchet.coin_priv = *coin_priv; +  planchet.blinding_key = *blinding_key; + +  if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub +    (coin_cmd, 0, &denom_pub)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;  +  } + +  if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig +     (coin_cmd, 0, &coin_sig)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;  +  } + +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Trying to get '%s..' paid back\n", +              TALER_B2S (&denom_pub->h_key)); +   +  ps->ph = TALER_EXCHANGE_payback (ps->exchange, +                                   denom_pub, +                                   coin_sig, +                                   &planchet, +                                   payback_cb, +                                   ps); +  GNUNET_assert (NULL != ps->ph); +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +revoke_cleanup (void *cls, +                const struct TALER_TESTING_Command *cmd) +{ + +  struct RevokeState *rs = cls; + +  if (NULL != rs->revoke_proc) +  { +    GNUNET_break (0 == GNUNET_OS_process_kill +      (rs->revoke_proc, SIGKILL)); +    GNUNET_OS_process_wait (rs->revoke_proc); +    GNUNET_OS_process_destroy (rs->revoke_proc); +    rs->revoke_proc = NULL; +  } + +  GNUNET_free (rs->dhks); +  GNUNET_free (rs); +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +payback_cleanup (void *cls, +                 const struct TALER_TESTING_Command *cmd) +{ +  struct PaybackState *ps = cls; +  if (NULL != ps->ph) +  { +    TALER_EXCHANGE_payback_cancel (ps->ph); +    ps->ph = NULL; +  } +  GNUNET_free (ps); +} + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +revoke_traits (void *cls, +               void **ret, +               const char *trait, +               unsigned int index) +{ + +  struct RevokeState *rs = 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_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +revoke_run (void *cls, +            const struct TALER_TESTING_Command *cmd, +            struct TALER_TESTING_Interpreter *is) +{ +  struct RevokeState *rs = cls; +  const struct TALER_TESTING_Command *coin_cmd; +  struct TALER_EXCHANGE_DenomPublicKey *denom_pub; + +  rs->is = is; +  /* 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, &denom_pub)); + +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Trying to revoke denom '%s..'\n", +              TALER_B2S (&denom_pub->h_key)); + +  rs->dhks = GNUNET_STRINGS_data_to_string_alloc +    (&denom_pub->h_key, sizeof (struct GNUNET_HashCode));  +   +  rs->revoke_proc = GNUNET_OS_start_process +    (GNUNET_NO, +     GNUNET_OS_INHERIT_STD_ALL, +     NULL, NULL, NULL, +     "taler-exchange-keyup", +     "taler-exchange-keyup", +     "-c", rs->config_filename, +     "-r", rs->dhks, +     NULL); + + +  if (NULL == rs->revoke_proc) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Revoke is ongoing..\n"); + +  is->reload_keys = GNUNET_OK; +  TALER_TESTING_wait_for_sigchld (is); +} + + +/** + * Make a /payback command. + * + * @param label the command label + * @param expected_response_code expected HTTP status code + * @param coin_reference reference to any command which + *        offers a reserve private key + * @param amount denomination to pay back. + * + * @return a /revoke command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_payback (const char *label, +                           unsigned int expected_response_code, +                           const char *coin_reference, +                           const char *amount) +{ +  struct PaybackState *ps; +  struct TALER_TESTING_Command cmd; +   +  ps = GNUNET_new (struct PaybackState); +  ps->expected_response_code = expected_response_code; +  ps->coin_reference = coin_reference; +  ps->amount = amount; + +  cmd.cls = ps; +  cmd.label = label; +  cmd.run = &payback_run; +  cmd.cleanup = &payback_cleanup; + +  return cmd; +} + +/** + * Make a /revoke 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 trait + * @param config_filename configuration file name. + * + * @return a /revoke command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_revoke (const char *label, +                          unsigned int expected_response_code, +                          const char *coin_reference, +                          const char *config_filename) +{ + +  struct RevokeState *rs; +  struct TALER_TESTING_Command cmd; + +  rs = GNUNET_new (struct RevokeState); +  rs->expected_response_code = expected_response_code; +  rs->coin_reference = coin_reference; +  rs->config_filename = config_filename; + +  cmd.cls = rs; +  cmd.label = label; +  cmd.run = &revoke_run; +  cmd.cleanup = &revoke_cleanup; +  cmd.traits = &revoke_traits; + +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_refresh.c b/src/exchange-lib/testing_api_cmd_refresh.c new file mode 100644 index 00000000..3e58552b --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_refresh.c @@ -0,0 +1,993 @@ +/* +  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 exchange-lib/testing_api_cmd_refresh.c + * @brief commands for testing all "refresh" features. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" +#include "taler_signatures.h" + +/** + * Structure specifying details about a coin to be melted. + * Used in a NULL-terminated array as part of command + * specification. + */ +struct MeltDetails +{ + +  /** +   * Amount to melt (including fee). +   */ +  const char *amount; + +  /** +   * Reference to reserve_withdraw operations for coin to +   * be used for the /refresh/melt operation. +   */ +  const char *coin_reference; +}; + + +/** + * State for a "refresh melt" operation. + */ +struct RefreshMeltState +{ +  /** +   * Fixme: figure out this data purpose. +   */ +  const char *amount; + +  /** +   * Information about coins to be melted. +   */ +  struct MeltDetails melted_coin; + +  /** +   * Data used in the refresh operation. +   */ +  char *refresh_data; + +  /** +   * Number of bytes in @e refresh_data. +   */ +  size_t refresh_data_length; + +  /** +   * Reference to a previous melt command. +   */ +  const char *melt_reference; + +  /** +   * Melt handle while operation is running. +   */ +  struct TALER_EXCHANGE_RefreshMeltHandle *rmh; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Array of the public keys corresponding to +   * the @e fresh_amounts, set by the interpreter. +   */ +  struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; + +  /** +   * Set by the interpreter (upon completion) to the +   * noreveal index selected by the exchange. +   */ +  uint16_t noreveal_index; +}; + +/** + * State for a "refresh reveal" operation. + */ +struct RefreshRevealState +{ +  /** +   * Link to a "refresh melt" command. +   */ +  const char *melt_reference; + +  /** +   * Reveal handle while operation is running. +   */ +  struct TALER_EXCHANGE_RefreshRevealHandle *rrh; + +  /** +   * Number of fresh coins withdrawn, set by the interpreter. +   * Length of the @e fresh_coins array. +   */ +  unsigned int num_fresh_coins; + +  /** +   * Information about coins withdrawn, set by the interpreter. +   */ +  struct FreshCoin *fresh_coins; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; +}; + +/** + * State for a "refresh link" operation. + */ +struct RefreshLinkState +{ +  /** +   * Link to a "refresh reveal" command. +   */ +  const char *reveal_reference; + +  /** +   * Link handle while operation is running. +   */ +  struct TALER_EXCHANGE_RefreshLinkHandle *rlh; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; +}; + + +/** + * Function called with the result of the /refresh/reveal operation. + * + * @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 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 full_response full response from the exchange (for + *        logging, in case of errors) + */ +static void +reveal_cb (void *cls, +           unsigned int http_status, +	   enum TALER_ErrorCode ec, +           unsigned int num_coins, +           const struct TALER_CoinSpendPrivateKeyP *coin_privs, +           const struct TALER_DenominationSignature *sigs, +           const json_t *full_response) +{ + +  struct RefreshRevealState *rrs = cls; +  const struct TALER_TESTING_Command *melt_cmd; + +  rrs->rrh = NULL; +  if (rrs->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                rrs->is->commands[rrs->is->ip].label); +    json_dumpf (full_response, stderr, 0); +    TALER_TESTING_interpreter_fail (rrs->is); +    return; +  } +  melt_cmd = TALER_TESTING_interpreter_lookup_command +    (rrs->is, rrs->melt_reference); +  if (NULL == melt_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (rrs->is); +    return; +  } +  rrs->num_fresh_coins = num_coins; +  switch (http_status) +  { +  case MHD_HTTP_OK: +    rrs->fresh_coins = GNUNET_new_array +      (num_coins, struct FreshCoin); + +    struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; +    unsigned int i; +    if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub +      (melt_cmd, 0, &fresh_pks)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rrs->is); +      return;     +    } + +    for (i=0; i<num_coins; i++) +    { +      struct FreshCoin *fc = &rrs->fresh_coins[i]; + +      fc->pk = &fresh_pks[i]; +      fc->coin_priv = coin_privs[i]; +      fc->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup +        (sigs[i].rsa_signature); +    } +    break; +  default: +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Unknown HTTP status %d\n", +                http_status); +  } +  TALER_TESTING_interpreter_next (rrs->is); +} + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +refresh_reveal_run (void *cls, +                    const struct TALER_TESTING_Command *cmd, +                    struct TALER_TESTING_Interpreter *is) +{ +  struct RefreshRevealState *rrs = cls; +  struct RefreshMeltState *rms; +  const struct TALER_TESTING_Command *melt_cmd; +     +  rrs->is = is; +  melt_cmd = TALER_TESTING_interpreter_lookup_command +    (is, rrs->melt_reference); +   +  if (NULL == melt_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (rrs->is); +    return; +  } +  rms = melt_cmd->cls; +  rrs->rrh = TALER_EXCHANGE_refresh_reveal +    (rrs->exchange, +     rms->refresh_data_length, +     rms->refresh_data, +     rms->noreveal_index, +     &reveal_cb, rrs); + +  if (NULL == rrs->rrh) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +} + + +/** + * Cleanup the state. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +void +refresh_reveal_cleanup (void *cls, +                        const struct TALER_TESTING_Command *cmd) +{ +  struct RefreshRevealState *rrs = cls;  + +  if (NULL != rrs->rrh) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                rrs->is->ip, +                cmd->label); + +    TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh); +    rrs->rrh = NULL; +  } + +  { /* Not sure why block-ing this */ +    unsigned int j; + +    for (j=0; j < rrs->num_fresh_coins; j++) +      GNUNET_CRYPTO_rsa_signature_free +        (rrs->fresh_coins[j].sig.rsa_signature); +  } + +  GNUNET_free_non_null (rrs->fresh_coins); +  rrs->fresh_coins = NULL; +  rrs->num_fresh_coins = 0; +} + + +/** + * Function called with the result of a /refresh/link operation. + * + * @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 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 full_response full response from the exchange (for + *        logging, in case of errors) + */ +static void +link_cb (void *cls, +         unsigned int http_status, +	 enum TALER_ErrorCode ec, +         unsigned int num_coins, +         const struct TALER_CoinSpendPrivateKeyP *coin_privs, +         const struct TALER_DenominationSignature *sigs, +         const struct TALER_DenominationPublicKey *pubs, +         const json_t *full_response) +{ + +  struct RefreshLinkState *rls = cls; +  const struct TALER_TESTING_Command *reveal_cmd; +  struct TALER_TESTING_Command *link_cmd +    = &rls->is->commands[rls->is->ip]; +  unsigned int found; +  unsigned int *num_fresh_coins; + +  rls->rlh = NULL; +  if (rls->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                link_cmd->label); +    json_dumpf (full_response, stderr, 0); +    TALER_TESTING_interpreter_fail (rls->is); +    return; +  } +  reveal_cmd = TALER_TESTING_interpreter_lookup_command +    (rls->is, rls->reveal_reference); + +  if (NULL == reveal_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (rls->is); +    return; +  } + +  switch (http_status) +  { +  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)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rls->is); +      return; +    } +    if (num_coins != *num_fresh_coins) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Unexpected number of fresh coins: %d vs %d\n", +                  num_coins, *num_fresh_coins); +      TALER_TESTING_interpreter_fail (rls->is); +      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++) +	if (0 == memcmp +          (&coin_privs[i], &coin_privs[j], +           sizeof (struct TALER_CoinSpendPrivateKeyP))) +	  GNUNET_break (0); +    /* Note: coins might be legitimately permutated in here... */ +    found = 0; + +    /* Will point to the pointer inside the cmd state. */ +    struct FreshCoin *fc = NULL; + +    if (GNUNET_OK != TALER_TESTING_get_trait_fresh_coins +      (reveal_cmd, 0, &fc)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rls->is); +      return; +    } + +    for (unsigned int i=0;i<num_coins;i++) +      for (unsigned int j=0;j<num_coins;j++) +      { +	if ( (0 == memcmp +                (&coin_privs[i], &fc[i].coin_priv, +                sizeof (struct TALER_CoinSpendPrivateKeyP))) && +	     (0 == GNUNET_CRYPTO_rsa_signature_cmp +                (fc[i].sig.rsa_signature, +                 sigs[i].rsa_signature)) && +	     (0 == GNUNET_CRYPTO_rsa_public_key_cmp +               (fc[i].pk->key.rsa_public_key, +                pubs[i].rsa_public_key)) ) +	{ +	  found++; +	  break; +	} +      } +    if (found != num_coins) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Only %u/%u coins match expectations\n", +	          found, num_coins); +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rls->is); +      return; +    } +    break; +  default: +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unknown HTTP response code %u.\n", +                http_status); +  } +  TALER_TESTING_interpreter_next (rls->is); +} + + +/** + * Run the command. + * + * @param cls closure + * @param cmd the command to execute, a /wire one. + * @param i the interpreter state. + */ +void +refresh_link_run (void *cls, +                  const struct TALER_TESTING_Command *cmd, +                  struct TALER_TESTING_Interpreter *is) +{ +   +  struct RefreshLinkState *rls = cls; +  struct RefreshRevealState *rrs; +  struct RefreshMeltState *rms; + +  const struct TALER_TESTING_Command *reveal_cmd; +  const struct TALER_TESTING_Command *melt_cmd; +  const struct TALER_TESTING_Command *coin_cmd; +  rls->is = is; + +  reveal_cmd = TALER_TESTING_interpreter_lookup_command +    (rls->is, rls->reveal_reference); + +  if (NULL == reveal_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (rls->is); +    return;  +  } +  rrs = reveal_cmd->cls; +  melt_cmd = TALER_TESTING_interpreter_lookup_command +    (rls->is, rrs->melt_reference); + +  if (NULL == melt_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (rls->is); +    return;  +  } + +  /* find reserve_withdraw command */ +  { +    const struct MeltDetails *md; +     +    rms = melt_cmd->cls; +    md = &rms->melted_coin; +    coin_cmd = TALER_TESTING_interpreter_lookup_command +      (rls->is, md->coin_reference); +    if (NULL == coin_cmd) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rls->is); +      return; +    } +  } + +  struct TALER_CoinSpendPrivateKeyP *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); +    return; +  } + +  /* finally, use private key from withdraw sign command */ +  rls->rlh = TALER_EXCHANGE_refresh_link +    (rls->exchange, coin_priv, &link_cb, rls); + +  if (NULL == rls->rlh) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (rls->is); +    return; +  } +} + +/** + * Cleanup the state. + * + * @param cls closure + * @param cmd the command which is being cleaned up. + */ +void +refresh_link_cleanup (void *cls, +                      const struct TALER_TESTING_Command *cmd) +{ +  struct RefreshLinkState *rls = cls; +   +  if (NULL != rls->rlh) +  { + +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                rls->is->ip, +                cmd->label); +    TALER_EXCHANGE_refresh_link_cancel (rls->rlh); +    rls->rlh = NULL; +  } +} + + +/** + * Function called with the result of the /refresh/melt operation. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, never #MHD_HTTP_OK (200) + *        as for successful intermediate response this callback is + *       skipped. 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 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 full_response full response from the exchange (for + *        logging, in case of errors) + */ +static void +melt_cb (void *cls, +         unsigned int http_status, +	 enum TALER_ErrorCode ec, +         uint32_t noreveal_index, +         const struct TALER_ExchangePublicKeyP *exchange_pub, +         const json_t *full_response) +{ +  struct RefreshMeltState *rms = cls; + +  rms->rmh = NULL; +  if (rms->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                rms->is->commands[rms->is->ip].label); +    json_dumpf (full_response, stderr, 0); +    TALER_TESTING_interpreter_fail (rms->is); +    return; +  } +  rms->noreveal_index = noreveal_index; +  TALER_TESTING_interpreter_next (rms->is); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param i the interpreter state. + */ +void +refresh_melt_run (void *cls, +                  const struct TALER_TESTING_Command *cmd, +                  struct TALER_TESTING_Interpreter *is) +{ +  struct RefreshMeltState *rms = cls; +  unsigned int num_fresh_coins; +  const struct TALER_TESTING_Command *coin_command; +  const char *melt_fresh_amounts[] = { +    /* with 0.01 withdraw fees (except for 1ct coins), +       this totals up to exactly EUR:3.97, and with +       the 0.03 refresh fee, to EUR:4.0 */ +    "EUR:1", "EUR:1", "EUR:1", "EUR:0.1", "EUR:0.1", "EUR:0.1", +    "EUR:0.1", "EUR:0.1", "EUR:0.1", "EUR:0.1", "EUR:0.1", +    "EUR:0.01", "EUR:0.01", "EUR:0.01", "EUR:0.01", "EUR:0.01", +    "EUR:0.01", NULL}; +  const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; + +  rms->is = is; +  rms->noreveal_index = UINT16_MAX; +  for (num_fresh_coins=0; +       NULL != melt_fresh_amounts[num_fresh_coins]; +       num_fresh_coins++) ; + +  rms->fresh_pks = GNUNET_new_array +    (num_fresh_coins, +     struct TALER_EXCHANGE_DenomPublicKey); +  { +    struct TALER_CoinSpendPrivateKeyP *melt_priv; +    struct TALER_Amount melt_amount; +    struct TALER_Amount fresh_amount; +    struct TALER_DenominationSignature *melt_sig; +    struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; +    unsigned int i; + +    const struct MeltDetails *md = &rms->melted_coin; +    if (NULL == (coin_command +      = TALER_TESTING_interpreter_lookup_command +        (is, md->coin_reference))) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rms->is); +      return;  +    } + +    if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv +      (coin_command, 0, &melt_priv)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rms->is); +      return;     +    } + +    if (GNUNET_OK != +        TALER_string_to_amount (md->amount, +                                &melt_amount)) +    { +      GNUNET_break (0); +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Failed to parse amount `%s' at %u\n", +                  md->amount, +                  is->ip); +      TALER_TESTING_interpreter_fail (rms->is); +      return; +    } +    if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig +      (coin_command, 0, &melt_sig)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rms->is); +      return; +    } +    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; +    } + +    for (i=0;i<num_fresh_coins;i++) +    { +      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); +        TALER_TESTING_interpreter_fail (rms->is); +        return; +      } +      fresh_pk = TALER_TESTING_find_pk +        (TALER_EXCHANGE_get_keys (rms->exchange), &fresh_amount); +      if (NULL == fresh_pk) +      { +        GNUNET_break (0); +        /* Subroutine logs specific error */ +        TALER_TESTING_interpreter_fail (rms->is); +        return; +      } + +      rms->fresh_pks[i] = *fresh_pk; +    } +    rms->refresh_data = TALER_EXCHANGE_refresh_prepare +      (melt_priv, &melt_amount, melt_sig, melt_denom_pub, +       GNUNET_YES, num_fresh_coins, rms->fresh_pks, +       &rms->refresh_data_length); + +    if (NULL == rms->refresh_data) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rms->is); +      return; +    } +    rms->rmh = TALER_EXCHANGE_refresh_melt +      (rms->exchange, rms->refresh_data_length, +       rms->refresh_data, &melt_cb, rms); + +    if (NULL == rms->rmh) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (rms->is); +      return; +    } +  } +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct RefreshMeltState. + * @param cmd the command which is being cleaned up. + */ +void +refresh_melt_cleanup (void *cls, +                      const struct TALER_TESTING_Command *cmd) +{ +  struct RefreshMeltState *rms = cls; + +  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_EXCHANGE_refresh_melt_cancel (rms->rmh); +    rms->rmh = NULL; +  } +  GNUNET_free_non_null (rms->fresh_pks); +  rms->fresh_pks = NULL; +  GNUNET_free_non_null (rms->refresh_data); +  rms->refresh_data = NULL; +  rms->refresh_data_length = 0; +} + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *        to return in case there were multiple generated + *        by the command + * @return #GNUNET_OK on success + */ +static int +refresh_melt_traits (void *cls, +                     void **ret, +                     const char *trait, +                     unsigned int index) +{ +  struct RefreshMeltState *rms = cls; + +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_denom_pub (0, rms->fresh_pks), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +/** + * Create a "refresh melt" command. + * + * @param label command label + * @param exchange connection to the exchange + * @param amount Fixme + * @param coin_reference reference to a command that will provide + *        a coin to refresh + * @param expected_response_code expected HTTP code + */ + +struct TALER_TESTING_Command +TALER_TESTING_cmd_refresh_melt +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *amount, +   const char *coin_reference, +   unsigned int expected_response_code) +{ +  struct RefreshMeltState *rms; +  struct MeltDetails md; +  struct TALER_TESTING_Command cmd; + +  md.coin_reference = coin_reference; +  md.amount = amount; + +  rms = GNUNET_new (struct RefreshMeltState); +  rms->amount = amount; +  rms->melted_coin = md; +  rms->expected_response_code = expected_response_code; +  rms->exchange = exchange; + +  cmd.label = label; +  cmd.cls = rms; +  cmd.run = &refresh_melt_run; +  cmd.cleanup = &refresh_melt_cleanup; +  cmd.traits = &refresh_melt_traits; +   +  return cmd; +} + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *        to return in case there were multiple generated + *        by the command + * @return #GNUNET_OK on success + */ +static int +refresh_reveal_traits (void *cls, +                       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 * 3) + 3 +  struct TALER_TESTING_Trait traits[NUM_TRAITS]; +  unsigned int i; + +  /* Making coin privs traits */ +  for (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 (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 (i=0; i<num_coins; i++) +    traits[(num_coins * 2) + i] +      = TALER_TESTING_make_trait_denom_sig +        (i, &rrs->fresh_coins[i].sig); + +  /* number of fresh coins */ +  traits[(num_coins * 3)] = TALER_TESTING_make_trait_uint +    (0, &rrs->num_fresh_coins); + +  /* whole array of fresh coins */ +  traits[(num_coins * 3) + 1] +    = TALER_TESTING_make_trait_fresh_coins (0, rrs->fresh_coins), + +  /* end of traits */ +  traits[(num_coins * 3) + 2] = TALER_TESTING_trait_end (); + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + +/** + * Create a "refresh reveal" command. + * + * @param label command label + * @param exchange connection to the exchange + * @param melt_reference reference to a "refresh melt" command + * @param expected_response_code expected HTTP response code + * + * @return the "refresh reveal" command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_refresh_reveal +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *melt_reference, +   unsigned int expected_response_code) +{ +  struct RefreshRevealState *rrs; +  struct TALER_TESTING_Command cmd; +   +  rrs = GNUNET_new (struct RefreshRevealState); +  rrs->melt_reference = melt_reference; +  rrs->exchange = exchange; +  rrs->expected_response_code = expected_response_code; + +  cmd.cls = rrs; +  cmd.label = label; +  cmd.run = &refresh_reveal_run; +  cmd.cleanup = &refresh_reveal_cleanup; +  cmd.traits = &refresh_reveal_traits; +   +  return cmd; +} + + +/** + * Create a "refresh link" command. + * + * @param label command label + * @param exchange connection to the exchange + * @param melt_reference reference to a "refresh melt" command + * @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, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *reveal_reference, +   unsigned int expected_response_code) +{ +  struct RefreshLinkState *rrs; +  struct TALER_TESTING_Command cmd; +   +  rrs = GNUNET_new (struct RefreshLinkState); +  rrs->reveal_reference = reveal_reference; +  rrs->exchange = exchange; +  rrs->expected_response_code = expected_response_code; + +  cmd.cls = rrs; +  cmd.label = label; +  cmd.run = &refresh_link_run; +  cmd.cleanup = &refresh_link_cleanup; +   +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_refund.c b/src/exchange-lib/testing_api_cmd_refund.c new file mode 100644 index 00000000..a092ee2d --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_refund.c @@ -0,0 +1,295 @@ +/* +  This file is part of TALER +  Copyright (C) 2014-2018 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as +  published by the Free Software Foundation; either version 3, or +  (at your option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ + +/** + * @file exchange/testing_api_cmd_refund.c + * @brief Implement the /refund test command, plus other + *        corollary commands (?). + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct RefundState +{ +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Amount to be refunded. +   */ +  const char *refund_amount; + +  /** +   * Expected refund fee. +   */ +  const char *refund_fee; + +  /** +   * Reference to any command that can provide a coin to refund. +   */ +  const char *coin_reference; + +  /** +   * Refund transaction identifier.  Left un-initialized in the +   * old test-suite.  What's the best way to init it? +   */ +  uint64_t refund_transaction_id; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Handle to the refund operation. +   */ +  struct TALER_EXCHANGE_RefundHandle *rh; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Check the result for the refund request. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for + *        successful deposit; 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 exchange_pub public key the exchange used for signing @a + *        obj + * @param obj the received JSON reply, should be kept as proof + *        (and, in particular, be forwarded to the customer) + */ +static void +refund_cb (void *cls, +           unsigned int http_status, +	   enum TALER_ErrorCode ec, +           const struct TALER_ExchangePublicKeyP *exchange_pub, +           const json_t *obj) +{ + +  struct RefundState *rs = cls; +  struct TALER_TESTING_Command *refund_cmd; + +  refund_cmd = &rs->is->commands[rs->is->ip]; +  rs->rh = NULL; +  if (rs->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                refund_cmd->label); +    json_dumpf (obj, stderr, 0); +    TALER_TESTING_interpreter_fail (rs->is); +    return; +  } + +  switch (http_status) +  { +  case MHD_HTTP_OK: +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Good /refund status code\n"); +    break; +  default: +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Unmanaged HTTP status code: %u, command: %s\n", +                http_status, refund_cmd->label); +  } + +  TALER_TESTING_interpreter_next (rs->is); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param i the interpreter state. + */ +void +refund_run (void *cls, +            const struct TALER_TESTING_Command *cmd, +            struct TALER_TESTING_Interpreter *is) +{ +  struct RefundState *rs = cls;  +  struct TALER_CoinSpendPrivateKeyP *coin_priv; +  struct TALER_CoinSpendPublicKeyP coin; +  const char *contract_terms; +  struct GNUNET_HashCode h_contract_terms; +  json_t *j_contract_terms; +  struct TALER_Amount refund_fee; +  struct TALER_Amount refund_amount; +  const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv; +  const struct TALER_TESTING_Command *coin_cmd; + +  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", +                rs->refund_amount, +                is->ip, +                cmd->label); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  if (GNUNET_OK != +      TALER_string_to_amount (rs->refund_fee, +                              &refund_fee)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at %u/%s\n", +                rs->refund_fee, +                is->ip, +                cmd->label); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  coin_cmd = TALER_TESTING_interpreter_lookup_command +    (is, rs->coin_reference); + +  if (NULL == coin_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms +    (coin_cmd, 0, &contract_terms)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;  +  } + +  j_contract_terms = json_loads +    (contract_terms, JSON_REJECT_DUPLICATES, NULL); + +  /* Very unlikely to fail */ +  GNUNET_assert (NULL != j_contract_terms); +  GNUNET_assert (GNUNET_OK == TALER_JSON_hash +    (j_contract_terms, &h_contract_terms)); + +  json_decref (j_contract_terms); + +  /* Hunting for a coin .. */ +  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv +    (coin_cmd, 0, &coin_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, +                                      &coin.eddsa_pub); +  if (GNUNET_OK != TALER_TESTING_get_trait_peer_key +    (coin_cmd, 0, &merchant_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return;  +  } + +  rs->rh = TALER_EXCHANGE_refund +    (rs->exchange, &refund_amount, &refund_fee, &h_contract_terms, +     &coin, rs->refund_transaction_id, +     (const struct TALER_MerchantPrivateKeyP *) merchant_priv, +     &refund_cb, rs); + +  GNUNET_assert (NULL != rs->rh); +} + + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +refund_cleanup (void *cls, +                const struct TALER_TESTING_Command *cmd) +{ +  struct RefundState *rs = cls; + +  if (NULL != rs->rh) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                rs->is->ip, +                cmd->label); +    TALER_EXCHANGE_refund_cancel (rs->rh); +    rs->rh = NULL; +  } +  GNUNET_free (rs); +} + +/** + * Create a /refund test command. + * + * @param label command label + * @param expected_response_code expected HTTP status code + * @param refund_amount the amount to ask a refund for + * @param refund_fee expected refund fee + * @param coin_reference reference to a command that can + *        provide a coin to be refunded. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_refund (const char *label, +                          unsigned int expected_response_code, +                          const char *refund_amount, +                          const char *refund_fee, +                          const char *coin_reference) +{ +  struct RefundState *rs; +  struct TALER_TESTING_Command cmd; + +  rs = GNUNET_new (struct RefundState); + +  rs->expected_response_code = expected_response_code; +  rs->refund_amount = refund_amount; +  rs->refund_fee = refund_fee; +  rs->coin_reference = coin_reference; + +  cmd.cls = rs; +  cmd.label = label; +  cmd.run = &refund_run; +  cmd.cleanup = &refund_cleanup; + +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_signal.c b/src/exchange-lib/testing_api_cmd_signal.c new file mode 100644 index 00000000..5b0fa1b9 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_signal.c @@ -0,0 +1,112 @@ +/* +  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 exchange-lib/testing_api_cmd_signal.c + * @brief command(s) to send signals to processes. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct SignalState +{ +  /** +   * The process to send the signal to. +   */ +  struct GNUNET_OS_Process *process; + +  /** +   * The signal to send to the process. +   */ +  int signal; + +}; + +/** + * Run the command. + * + * @param cls closure, typically a #struct SignalState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +signal_run (void *cls, +            const struct TALER_TESTING_Command *cmd, +            struct TALER_TESTING_Interpreter *is) +{ +  struct SignalState *ss = cls; + +  GNUNET_break (0 == GNUNET_OS_process_kill +    (ss->process, ss->signal)); +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Signaling '%d'..\n", +              ss->signal); +  sleep (6); +  TALER_TESTING_interpreter_next (is); +} + + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct SignalState. + * @param cmd the command which is being cleaned up. + */ +void +signal_cleanup (void *cls, +                const struct TALER_TESTING_Command *cmd) +{ +  struct SignalState *ss = cls; + +  GNUNET_free (ss); +} + +/** + * Send a signal to a process. + * + * @param process handle to the process + * @param signal signal to send + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_signal (const char *label, +                          struct GNUNET_OS_Process *process, +                          int signal) +{ +  struct SignalState *ss; +  struct TALER_TESTING_Command cmd; + +  ss = GNUNET_new (struct SignalState); + +  ss->process = process; +  ss->signal = signal; + +  cmd.cls = ss; +  cmd.label = label; +  cmd.run = &signal_run; +  cmd.cleanup = &signal_cleanup; + +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_status.c b/src/exchange-lib/testing_api_cmd_status.c new file mode 100644 index 00000000..c948277b --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_status.c @@ -0,0 +1,240 @@ +/* +  This file is part of TALER +  Copyright (C) 2014-2018 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as +  published by the Free Software Foundation; either version 3, or +  (at your option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ + +/** + * @file exchange/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 "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct StatusState +{ +  /** +   * Label to the command which created the reserve to check, +   * needed to resort the reserve key. +   */ +   const char *reserve_reference; + +  /** +   * Handle to a /reserve/status operation. +   */ +  struct TALER_EXCHANGE_ReserveStatusHandle *rsh; + +  /** +   * Expected reserve balance. +   */ +  const char *expected_balance; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Handle to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; +}; + +/** + * Check exchange returned expected values. + * + * @param cls closure + * @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[in] json original response in JSON format (useful only + *            for diagnostics) + * @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 + */ +void +reserve_status_cb +  (void *cls, +   unsigned int http_status, +   enum TALER_ErrorCode ec, +   const json_t *json, +   const struct TALER_Amount *balance, +   unsigned int history_length, +   const struct TALER_EXCHANGE_ReserveHistory *history) +{ +  struct StatusState *ss = cls; +  struct TALER_Amount eb; +   +  ss->rsh = NULL; +  if (ss->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected HTTP response code: %d\n", +                http_status); +    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\n"); +    TALER_TESTING_interpreter_fail (ss->is); +    return; +  } + +/** + * Fixme: need a way to check if reserve history is consistent. + * Every command which relates to reserve 'x' should be added in + * a linked list of all commands that relate to the same reserve + * 'x'. + * + * API-wise, any command that relates to a reserve should offer a + * method called e.g. "compare_with_history" that takes an element + * of the array returned by "/reserve/status" and checks if that + * element correspond to itself (= the command exposing the check- + * method). + */ + +  TALER_TESTING_interpreter_next (ss->is); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param i the interpreter state. + */ +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; +  struct TALER_ReservePrivateKeyP *reserve_priv; +  struct TALER_ReservePublicKeyP reserve_pub; + +  ss->is = is; +  GNUNET_assert (NULL != ss->reserve_reference); + +  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, +                                            0, +                                            &reserve_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,  +                                      &reserve_pub.eddsa_pub); +  ss->rsh +    = TALER_EXCHANGE_reserve_status (ss->exchange, +                                     &reserve_pub, +                                     &reserve_status_cb, +                                     ss); +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +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_reserve_status_cancel (ss->rsh); +    ss->rsh = NULL; +  } +  GNUNET_free (ss); +} + + +/** + * Create a /reserve/status command. + * + * @param label the command label. + * @param exchange the exchange to connect to. + * @param reserve_reference reference to the reserve to check. + * @param expected_balance balance expected to be at the referenced reserve. + * @param expected_response_code expected HTTP response code. + * + * @return the command to be executed by the interpreter. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_status (const char *label, +                          struct TALER_EXCHANGE_Handle *exchange, +                          const char *reserve_reference, +                          const char *expected_balance, +                          unsigned int expected_response_code) +{ +  struct TALER_TESTING_Command cmd; +  struct StatusState *ss; + +  ss = GNUNET_new (struct StatusState); +  ss->exchange = exchange; +  ss->reserve_reference = reserve_reference; +  ss->expected_balance = expected_balance; +  ss->expected_response_code = expected_response_code; + +  cmd.cls = ss; +  cmd.label = label; +  cmd.run = &status_run; +  cmd.cleanup = &status_cleanup; + +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_track.c b/src/exchange-lib/testing_api_cmd_track.c new file mode 100644 index 00000000..40894872 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_track.c @@ -0,0 +1,819 @@ +/* +  This file is part of TALER +  Copyright (C) 2014-2018 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as +  published by the Free Software Foundation; either version 3, or +  (at your option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ + +/** + * @file exchange/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 "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct TrackTransactionState +{ + +  /** +   * Which #OC_CHECK_BANK_TRANSFER wtid should this match? NULL +   * for none. +   */ +  const char *bank_transfer_reference; + +  /** +   * Wire transfer identifier, set if #MHD_HTTP_OK was the +   * response code. +   */ +  struct TALER_WireTransferIdentifierRawP wtid; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Reference to any operation that can provide a transaction. +   * Tipically a /deposit operation. +   */ +  const char *transaction_reference; + +  /** +   * Index of the coin involved in the transaction. +   */ +  unsigned int coin_index; + +  /** +   * Handle to the deposit wtid request. +   */ +  struct TALER_EXCHANGE_TrackTransactionHandle *tth; + +  /** +   * Handle to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; +}; + +struct TrackTransferState +{ + +  /** +   * Expected amount for this WTID. +   */ +  const char *expected_total_amount; + +  /** +   * Expected fee for this WTID. +   */ +  const char *expected_wire_fee; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Reference to any operation that can provide a WTID. +   */ +  const char *wtid_reference; + +  /** +   * Reference to any operation that can provide wire details. +   */ +  const char *wire_details_reference; + +  /** +   * Reference to any operation that can provide an amount. +   */ +  const char *total_amount_reference; + +  /** +   * Index to the WTID to pick. +   */ +  unsigned int index; + +  /** +   * Handle to the deposit wtid request. +   */ +  struct TALER_EXCHANGE_TrackTransferHandle *tth; + +  /** +   * Handle to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Function called with detailed wire transfer data. + * + * @param cls closure + * @param http_status HTTP status code we got, 0 on exchange + *        protocol violation + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param exchange_pub public key the exchange used for signing + * @param json original json reply (may include signatures, those + *        have then been validated already) + * @param wtid wire transfer identifier used by the exchange, NULL + *        if exchange did not yet execute the transaction + * @param execution_time actual or planned execution time for the + *        wire transfer + * @param coin_contribution contribution to the @a total_amount of + *        the deposited coin (may be NULL) + * @param total_amount total amount of the wire transfer, or NULL + *        if the exchange could not provide any @a wtid (set only + *        if @a http_status is #MHD_HTTP_OK) + */ +static void +deposit_wtid_cb +  (void *cls, +   unsigned int http_status, +   enum TALER_ErrorCode ec, +   const struct TALER_ExchangePublicKeyP *exchange_pub, +   const json_t *json, +   const struct TALER_WireTransferIdentifierRawP *wtid, +   struct GNUNET_TIME_Absolute execution_time, +   const struct TALER_Amount *coin_contribution) +{ +  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 != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                cmd->label); +    json_dumpf (json, stderr, 0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +  switch (http_status) +  { +  case MHD_HTTP_OK: +    tts->wtid = *wtid; +    if (NULL != tts->bank_transfer_reference) +    { +      const struct TALER_TESTING_Command *bank_transfer_cmd;  +      char *ws; + +      ws = GNUNET_STRINGS_data_to_string_alloc (wtid, +                                                sizeof (*wtid)); +      bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command +        (is, tts->bank_transfer_reference); + +      if (NULL == bank_transfer_cmd) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } + +      char *transfer_subject; + +      if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject +        (bank_transfer_cmd, 0, &transfer_subject)) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return;        +      } + +      if (0 != strcmp (ws, transfer_subject)) +      { +        GNUNET_break (0); +        GNUNET_free (ws); +        TALER_TESTING_interpreter_fail (tts->is); +        return; +      } + +      GNUNET_free (ws); +    } +    break; +  case MHD_HTTP_ACCEPTED: +    /* allowed, nothing to check here */ +    break; +  case MHD_HTTP_NOT_FOUND: +    /* allowed, nothing to check here */ +    break; +  default: +    GNUNET_break (0); +    break; +  } +  TALER_TESTING_interpreter_next (tts->is); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /track/transaction one. + * @param is the interpreter state. + */ +void +track_transaction_run (void *cls, +                       const struct TALER_TESTING_Command *cmd, +                       struct TALER_TESTING_Interpreter *is) +{ +  struct TrackTransactionState *tts = cls; +  const struct TALER_TESTING_Command *transaction_cmd; +  struct TALER_CoinSpendPrivateKeyP *coin_priv; +  struct TALER_CoinSpendPublicKeyP coin_pub; +  const char *wire_details; +  const char *contract_terms; +  json_t *j_wire_details; +  json_t *j_contract_terms; +  struct GNUNET_HashCode h_wire_details; +  struct GNUNET_HashCode h_contract_terms; +  const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv; +   +  tts->is = is; +  transaction_cmd = TALER_TESTING_interpreter_lookup_command +    (tts->is, tts->transaction_reference); + +  if (NULL == transaction_cmd) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (tts->is); +    return;   +  } + +  if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv +    (transaction_cmd, tts->coin_index, &coin_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (tts->is); +    return;   +  } + +  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, +                                      &coin_pub.eddsa_pub); + +  /* 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; +  } + +  if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms +    (transaction_cmd, 0, &contract_terms)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (tts->is); +    return; +  } + +  /* Parse them.. */ +  j_wire_details = json_loads +    (wire_details, JSON_REJECT_DUPLICATES, NULL); +  j_contract_terms = json_loads +    (contract_terms, JSON_REJECT_DUPLICATES, NULL); +   +  if ((NULL == j_wire_details) || (NULL == j_contract_terms)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (tts->is); +    return;   +  } + +  /* Should not fail here, json has been parsed already */ +  GNUNET_assert +    ( (GNUNET_OK == TALER_JSON_hash (j_wire_details, +                                      &h_wire_details)) && +      (GNUNET_OK == TALER_JSON_hash (j_contract_terms, +                                      &h_contract_terms)) ); + +  if (GNUNET_OK != TALER_TESTING_get_trait_peer_key +    (transaction_cmd, 0, &merchant_priv)) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (tts->is); +    return;  +  } + +  tts->tth = TALER_EXCHANGE_track_transaction +    (tts->exchange, +     (struct TALER_MerchantPrivateKeyP *) merchant_priv, +     &h_wire_details, +     &h_contract_terms, +     &coin_pub, +     &deposit_wtid_cb, +     tts); + +  GNUNET_assert (NULL != tts->tth); +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +track_transaction_cleanup +  (void *cls, +   const struct TALER_TESTING_Command *cmd) +{ +  struct TrackTransactionState *tts = cls; + +  if (NULL != tts->tth) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                tts->is->ip, +                cmd->label); +    TALER_EXCHANGE_track_transaction_cancel (tts->tth); +    tts->tth = NULL; +  } +  GNUNET_free (tts); +} + + +/** + * Extract information from a command that is useful for other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param selector more detailed information about which object + *                 to return in case there were multiple generated + *                 by the command + * @return #GNUNET_OK on success + */ +static int +track_transaction_traits (void *cls, +                          void **ret, +                          const char *trait, +                          unsigned int index) +{ +  struct TrackTransactionState *tts = cls; + +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_wtid (0, &tts->wtid), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + +/** + * Create a /track/transaction command. + * + * @param label the command label. + * @param exchange the exchange to connect to. + * @param transaction_reference reference to a deposit operation. + * @param coin_index index of the coin involved in the transaction + * @param expected_response_code expected HTTP response code. + * @param bank_transfer_reference which #OC_CHECK_BANK_TRANSFER + *        wtid should this match? NULL +   * for none + * + * @return the command to be executed by the interpreter. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_track_transaction +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *transaction_reference, +   unsigned int coin_index, +   unsigned int expected_response_code, +   const char *bank_transfer_reference) +{ +  struct TALER_TESTING_Command cmd; +  struct TrackTransactionState *tts; + +  tts = GNUNET_new (struct TrackTransactionState); +  tts->exchange = exchange; +  tts->transaction_reference = transaction_reference; +  tts->expected_response_code = expected_response_code; +  tts->bank_transfer_reference = bank_transfer_reference; +  tts->coin_index = coin_index; + +  cmd.cls = tts; +  cmd.label = label; +  cmd.run = &track_transaction_run; +  cmd.cleanup = &track_transaction_cleanup; +  cmd.traits = &track_transaction_traits; + +  return cmd; + +} + +/** + * Cleanup the state. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +void +track_transfer_cleanup (void *cls, +                        const struct TALER_TESTING_Command *cmd) +{ + +  struct TrackTransferState *tts = cls; + +  if (NULL != tts->tth) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                tts->is->ip, +                cmd->label); +    TALER_EXCHANGE_track_transfer_cancel (tts->tth); +    tts->tth = NULL; +  } +  GNUNET_free (tts); + +} + +/** + * Function called with detailed wire transfer data, including all + * of the coin transactions that were combined into the wire + * transfer. + * + * @param cls closure + * @param http_status HTTP status code we got, 0 on exchange + *        protocol violation + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param exchange_pub public key the exchange used for signing + * @param json original json reply (may include signatures, those + *        have then been validated already) + * @param h_wire hash of the wire transfer address the transfer + *        went to, or NULL on error + * @param execution_time time when the exchange claims to have + *        performed the wire transfer + * @param total_amount total amount of the wire transfer, or NULL + *        if the exchange could not provide any @a wtid (set only + *        if @a http_status is #MHD_HTTP_OK) + * @param wire_fee wire fee that was charged by the exchange + * @param details_length length of the @a details array + * @param details array with details about the combined + *        transactions + */ +static void +track_transfer_cb +  (void *cls, +   unsigned int http_status, +   enum TALER_ErrorCode ec, +   const struct TALER_ExchangePublicKeyP *exchange_pub, +   const json_t *json, +   const struct GNUNET_HashCode *h_wire, +   struct GNUNET_TIME_Absolute execution_time, +   const struct TALER_Amount *total_amount, +   const struct TALER_Amount *wire_fee, +   unsigned int details_length, +   const struct TALER_TrackTransferDetails *details) +{ +  struct TrackTransferState *tts = cls; +  struct TALER_TESTING_Interpreter *is = tts->is; +  struct TALER_TESTING_Command *cmd = &is->commands[is->ip]; + +  const struct TALER_TESTING_Command *wtid_cmd; +  struct TALER_Amount expected_amount; + +  tts->tth = NULL; + +  if (tts->expected_response_code != http_status) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Unexpected response code %u to command %s\n", +                http_status, +                cmd->label); +    json_dumpf (json, stderr, 0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  switch (http_status) +  { + +  if ( +    (NULL == tts->expected_total_amount) || +      (NULL == tts->expected_wire_fee)) +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Expected amount and fee not specified, " +                "likely to segfault...\n"); + +  case MHD_HTTP_OK: +    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 (total_amount, +                               &expected_amount)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Total amount missmatch to command %s - " +                  "%s vs %s\n", +                  cmd->label, +                  TALER_amount_to_string (total_amount), +                  TALER_amount_to_string (&expected_amount)); +      json_dumpf (json, 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 (wire_fee, +                               &expected_amount)) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "Wire fee missmatch to command %s\n", +                  cmd->label); +      json_dumpf (json, stderr, 0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } + +    wtid_cmd = TALER_TESTING_interpreter_lookup_command +      (is, tts->wtid_reference); + +    if (NULL == wtid_cmd) +    { +      GNUNET_break (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 *wire_details; +      json_t *j_wire_details; +      struct GNUNET_HashCode h_wire_details; + +      if (NULL == (wire_details_cmd +        = TALER_TESTING_interpreter_lookup_command +          (is, tts->wire_details_reference))) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } + +      if (GNUNET_OK != TALER_TESTING_get_trait_wire_details +        (wire_details_cmd, 0, &wire_details)) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } + +      j_wire_details = json_loads +        (wire_details, JSON_REJECT_DUPLICATES, NULL); + +      GNUNET_assert (NULL != j_wire_details); + +      GNUNET_assert (GNUNET_OK == TALER_JSON_hash +        (j_wire_details, &h_wire_details)); + +      if (0 != memcmp (&h_wire_details, +                       h_wire, +                       sizeof (struct GNUNET_HashCode))) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "Wire hash missmath to command %s\n", +                    cmd->label); +        json_dumpf (json, stderr, 0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } +    } +    if (NULL != tts->total_amount_reference) +    { +      const struct TALER_TESTING_Command *total_amount_cmd; +      const char *total_amount_from_reference_str; +      struct TALER_Amount total_amount_from_reference; + +      if (NULL == (total_amount_cmd +        = TALER_TESTING_interpreter_lookup_command +          (is, tts->total_amount_reference))) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return;       +      } + +      if (GNUNET_OK != TALER_TESTING_get_trait_amount +        (total_amount_cmd, 0, &total_amount_from_reference_str)) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } +       +      GNUNET_assert (GNUNET_OK == TALER_string_to_amount +        (total_amount_from_reference_str, +         &total_amount_from_reference)); + +      if (0 != TALER_amount_cmp (total_amount, +                                 &total_amount_from_reference)) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "Amount missmath to command %s\n", +                    cmd->label); +        json_dumpf (json, stderr, 0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } +    } +  } +  TALER_TESTING_interpreter_next (is); +} + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command to execute, a /track/transfer one. + * @param is the interpreter state. + */ +void +track_transfer_run (void *cls, +                    const struct TALER_TESTING_Command *cmd, +                    struct TALER_TESTING_Interpreter *is) +{ +  /* looking for a wtid to track .. */ + +  struct TrackTransferState *tts = cls; +  struct TALER_WireTransferIdentifierRawP wtid; +  struct TALER_WireTransferIdentifierRawP *wtid_ptr; + +  /* If no reference is given, we'll use a all-zeros +   * 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); + +    if (NULL == wtid_cmd) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (tts->is); +      return; +    } + +    if (GNUNET_OK != TALER_TESTING_get_trait_wtid +      (wtid_cmd, tts->index, &wtid_ptr)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (tts->is); +      return;      +    } +  } +  tts->tth = TALER_EXCHANGE_track_transfer (tts->exchange, +                                            wtid_ptr, +                                            &track_transfer_cb, +                                            tts); + +  GNUNET_assert (NULL != tts->tth); +} + +/** + * Make a /track/transfer command, expecting the transfer + * not being done (yet). + * + * @param label the command label + * @param exchange connection to the exchange + * @param wtid_reference reference to any command which can provide + *        a wtid + * @param index in case there are multiple wtid offered, this + *        parameter selects a particular one + * @param expected_response_code expected HTTP response code + * + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_track_transfer_empty +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *wtid_reference, +   unsigned int index, +   unsigned int expected_response_code) +{ +  struct TrackTransferState *tts; +  struct TALER_TESTING_Command cmd; +   +  tts = GNUNET_new (struct TrackTransferState); + +  tts->wtid_reference = wtid_reference; +  tts->index = index; +  tts->expected_response_code = expected_response_code; +  tts->exchange = exchange; + +  cmd.cls = tts; +  cmd.label = label; +  cmd.run = &track_transfer_run; +  cmd.cleanup = &track_transfer_cleanup; + +  return cmd;  +} + +/** + * Make a /track/transfer command, specifying which amount and + * wire fee are expected. + * + * @param label the command label + * @param exchange connection to the exchange + * @param wtid_reference reference to any command which can provide + *        a wtid + * @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_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, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *wtid_reference, +   unsigned int index, +   unsigned int expected_response_code, +   const char *expected_total_amount, +   const char *expected_wire_fee) +{ +  struct TrackTransferState *tts; +  struct TALER_TESTING_Command cmd; +   +  tts = GNUNET_new (struct TrackTransferState); + +  tts->wtid_reference = wtid_reference; +  tts->index = index; +  tts->expected_response_code = expected_response_code; +  tts->exchange = exchange; +  tts->expected_total_amount = expected_total_amount; +  tts->expected_wire_fee = expected_wire_fee; + +  cmd.cls = tts; +  cmd.label = label; +  cmd.run = &track_transfer_run; +  cmd.cleanup = &track_transfer_cleanup; + +  return cmd;  +} diff --git a/src/exchange-lib/testing_api_cmd_wire.c b/src/exchange-lib/testing_api_cmd_wire.c new file mode 100644 index 00000000..f65daec0 --- /dev/null +++ b/src/exchange-lib/testing_api_cmd_wire.c @@ -0,0 +1,267 @@ +/* +  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 exchange-lib/testing_api_cmd_wire.c + * @brief command for testing /wire. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_testing_lib.h" + +struct WireState +{ + +  /** +   * Handle to the /wire operation. +   */ +  struct TALER_EXCHANGE_WireHandle *wh; + +  /** +   * Which wire-method we expect are offered by the exchange. +   */ +  const char *expected_method; + +  /** +   * Flag indicating if the expected method is actually +   * offered. +   */ +  unsigned int method_found; + +  /** +   * Fee we expect is charged for this wire-transfer method. +   */ +  const char *expected_fee; + +  /** +   * Expected HTTP response code. +   */ +  unsigned int expected_response_code; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Connection to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; +}; + + +/** + * Check all the expected values have been returned by /wire. + * + * @param cls closure + * @param wire_method name of the wire method (i.e. "sepa") + * @param fees fee structure for this method + */ +static void +check_method_and_fee_cb +  (void *cls, +   const char *wire_method, +   const struct TALER_EXCHANGE_WireAggregateFees *fees); + +/** + * Callbacks called with the result(s) of a wire format inquiry + * request to the exchange. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) + *                    for successful 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 obj the received JSON reply, if successful this should + *            be the wire format details as provided by /wire. + */ +static void +wire_cb (void *cls, +         unsigned int http_status, +	 enum TALER_ErrorCode ec, +         const json_t *obj) +{ +  struct WireState *ws = cls; +  struct TALER_TESTING_Command *cmd +    = &ws->is->commands[ws->is->ip]; + +  /** +   * The handle has been free'd by GNUnet curl-lib. FIXME: +   * shouldn't GNUnet nullify it once it frees it? +   */ +  ws->wh = NULL; +  if (ws->expected_response_code != http_status) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (ws->is); +    return; +  } + +  if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees +       (&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub, +        obj, +        // will check synchronously. +        &check_method_and_fee_cb, +        ws)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Wire fee extraction in command %s failed\n", +                cmd->label); +    json_dumpf (obj, stderr, 0); +    TALER_TESTING_interpreter_fail (ws->is); +    return; +  } + +  if (ws->method_found != GNUNET_OK) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "/wire does not offer method '%s'\n", +                ws->expected_method); +    TALER_TESTING_interpreter_fail (ws->is); +    return; +  } +  TALER_TESTING_interpreter_next (ws->is); +} + +/** + * Check all the expected values have been returned by /wire. + * + * @param cls closure + * @param wire_method name of the wire method (i.e. "sepa") + * @param fees fee structure for this method + */ +static void +check_method_and_fee_cb +  (void *cls, +   const char *wire_method, +   const struct TALER_EXCHANGE_WireAggregateFees *fees) +{ +  struct WireState *ws = cls; +  struct TALER_TESTING_Command *cmd +    = &ws->is->commands[ws->is->ip]; // ugly? +  struct TALER_Amount expected_fee; + +  if (0 == strcmp (ws->expected_method, wire_method)) +    ws->method_found = GNUNET_OK; + +  if ( ws->expected_fee && (ws->method_found == GNUNET_OK) ) +  { +    GNUNET_assert (GNUNET_OK == TALER_string_to_amount +                     (ws->expected_fee, +                      &expected_fee)); +    while (NULL != fees) +    { +      if (0 != TALER_amount_cmp (&fees->wire_fee, +                                 &expected_fee)) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "Wire fee missmatch to command %s\n", +                    cmd->label); +        TALER_TESTING_interpreter_fail (ws->is); +        return; +      } +      fees = fees->next; +    } +  } +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param i the interpreter state. + */ +void +wire_run (void *cls, +          const struct TALER_TESTING_Command *cmd, +          struct TALER_TESTING_Interpreter *i) +{ +  struct WireState *ws = cls; +  ws->is = i; +  ws->wh = TALER_EXCHANGE_wire (ws->exchange, +                                &wire_cb, +                                ws); +} + + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +wire_cleanup (void *cls, +              const struct TALER_TESTING_Command *cmd) +{ +  struct WireState *ws = cls; + +  if (NULL != ws->wh) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %u (%s) did not complete\n", +                ws->is->ip, +                cmd->label); +    TALER_EXCHANGE_wire_cancel (ws->wh); +    ws->wh = NULL; +  } +  GNUNET_free (ws); +} + +/** + * Create a /wire command. + * + * @param label the command label. + * @param exchange the exchange to connect to. + * @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 to be executed by the interpreter. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_wire (const char *label, +                        struct TALER_EXCHANGE_Handle *exchange, +                        const char *expected_method, +                        const char *expected_fee, +                        unsigned int expected_response_code) +{ +  struct TALER_TESTING_Command cmd; +  struct WireState *ws; + +  ws = GNUNET_new (struct WireState); +  ws->exchange = exchange; +  ws->expected_method = expected_method; +  ws->expected_fee = expected_fee; +  ws->expected_response_code = expected_response_code; + +  cmd.cls = ws; +  cmd.label = label; +  cmd.run = &wire_run; +  cmd.cleanup = &wire_cleanup; + +  return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_withdraw.c b/src/exchange-lib/testing_api_cmd_withdraw.c index 642722b4..eb3bc8a6 100644 --- a/src/exchange-lib/testing_api_cmd_withdraw.c +++ b/src/exchange-lib/testing_api_cmd_withdraw.c @@ -2,16 +2,18 @@    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 +  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/>  */  /** @@ -85,14 +87,17 @@ struct WithdrawState  /** - * Function called upon completion of our /reserve/withdraw request. + * Function called upon completion of our /reserve/withdraw + * request.   *   * @param cls closure with the withdraw 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 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 sig signature over the coin, NULL on error - * @param full_response full response from the exchange (for logging, in case of errors) + * @param full_response full response from the exchange (for + *        logging, in case of errors)   */  static void  reserve_withdraw_cb (void *cls, @@ -164,9 +169,8 @@ withdraw_run (void *cls,    struct TALER_ReservePrivateKeyP *rp;    const struct TALER_TESTING_Command *create_reserve; -  create_reserve -    = TALER_TESTING_interpreter_lookup_command (is, -                                                ws->reserve_reference); +  create_reserve = TALER_TESTING_interpreter_lookup_command +    (is, ws->reserve_reference);    if (NULL == create_reserve)    {      GNUNET_break (0); @@ -175,7 +179,7 @@ withdraw_run (void *cls,    }    if (GNUNET_OK !=        TALER_TESTING_get_trait_reserve_priv (create_reserve, -                                            NULL, +                                            0,                                              &rp))    {      GNUNET_break (0); @@ -245,38 +249,72 @@ static int  withdraw_traits (void *cls,                   void **ret,                   const char *trait, -                 const char *selector) +                 unsigned int index)  {    struct WithdrawState *ws = cls; +  const struct TALER_TESTING_Command *reserve_cmd; +  struct TALER_ReservePrivateKeyP *reserve_priv; + +  /* 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;   +  } +    struct TALER_TESTING_Trait traits[] = { -    TALER_TESTING_make_trait_coin_priv (NULL /* only one coin */, +    TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,                                          &ws->ps.coin_priv), -    TALER_TESTING_make_trait_blinding_key (NULL /* only one coin */, +    TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,                                             &ws->ps.blinding_key), -    TALER_TESTING_make_trait_denom_pub (NULL /* only one coin */, +    TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,                                          ws->pk), -    TALER_TESTING_make_trait_denom_sig (NULL /* only one coin */, +    TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,                                          &ws->sig), +    TALER_TESTING_make_trait_reserve_priv (0, +                                           reserve_priv),      TALER_TESTING_trait_end ()    };    return TALER_TESTING_get_trait (traits,                                    ret,                                    trait, -                                  selector); +                                  index);  }  /** - * Create withdraw command. + * Create a withdraw command.   * + * @param label command label, used by other commands to + *        reference this. + * @param exchange handle to the exchange. + * @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, -                                   struct TALER_EXCHANGE_Handle *exchange, -                                   const char *reserve_reference, -                                   const char *amount, -                                   unsigned int expected_response_code) +TALER_TESTING_cmd_withdraw_amount +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *reserve_reference, +   const char *amount, +   unsigned int expected_response_code)  {    struct TALER_TESTING_Command cmd;    struct WithdrawState *ws; @@ -294,8 +332,9 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,                  label);      GNUNET_assert (0);    } -  ws->pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange), -                                  &ws->amount); +  ws->pk = TALER_TESTING_find_pk +    (TALER_EXCHANGE_get_keys (exchange), +     &ws->amount);    if (NULL == ws->pk)    {      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -319,11 +358,12 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,   *   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_withdraw_denomination (const char *label, -                                         struct TALER_EXCHANGE_Handle *exchange, -                                         const char *reserve_reference, -                                         const struct TALER_EXCHANGE_DenomPublicKey *dk, -                                         unsigned int expected_response_code) +TALER_TESTING_cmd_withdraw_denomination +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *reserve_reference, +   const struct TALER_EXCHANGE_DenomPublicKey *dk, +   unsigned int expected_response_code)  {    struct TALER_TESTING_Command cmd;    struct WithdrawState *ws; @@ -349,7 +389,4 @@ TALER_TESTING_cmd_withdraw_denomination (const char *label,  } - - -  /* end of testing_api_cmd_withdraw.c */ diff --git a/src/exchange-lib/testing_api_helpers.c b/src/exchange-lib/testing_api_helpers.c index 1721b3e9..a6e421e1 100644 --- a/src/exchange-lib/testing_api_helpers.c +++ b/src/exchange-lib/testing_api_helpers.c @@ -1,4 +1,3 @@ -  /*    This file is part of TALER    Copyright (C) 2018 Taler Systems SA @@ -31,6 +30,8 @@  /**   * Remove files from previous runs + * + * @param config_name configuration filename.   */  void  TALER_TESTING_cleanup_files (const char *config_name) @@ -65,7 +66,7 @@ TALER_TESTING_cleanup_files (const char *config_name)  /**   * Prepare launching an exchange.  Checks that the configured   * port is available, runs taler-exchange-keyup, - * taler-auditor-sign and taler-exchange-dbinit.  Does not + * taler-auditor-sign and taler-exchange-dbinit.  Does NOT   * launch the exchange process itself.   *   * @param config_filename configuration file to use @@ -79,42 +80,16 @@ TALER_TESTING_prepare_exchange (const char *config_filename)    enum GNUNET_OS_ProcessStatusType type;    unsigned long code;    struct GNUNET_CONFIGURATION_Handle *cfg; -  unsigned long long port; - -  cfg = GNUNET_CONFIGURATION_create (); -  if (GNUNET_OK != -      GNUNET_CONFIGURATION_load (cfg, -                                 config_filename)) -    return GNUNET_NO; -  if (GNUNET_OK != -      GNUNET_CONFIGURATION_get_value_number (cfg, -                                             "exchange", -                                             "PORT", -                                             &port)) -  { -    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -                               "exchange", -                               "PORT"); -    GNUNET_CONFIGURATION_destroy (cfg); -    return GNUNET_NO; -  } -  GNUNET_CONFIGURATION_destroy (cfg); -  if (GNUNET_OK != -      GNUNET_NETWORK_test_port_free (IPPROTO_TCP, -				     (uint16_t) port)) -  { -    fprintf (stderr, -             "Required port %llu not available, skipping.\n", -	     port); -    return GNUNET_NO; -  } +  char *test_home_dir; +  char *signed_keys_out; +  char *exchange_master_pub;    proc = GNUNET_OS_start_process (GNUNET_NO,                                    GNUNET_OS_INHERIT_STD_ALL,                                    NULL, NULL, NULL,                                    "taler-exchange-keyup",                                    "taler-exchange-keyup", -                                  "-c", "test_exchange_api.conf", +                                  "-c", config_filename,                                    "-o", "auditor.in",                                    NULL);    if (NULL == proc) @@ -126,21 +101,58 @@ TALER_TESTING_prepare_exchange (const char *config_filename)    GNUNET_OS_process_wait (proc);    GNUNET_OS_process_destroy (proc); +  cfg = GNUNET_CONFIGURATION_create (); +  if (GNUNET_OK != GNUNET_CONFIGURATION_load +    (cfg, config_filename)) +    return GNUNET_SYSERR; + +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_string (cfg, +                                             "paths", +                                             "TALER_TEST_HOME", +                                             &test_home_dir)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "paths", +                               "TALER_TEST_HOME"); +    GNUNET_CONFIGURATION_destroy (cfg); +    return GNUNET_SYSERR; +  } + +  GNUNET_asprintf (&signed_keys_out, +                   "%s/.local/share/taler/auditors/auditor.out", +                   test_home_dir); + +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_string (cfg, +                                             "exchange", +                                             "MASTER_PUBLIC_KEY", +                                             &exchange_master_pub)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "exchange", +                               "MASTER_PUBLIC_KEY"); +    GNUNET_CONFIGURATION_destroy (cfg); +    return GNUNET_SYSERR; +  } + +  GNUNET_CONFIGURATION_destroy (cfg); +    proc = GNUNET_OS_start_process (GNUNET_NO,                                    GNUNET_OS_INHERIT_STD_ALL,                                    NULL, NULL, NULL,                                    "taler-auditor-sign",                                    "taler-auditor-sign", -                                  "-c", "test_exchange_api.conf", +                                  "-c", config_filename,                                    "-u", "http://auditor/", -                                  "-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG", +                                  "-m", exchange_master_pub,                                    "-r", "auditor.in", -                                  "-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out", +                                  "-o", signed_keys_out,                                    NULL);    if (NULL == proc)    {      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -		"Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); +		"Failed to run `taler-auditor-sign`, is your PATH correct?\n");      return GNUNET_NO;    }    GNUNET_OS_process_wait (proc); @@ -151,7 +163,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename)                                    NULL, NULL, NULL,                                    "taler-exchange-dbinit",                                    "taler-exchange-dbinit", -                                  "-c", "test_exchange_api.conf", +                                  "-c", config_filename,                                    "-r",                                    NULL);    if (NULL == proc) @@ -245,22 +257,60 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,   * Initialize scheduler loop and curl context for the testcase   * including starting and stopping the exchange using the given   * configuration file. + * + * @param main_cb routine containing all the commands to run. + * @param main_cb_cls closure for @a main_cb, typically NULL. + * @param config_file configuration file for the test-suite. + * + * @return FIXME: depends on what TALER_TESTING_setup returns.   */  int  TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,                                     void *main_cb_cls, -                                   const char *config_file) +                                   const char *config_filename)  {    int result;    unsigned int iter;    struct GNUNET_OS_Process *exchanged; +  struct GNUNET_CONFIGURATION_Handle *cfg; +  unsigned long long port; + +  cfg = GNUNET_CONFIGURATION_create (); +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_load (cfg, +                                 config_filename)) +    return GNUNET_NO; +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_number (cfg, +                                             "exchange", +                                             "PORT", +                                             &port)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "exchange", +                               "PORT"); +    GNUNET_CONFIGURATION_destroy (cfg); +    return GNUNET_NO; +  } + +  GNUNET_CONFIGURATION_destroy (cfg); +  if (GNUNET_OK != +      GNUNET_NETWORK_test_port_free (IPPROTO_TCP, +				     (uint16_t) port)) +  { +    fprintf (stderr, +             "Required port %llu not available, skipping.\n", +	     port); +    return GNUNET_NO; +  } +    exchanged = GNUNET_OS_start_process (GNUNET_NO,                                         GNUNET_OS_INHERIT_STD_ALL,                                         NULL, NULL, NULL,                                         "taler-exchange-httpd",                                         "taler-exchange-httpd", -                                       "-c", config_file, +                                       "-c", config_filename,                                         "-i",                                         NULL);    /* give child time to start and bind against the socket */ @@ -287,7 +337,9 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,    fprintf (stderr, "\n");    result = TALER_TESTING_setup (main_cb, -                                main_cb_cls); +                                main_cb_cls, +                                config_filename, +                                exchanged);    GNUNET_break (0 ==                  GNUNET_OS_process_kill (exchanged,                                          SIGTERM)); @@ -325,10 +377,32 @@ TALER_TESTING_url_port_free (const char *url)    return GNUNET_OK;  } +/** + * Allocate and return a piece of wire-details.  Mostly, it adds + * the bank_url to the JSON. + * + * @param template the wire-details template. + * @param bank_url the bank_url + * + * @return the filled out and stringified wire-details.  To + *         be manually free'd. + */ +char * +TALER_TESTING_make_wire_details (const char *template, +                                 const char *bank_url) +{ +  json_t *jtemplate; + +  GNUNET_assert (NULL != (jtemplate = json_loads +    (template, JSON_REJECT_DUPLICATES, NULL))); +  GNUNET_assert (0 == json_object_set +    (jtemplate, "bank_url", json_string (bank_url))); +  return json_dumps (jtemplate, JSON_COMPACT); +}  /**   * Prepare launching a fakebank.  Check that the configuration - * file has the right option, and that the port is avaiable. + * file has the right option, and that the port is available.   * If everything is OK, return the configured URL of the fakebank.   *   * @param config_filename configuration file to use diff --git a/src/exchange-lib/testing_api_loop.c b/src/exchange-lib/testing_api_loop.c index 8c236498..f22c99d8 100644 --- a/src/exchange-lib/testing_api_loop.c +++ b/src/exchange-lib/testing_api_loop.c @@ -2,18 +2,21 @@    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 +  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 exchange-lib/testing_api_loop.c   * @brief main interpreter loop for testcases @@ -28,73 +31,12 @@  #include "taler_testing_lib.h"  #include "taler_fakebank_lib.h" - -/** - * Global state of the interpreter, used by a command - * to access information about other commands. - */ -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; - -  /** -   * ID of task called whenever we get a SIGCHILD. -   * Used for #TALER_TESTING_wait_for_sigchld(). -   */ -  struct GNUNET_SCHEDULER_Task *child_death_task; - -  /** -   * Main execution context for the main loop. -   */ -  struct GNUNET_CURL_Context *ctx; - -  /** -   * Context for running the CURL event loop. -   */ -  struct GNUNET_CURL_RescheduleContext *rc; - -  /** -   * Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used. -   * Otherwise NULL. -   */ -  struct TALER_FAKEBANK_Handle *fakebank; - -  /** -   * Task run on timeout. -   */ -  struct GNUNET_SCHEDULER_Task *timeout_task; - -  /** -   * Instruction pointer.  Tells #interpreter_run() which -   * instruction to run next. -   */ -  unsigned int ip; - -  /** -   * Result of the testcases, #GNUNET_OK on success -   */ -  int result; - -}; - -  /**   * 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.   * @@ -103,8 +45,9 @@ static struct GNUNET_DISK_PipeHandle *sigpipe;   * @return NULL if command was not found   */  const struct TALER_TESTING_Command * -TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is, -                                          const char *label) +TALER_TESTING_interpreter_lookup_command +  (struct TALER_TESTING_Interpreter *is, +   const char *label)  {    const struct TALER_TESTING_Command *cmd; @@ -131,23 +74,35 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,   * 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) +TALER_TESTING_interpreter_get_fakebank +  (struct TALER_TESTING_Interpreter *is)  {    return is->fakebank;  } +/** + * 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_run_with_fakebank +  (struct TALER_TESTING_Interpreter *is, +   struct TALER_TESTING_Command *commands, +   const char *bank_url)  {    const char *port;    long pnum; @@ -159,7 +114,7 @@ TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,    else      pnum = strtol (port + 1, NULL, 10);    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -              "Staring Fakebank on port %u (%s)\n", +              "Starting Fakebank on port %u (%s)\n",                (unsigned int) pnum,                bank_url);    is->fakebank = TALER_FAKEBANK_start ((uint16_t) pnum); @@ -192,8 +147,7 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)    if (GNUNET_SYSERR == i->result)      return; /* ignore, we already failed! */    i->ip++; -  i->task = GNUNET_SCHEDULER_add_now (&interpreter_run, -                                      i); +  i->task = GNUNET_SCHEDULER_add_now (&interpreter_run, i);  } @@ -201,21 +155,25 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)   * Current command failed, clean up and fail the test case.   */  void -TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *i) +TALER_TESTING_interpreter_fail +  (struct TALER_TESTING_Interpreter *is)  { -  i->result = GNUNET_SYSERR; +  // FIXME: disconnect from the exchange. +  is->result = GNUNET_SYSERR; +  // this cleans up too.    GNUNET_SCHEDULER_shutdown ();  }  /**   * Create command array terminator. + * + * @return a end-command.   */  struct TALER_TESTING_Command  TALER_TESTING_cmd_end (void)  {    static struct TALER_TESTING_Command cmd; -    return cmd;  } @@ -224,7 +182,8 @@ TALER_TESTING_cmd_end (void)   * 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]; @@ -278,6 +237,10 @@ do_shutdown (void *cls)    for (unsigned int j=0;NULL != (cmd = &is->commands[j])->label;j++)      cmd->cleanup (cmd->cls,                    cmd); +  if (NULL != is->exchange) +  { +    TALER_EXCHANGE_disconnect (is->exchange); +  }    if (NULL != is->task)    {      GNUNET_SCHEDULER_cancel (is->task); @@ -351,7 +314,7 @@ maint_child_death (void *cls)                                         sizeof (c)));    if (GNUNET_OK !=        TALER_TESTING_get_trait_process (cmd, -                                       NULL, +                                       0,                                         &processp))    {      GNUNET_break (0); @@ -361,6 +324,14 @@ maint_child_death (void *cls)    GNUNET_OS_process_wait (*processp);    GNUNET_OS_process_destroy (*processp);    *processp = NULL; + +  if (GNUNET_OK == is->reload_keys) +  { +    GNUNET_break (0 == GNUNET_OS_process_kill +    (is->exchanged, SIGUSR1)); +    sleep (5); /* make sure signal was received and processed */ +  } +    TALER_TESTING_interpreter_next (is);  } @@ -372,7 +343,8 @@ maint_child_death (void *cls)   * with the next command.   */  void -TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is) +TALER_TESTING_wait_for_sigchld +  (struct TALER_TESTING_Interpreter *is)  {    const struct GNUNET_DISK_FileHandle *pr; @@ -384,42 +356,66 @@ TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)                                        pr,                                        &maint_child_death,                                        is); -  } +/** + * Run the testsuite.  FIXME: explain why the commands are + * copied into the state. + * + * @param is the interpreter state + * @param commands the list of command to execute + */  void  TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,                     struct TALER_TESTING_Command *commands)  {    unsigned int i; - +  /* 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 (GNUNET_TIME_relative_multiply -                                    (GNUNET_TIME_UNIT_SECONDS, 300), -                                    &do_timeout, -                                    is); -  GNUNET_SCHEDULER_add_shutdown (&do_shutdown, -                                 is); -  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, -                                      is); +  is->timeout_task = GNUNET_SCHEDULER_add_delayed +    (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300), +     &do_timeout, is); +  GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); +  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is);  } +/** + * Information used by the wrapper around the main + * "run" method. + */  struct MainContext  { +  /** +   * Main "run" method. +   */    TALER_TESTING_Main main_cb; +  /** +   * Closure for "run". +   */    void *main_cb_cls; +  /** +   * Interpreter state. +   */    struct TALER_TESTING_Interpreter *is; +  /** +   * Configuration filename.  The wrapper uses it to fetch +   * the exchange port number; We could have passed the port +   * number here, but having the config filename seems more +   * generic. +   */ +  const char *config_filename; +  }; @@ -433,58 +429,170 @@ sighandler_child_death ()    static char c;    int old_errno = errno;	/* back-up errno */ -  GNUNET_break (1 == -		GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle -					(sigpipe, -                                         GNUNET_DISK_PIPE_END_WRITE), -					&c, sizeof (c))); +  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 */  } +/** + * Called once a connection to the exchange has been + * established. + * + * @param cls closure, typically, the "run" method containing + *        all the commands to be run, and a closure for it. + * @param keys the exchange's keys. + * @param compat protocol compatibility information. + */ +void +cert_cb (void *cls, +         const struct TALER_EXCHANGE_Keys *keys, +	 enum TALER_EXCHANGE_VersionCompatibility compat) +{ +  struct MainContext *main_ctx = cls; + +  if (NULL == keys) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Got NULL response for /keys\n"); +   +  } + +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Got %d DK from /keys\n", +              keys->num_denom_keys); + +  main_ctx->is->key_generation++; +  main_ctx->is->keys = keys; +   +  /* /keys has been called for some reason and +   * the interpreter is already running. */ +  if (GNUNET_YES == main_ctx->is->working) +    return; + +  main_ctx->is->working = GNUNET_YES; + +  /* Very first start of tests, call "run()" */ +  if (1 == main_ctx->is->key_generation) +  { +    main_ctx->main_cb (main_ctx->main_cb_cls, +                       main_ctx->is); +    return; +  } + +  /* Tests already started, just trigger the +   * next command. */ +  GNUNET_SCHEDULER_add_now (&interpreter_run, +                            main_ctx->is); +} + + +/** + * 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  main_wrapper (void *cls)  {    struct MainContext *main_ctx = cls;    struct TALER_TESTING_Interpreter *is = main_ctx->is; +  struct GNUNET_CONFIGURATION_Handle *cfg; +  char *exchange_url; +  long long unsigned int exchange_port; -  is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, -                              &is->rc); +  cfg = GNUNET_CONFIGURATION_create (); +  if (GNUNET_OK != GNUNET_CONFIGURATION_load +    (cfg, main_ctx->config_filename)) +    return; + +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_number (cfg, +                                             "exchange", +                                             "PORT", +                                             &exchange_port)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "exchange", +                               "PORT"); +    GNUNET_CONFIGURATION_destroy (cfg); +    return; +  } +  GNUNET_asprintf (&exchange_url, +                   "http://localhost:%llu/", +                   exchange_port); + +  is->ctx = GNUNET_CURL_init +    (&GNUNET_CURL_gnunet_scheduler_reschedule, &is->rc);    GNUNET_assert (NULL != is->ctx);    is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx); -  main_ctx->main_cb (main_ctx->main_cb_cls, -                     main_ctx->is); + +  GNUNET_assert ( NULL != +    (is->exchange = TALER_EXCHANGE_connect (is->ctx, +                                            exchange_url, +                                            cert_cb, +                                            main_ctx)) ); +  GNUNET_free (exchange_url); +  GNUNET_CONFIGURATION_destroy (cfg); +  }  /** - * Initialize scheduler loop and curl context for the testcase. + * Install signal handlers plus schedules the main wrapper + * around the "run" method. + * + * @param main_cb the "run" method which coontains all the + *        commands. + * @param main_cb_cls a closure for "run", typically NULL. + * @param config_filename configuration filename. + * @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.. + * + * @return FIXME: not sure what 'is.result' is at this stage.   */  int  TALER_TESTING_setup (TALER_TESTING_Main main_cb, -                     void *main_cb_cls) +                     void *main_cb_cls, +                     const char *config_filename, +                     struct GNUNET_OS_Process *exchanged)  {    struct TALER_TESTING_Interpreter is;    struct MainContext main_ctx = {      .main_cb = main_cb,      .main_cb_cls = main_cb_cls, -    .is = &is +    /* needed to init the curl ctx */ +    .is = &is, +    /* needed to read values like exchange port +     * number and construct the exchange url.  The +     * port number _could_ have been passed here, but +     * we prefer to stay "general" as other values might +     * need to be passed around in the future. */ +    .config_filename = config_filename    };    struct GNUNET_SIGNAL_Context *shc_chld; - +  /* zero-ing the state */    memset (&is,            0,            sizeof (is)); +  is.exchanged = exchanged;    sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,                                GNUNET_NO, GNUNET_NO);    GNUNET_assert (NULL != sigpipe); -  shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, -                                            &sighandler_child_death); +  shc_chld = GNUNET_SIGNAL_handler_install +    (GNUNET_SIGCHLD, &sighandler_child_death);    GNUNET_SCHEDULER_run (&main_wrapper,                          &main_ctx);    GNUNET_SIGNAL_handler_uninstall (shc_chld);    GNUNET_DISK_pipe_close (sigpipe);    sigpipe = NULL; + +  /*FIXME: ?? */    return is.result;  } diff --git a/src/exchange-lib/testing_api_trait_blinding_key.c b/src/exchange-lib/testing_api_trait_blinding_key.c index d23fd93b..c9415cae 100644 --- a/src/exchange-lib/testing_api_trait_blinding_key.c +++ b/src/exchange-lib/testing_api_trait_blinding_key.c @@ -40,22 +40,22 @@   */  int  TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd, -                                      const char *selector, +                                      unsigned int index,                                        struct TALER_DenominationBlindingKeyP **blinding_key)  {    return cmd->traits (cmd->cls,                        (void **) blinding_key,                        TALER_TESTING_TRAIT_BLINDING_KEY, -                      selector); +                      index);  }  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_blinding_key (const char *selector, +TALER_TESTING_make_trait_blinding_key (unsigned int index,                                         const struct TALER_DenominationBlindingKeyP *blinding_key)  {    struct TALER_TESTING_Trait ret = { -    .selector = selector, +    .index = index,      .trait_name = TALER_TESTING_TRAIT_BLINDING_KEY,      .ptr = (const void *) blinding_key    }; diff --git a/src/exchange-lib/testing_api_trait_coin_priv.c b/src/exchange-lib/testing_api_trait_coin_priv.c index 6ec4170e..522f7285 100644 --- a/src/exchange-lib/testing_api_trait_coin_priv.c +++ b/src/exchange-lib/testing_api_trait_coin_priv.c @@ -2,18 +2,21 @@    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 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. +  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 +  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 exchange-lib/testing_api_trait_coin_priv.c   * @brief main interpreter loop for testcases @@ -34,28 +37,31 @@   * Obtain a coin private key from a @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer + * @param selector which coin to pick if @a cmd has multiple on + *        offer   * @param coin_priv[out] 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, -                                   const char *selector, -                                   struct TALER_CoinSpendPrivateKeyP **coin_priv) +TALER_TESTING_get_trait_coin_priv +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct TALER_CoinSpendPrivateKeyP **coin_priv)  {    return cmd->traits (cmd->cls,                        (void **) coin_priv,                        TALER_TESTING_TRAIT_COIN_PRIVATE_KEY, -                      selector); +                      index);  }  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_coin_priv (const char *selector, -                                    const struct TALER_CoinSpendPrivateKeyP *coin_priv) +TALER_TESTING_make_trait_coin_priv +  (unsigned int index, +   const struct TALER_CoinSpendPrivateKeyP *coin_priv)  {    struct TALER_TESTING_Trait ret = { -    .selector = selector, +    .index = index,      .trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,      .ptr = (const void *) coin_priv    }; diff --git a/src/exchange-lib/testing_api_trait_denom_pub.c b/src/exchange-lib/testing_api_trait_denom_pub.c index 3113818b..22d9a346 100644 --- a/src/exchange-lib/testing_api_trait_denom_pub.c +++ b/src/exchange-lib/testing_api_trait_denom_pub.c @@ -2,16 +2,18 @@    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 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. +  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 +  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/>  */  /** @@ -34,28 +36,39 @@   * Obtain a denomination public key from a @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer + * @param selector which coin to pick if @a cmd has multiple on + *        offer   * @param denom_pub[out] set to the blinding key of the coin   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd, -                                   const char *selector, -                                   struct TALER_EXCHANGE_DenomPublicKey **denom_pub) +TALER_TESTING_get_trait_denom_pub +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct TALER_EXCHANGE_DenomPublicKey **denom_pub)  {    return cmd->traits (cmd->cls,                        (void **) denom_pub,                        TALER_TESTING_TRAIT_DENOM_PUB, -                      selector); +                      index);  } +/** + * Make a trait for a denomination public key. + * + * @param selector in case the trait provides multiple + *        objects, this parameter extracts a particular one. + * @param denom_pub pointer to the data to be returned from + *        this trait + */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_denom_pub (const char *selector, -                                    const struct TALER_EXCHANGE_DenomPublicKey *denom_pub) +TALER_TESTING_make_trait_denom_pub +  (unsigned int index, +   const struct TALER_EXCHANGE_DenomPublicKey *denom_pub)  {    struct TALER_TESTING_Trait ret = { -    .selector = selector, +    .index = index,      .trait_name = TALER_TESTING_TRAIT_DENOM_PUB,      .ptr = (const void *) denom_pub    }; @@ -63,5 +76,4 @@ TALER_TESTING_make_trait_denom_pub (const char *selector,    return ret;  } -  /* end of testing_api_trait_denom_pub.c */ diff --git a/src/exchange-lib/testing_api_trait_denom_sig.c b/src/exchange-lib/testing_api_trait_denom_sig.c index 66785c7f..9578277c 100644 --- a/src/exchange-lib/testing_api_trait_denom_sig.c +++ b/src/exchange-lib/testing_api_trait_denom_sig.c @@ -40,22 +40,22 @@   */  int  TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd, -                                   const char *selector, +                                   unsigned int index,                                     struct TALER_DenominationSignature **denom_sig)  {    return cmd->traits (cmd->cls,                        (void **) denom_sig,                        TALER_TESTING_TRAIT_DENOM_SIG, -                      selector); +                      index);  }  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_denom_sig (const char *selector, +TALER_TESTING_make_trait_denom_sig (unsigned int index,                                      const struct TALER_DenominationSignature *denom_sig)  {    struct TALER_TESTING_Trait ret = { -    .selector = selector, +    .index = index,      .trait_name = TALER_TESTING_TRAIT_DENOM_SIG,      .ptr = (const void *) denom_sig    }; diff --git a/src/exchange-lib/testing_api_trait_fresh_coin.c b/src/exchange-lib/testing_api_trait_fresh_coin.c new file mode 100644 index 00000000..985591ba --- /dev/null +++ b/src/exchange-lib/testing_api_trait_fresh_coin.c @@ -0,0 +1,74 @@ +/* +  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 exchange-lib/testing_api_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 "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + +#define TALER_TESTING_TRAIT_FRESH_COINS "fresh-coins" + +/** + * Obtain a "number" value from @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param fresh_coins[out] will point to array of fresh coins + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_fresh_coins +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct FreshCoin **fresh_coins) +{ +  return cmd->traits (cmd->cls, +                      (void **) fresh_coins, +                      TALER_TESTING_TRAIT_FRESH_COINS, +                      index); +} + +/** + * @param selector associate the object with this "tag" + * @param fresh_coins the array of fresh coins to offer + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_fresh_coins +  (unsigned int index, +   struct FreshCoin *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/exchange-lib/testing_api_trait_key_peer.c b/src/exchange-lib/testing_api_trait_key_peer.c new file mode 100644 index 00000000..d4e207c5 --- /dev/null +++ b/src/exchange-lib/testing_api_trait_key_peer.c @@ -0,0 +1,82 @@ +/* +  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 exchange-lib/testing_api_trait_key_peer.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 "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + +/** + * NOTE: calling it "peer" key to make clear it is _not a coin_ + *       key. + */ + +#define TALER_TESTING_TRAIT_KEY_PEER "key-peer" + +/** + * Obtain a private key from a "peer".  Used e.g. to obtain + * a merchant's priv to sign a /track request. + * + * @param index (tipically zero) which key to return if they + *        exist in an array. + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param priv[out] set to the key coming from @a cmd. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_peer_key +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   const struct GNUNET_CRYPTO_EddsaPrivateKey **priv) +{ +  return cmd->traits (cmd->cls, +                      (void **) priv, +                      TALER_TESTING_TRAIT_KEY_PEER, +                      index); +} + +/** + * @param index (tipically zero) which key to return if they + *        exist in an array. + * @param priv which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_peer_key +  (unsigned int index, +   struct GNUNET_CRYPTO_EddsaPrivateKey *priv) +{ +  struct TALER_TESTING_Trait ret = { +    .index = index, +    .trait_name = TALER_TESTING_TRAIT_KEY_PEER, +    .ptr = (const void *) priv +  }; +  return ret; +} + +/* end of testing_api_trait_key_peer.c */ diff --git a/src/exchange-lib/testing_api_trait_number.c b/src/exchange-lib/testing_api_trait_number.c new file mode 100644 index 00000000..8f011dca --- /dev/null +++ b/src/exchange-lib/testing_api_trait_number.c @@ -0,0 +1,74 @@ +/* +  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 exchange-lib/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 "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + +#define TALER_TESTING_TRAIT_NUMBER "number" + +/** + * Obtain a "number" value from @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param n[out] 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, +   unsigned int **n) +{ +  return cmd->traits (cmd->cls, +                      (void **) n, +                      TALER_TESTING_TRAIT_NUMBER, +                      index); +} + +/** + * @param selector associate the object with this "tag" + * @param n which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +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_NUMBER, +    .ptr = (const void *) n +  }; +  return ret; +} + +/* end of testing_api_trait_number.c */ diff --git a/src/exchange-lib/testing_api_trait_process.c b/src/exchange-lib/testing_api_trait_process.c index 877eca97..e3c1bdf4 100644 --- a/src/exchange-lib/testing_api_trait_process.c +++ b/src/exchange-lib/testing_api_trait_process.c @@ -40,22 +40,22 @@   */  int  TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd, -                                 const char *selector, +                                 unsigned int index,                                   struct GNUNET_OS_Process ***processp)  {    return cmd->traits (cmd->cls,                        (void **) processp,                        TALER_TESTING_TRAIT_PROCESS, -                      selector); +                      index);  }  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_process (const char *selector, +TALER_TESTING_make_trait_process (unsigned int index,                                    struct GNUNET_OS_Process **processp)  {    struct TALER_TESTING_Trait ret = { -    .selector = selector, +    .index = index,      .trait_name = TALER_TESTING_TRAIT_PROCESS,      .ptr = (const void *) processp    }; diff --git a/src/exchange-lib/testing_api_trait_reserve_priv.c b/src/exchange-lib/testing_api_trait_reserve_priv.c index a849f765..fb80a064 100644 --- a/src/exchange-lib/testing_api_trait_reserve_priv.c +++ b/src/exchange-lib/testing_api_trait_reserve_priv.c @@ -39,22 +39,22 @@   */  int  TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd, -                                      const char *selector, +                                      unsigned int index,                                        struct TALER_ReservePrivateKeyP **reserve_priv)  {    return cmd->traits (cmd->cls,                        (void **) reserve_priv,                        TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY, -                      selector); +                      index);  }  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_reserve_priv (const char *selector, +TALER_TESTING_make_trait_reserve_priv (unsigned int index,                                         const struct TALER_ReservePrivateKeyP *reserve_priv)  {    struct TALER_TESTING_Trait ret = { -    .selector = selector, +    .index = index,      .trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,      .ptr = (const void *) reserve_priv    }; diff --git a/src/exchange-lib/testing_api_trait_string.c b/src/exchange-lib/testing_api_trait_string.c new file mode 100644 index 00000000..308c4ea2 --- /dev/null +++ b/src/exchange-lib/testing_api_trait_string.c @@ -0,0 +1,209 @@ +/* +  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 exchange-lib/testing_api_trait_string.c + * @brief offers strings traits.  Mostly used to offer + *        stringified JSONs. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + +#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details" +#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms" +#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject" +#define TALER_TESTING_TRAIT_AMOUNT "amount" + +/** + * Obtain contract terms from @a cmd. + * + * @param cmd command to extract trait from + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param contract_terms[out] 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 char **contract_terms) +{ +  return cmd->traits (cmd->cls, +                      (void **) contract_terms, +                      TALER_TESTING_TRAIT_CONTRACT_TERMS, +                      index); +} + +/** + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param contract_terms contract terms to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_contract_terms +  (unsigned int index, +   const char *contract_terms) +{ +  struct TALER_TESTING_Trait ret = { +    .index = index, +    .trait_name = TALER_TESTING_TRAIT_CONTRACT_TERMS, +    .ptr = (const void *) contract_terms +  }; +  return ret; +} + + +/** + * Obtain wire details from @a cmd. + * + * @param cmd command to extract trait from + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param wire_details[out] 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 char **wire_details) +{ +  return cmd->traits (cmd->cls, +                      (void **) wire_details, +                      TALER_TESTING_TRAIT_WIRE_DETAILS, +                      index); +} + +/** + * Offer wire details in a trait. + * + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param wire_details wire details to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_wire_details +  (unsigned int index, +   const char *wire_details) +{ +  struct TALER_TESTING_Trait ret = { +    .index = index, +    .trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS, +    .ptr = (const void *) wire_details +  }; +  return ret; +} + + +/** + * Obtain a transfer subject from @a cmd. + * + * @param cmd command to extract trait from + * @param index always (?) zero, as one command sticks + *        to one bank transfer + * @param transfer_subject[out] where to write the wire details. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_transfer_subject +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   char **transfer_subject) +{ +  return cmd->traits (cmd->cls, +                      (void **) transfer_subject, +                      TALER_TESTING_TRAIT_TRANSFER_SUBJECT, +                      index); +} + +/** + * Offer wire details in a trait. + * + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param wire_details wire details to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_transfer_subject +  (unsigned int index, +   char *transfer_subject) +{ +  struct TALER_TESTING_Trait ret = { +    .index = index, +    .trait_name = TALER_TESTING_TRAIT_TRANSFER_SUBJECT, +    .ptr = (const void *) transfer_subject +  }; +  return ret; +} + + +/** + * Obtain an amount from @a cmd. + * + * @param cmd command to extract trait from + * @param index which amount is to be picked, in case + *        multiple are offered. + * @param amount[out] where to write the wire details. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_amount +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   const char **amount) +{ +  return cmd->traits (cmd->cls, +                      (void **) amount, +                      TALER_TESTING_TRAIT_AMOUNT, +                      index); +} + +/** + * Offer amount in a trait. + * + * @param index which amount is to be picked, in case + *        multiple are offered. + * @param amount the amount to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_amount +  (unsigned int index, +   const char *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_string.c */ diff --git a/src/exchange-lib/testing_api_trait_wtid.c b/src/exchange-lib/testing_api_trait_wtid.c new file mode 100644 index 00000000..140a2ac3 --- /dev/null +++ b/src/exchange-lib/testing_api_trait_wtid.c @@ -0,0 +1,74 @@ +/* +  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 exchange-lib/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 "exchange_api_handle.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 wtid[out] 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, +   struct TALER_WireTransferIdentifierRawP **wtid) +{ +  return cmd->traits (cmd->cls, +                      (void **) wtid, +                      TALER_TESTING_TRAIT_WTID, +                      index); +} + +/** + * @param index associate the object with this index + * @param wtid which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_wtid +  (unsigned int index, +   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/exchange-lib/testing_api_traits.c b/src/exchange-lib/testing_api_traits.c index 3983d320..c83c7e77 100644 --- a/src/exchange-lib/testing_api_traits.c +++ b/src/exchange-lib/testing_api_traits.c @@ -2,16 +2,18 @@    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 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. +  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 +  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/>  */  /** @@ -28,11 +30,15 @@  #include "taler_testing_lib.h" +/** + * End a trait array.  Usually, commands offer several traits, + * and put them in arrays. + */  struct TALER_TESTING_Trait  TALER_TESTING_trait_end ()  {    struct TALER_TESTING_Trait end = { -    .selector = NULL, +    .index = 0,      .trait_name = NULL,      .ptr = NULL    }; @@ -40,31 +46,35 @@ TALER_TESTING_trait_end ()    return end;  } - +/** + * Pick the chosen trait from the traits array. + * + * @param traits the traits array + * @param ret where to store the result + * @param selector which particular object in the trait should be + *        returned + * + * @return GNUNET_OK if no error occurred, GNUNET_SYSERR otherwise + */  int  TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,                           void **ret,                           const char *trait, -                         const char *selector) +                         unsigned int index)  { -  for (unsigned int i=0; -       NULL != traits[i].trait_name; -       i++) +  for (unsigned int i=0; NULL != traits[i].trait_name; i++)    { -    if ( (0 == strcmp (trait, -                       traits[i].trait_name)) && -         ( (NULL == selector) || -           (0 == strcasecmp (selector, -                             traits[i].selector) ) ) ) +    if ( (0 == strcmp (trait, traits[i].trait_name)) && +         (index == traits[i].index) )      {        *ret = (void *) traits[i].ptr;        return GNUNET_OK;      }    } -  /* FIXME: log */ +  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +              "Trait %s/%u not found.\n", +              trait, index);    return GNUNET_SYSERR;  } - -  /* end of testing_api_traits.c */ diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index b37a7a4c..5e069af5 100644 --- a/src/exchange-tools/taler-exchange-keyup.c +++ b/src/exchange-tools/taler-exchange-keyup.c @@ -1124,6 +1124,9 @@ exchange_keys_revoke_by_dki (void *cls,      rc->ok = GNUNET_SYSERR;      return GNUNET_SYSERR;    } +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Successfully revoking denom '%s..'\n", +              TALER_B2S (rc->hc));    return GNUNET_NO;  } diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c index f57451a7..0428b101 100644 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ b/src/exchange/taler-exchange-httpd_keystate.c @@ -1682,6 +1682,10 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,    GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,                                       &hc); +  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, +              "Looking for denom: '%s..'\n", +              TALER_B2S (&hc)); +    return TEH_KS_denomination_key_lookup_by_hash (key_state,                                                   &hc,                                                   use); diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 2a95ff6d..de671c9f 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -2,16 +2,19 @@    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/> +  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/>  */  /** @@ -29,7 +32,21 @@  #include <microhttpd.h> -/* ********************* Helper functions *********************** */ +/* ********************* Helper functions ********************* */ + +/** + * Allocate and return a piece of wire-details.  Mostly, it adds + * the bank_url to the JSON. + * + * @param template the wire-details template. + * @param bank_url the bank_url + * + * @return the filled out and stringified wire-details.  To + *         be manually free'd. + */ +char * +TALER_TESTING_make_wire_details (const char *template, +                                 const char *bank_url);  /**   * Find denomination key matching the given amount. @@ -50,8 +67,8 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,   * launch the exchange process itself.   *   * @param config_filename configuration file to use - * @return #GNUNET_OK on success, #GNUNET_NO if test should be skipped, - *         #GNUNET_SYSERR on test failure + * @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); @@ -83,13 +100,100 @@ char *  TALER_TESTING_prepare_fakebank (const char *config_filename); -/* ******************* Generic interpreter logic ****************** */ +/* ******************* Generic interpreter logic ************ */  /**   * Global state of the interpreter, used by a command   * to access information about other commands.   */ -struct TALER_TESTING_Interpreter; +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; + +  /** +   * ID of task called whenever we get a SIGCHILD. +   * Used for #TALER_TESTING_wait_for_sigchld(). +   */ +  struct GNUNET_SCHEDULER_Task *child_death_task; + +  /** +   * Main execution context for the main loop. +   */ +  struct GNUNET_CURL_Context *ctx; + +  /** +   * Context for running the CURL event loop. +   */ +  struct GNUNET_CURL_RescheduleContext *rc; + +  /** +   * Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used. +   * Otherwise NULL. +   */ +  struct TALER_FAKEBANK_Handle *fakebank; + +  /** +   * Task run on timeout. +   */ +  struct GNUNET_SCHEDULER_Task *timeout_task; + +  /** +   * Instruction pointer.  Tells #interpreter_run() which +   * instruction to run next. +   */ +  unsigned int ip; + +  /** +   * Result of the testcases, #GNUNET_OK on success +   */ +  int result; + +  /** +   * Handle to the exchange. +   */ +  struct TALER_EXCHANGE_Handle *exchange; + +  /** +   * Handle to exchange process; some commands need it +   * to send signals.  E.g. to trigger the key state reload. +   */ +  struct GNUNET_OS_Process *exchanged; + +  /** +   * GNUNET_OK if key state should be reloaded.  NOTE: this +   * field can be removed because a new "send signal" command +   * has been introduced. +   */ +  int reload_keys; + +  /** +   * Is the interpreter running (#GNUNET_YES) or waiting +   * for /keys (#GNUNET_NO)? +   */ +  int working; + +  /** +   * How often have we gotten a /keys response so far? +   */ +  unsigned int key_generation; + +  /** +   * Exchange keys from last download. +   */ +  const struct TALER_EXCHANGE_Keys *keys; + +}; + +  /** @@ -143,66 +247,73 @@ struct TALER_TESTING_Command     * @param ret[out] result (could be anything)     * @param trait name of the trait     * @param selector more detailed information about which object -   *                 to return in case there were multiple generated -   *                 by the command +   *                 to return in case there were multiple +   *                 generated by the command     * @return #GNUNET_OK on success     */    int    (*traits)(void *cls,              void **ret,              const char *trait, -            const char *selector); +            unsigned int index);  }; -  /**   * Lookup command by label.   */  const struct TALER_TESTING_Command * -TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *i, -                                          const char *label); - +TALER_TESTING_interpreter_lookup_command +  (struct TALER_TESTING_Interpreter *i, +   const char *label);  /**   * 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);  /**   * 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);  /**   * 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);  struct TALER_FAKEBANK_Handle * -TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is); +TALER_TESTING_interpreter_get_fakebank +  (struct TALER_TESTING_Interpreter *is);  /**   * Current command is done, run the next one.   */  void -TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is); +TALER_TESTING_interpreter_next +  (struct TALER_TESTING_Interpreter *is);  /**   * Current command failed, clean up and fail the test case.   */  void -TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is); +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); +TALER_TESTING_cmd_end ();  /** @@ -212,7 +323,8 @@ TALER_TESTING_cmd_end (void);   * with the next command.   */  void -TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is); +TALER_TESTING_wait_for_sigchld +  (struct TALER_TESTING_Interpreter *is);  void @@ -222,22 +334,39 @@ TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,  void -TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is, -                                 struct TALER_TESTING_Command *commands, -                                 const char *bank_url); +TALER_TESTING_run_with_fakebank +  (struct TALER_TESTING_Interpreter *is, +   struct TALER_TESTING_Command *commands, +   const char *bank_url); +/** + * FIXME + */  typedef void  (*TALER_TESTING_Main)(void *cls,                        struct TALER_TESTING_Interpreter *is); -  /** - * Initialize scheduler loop and curl context for the testcase. + * Install signal handlers plus schedules the main wrapper + * around the "run" method. + * + * @param main_cb the "run" method which coontains all the + *        commands. + * @param main_cb_cls a closure for "run", typically NULL. + * @param config_filename configuration filename. + * @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.. + * + * @return FIXME: not sure what 'is.result' is at this stage.   */  int  TALER_TESTING_setup (TALER_TESTING_Main main_cb, -                     void *main_cb_cls); +                     void *main_cb_cls, +                     const char *config_filename, +                     struct GNUNET_OS_Process *exchanged);  /** @@ -253,7 +382,7 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb, -/* ****************** Specific interpreter commands **************** */ +/* ************** Specific interpreter commands ************ */  /**   * Perform a wire transfer (formerly Admin-add-incoming) @@ -275,14 +404,15 @@ TALER_TESTING_cmd_fakebank_transfer (const char *label,   *   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label, -                                                  const char *amount, -                                                  const char *bank_url, -                                                  uint64_t debit_account_no, -                                                  uint64_t credit_account_no, -                                                  const char *auth_username, -                                                  const char *auth_password, -                                                  const char *subject); +TALER_TESTING_cmd_fakebank_transfer_with_subject +  (const char *label, +   const char *amount, +   const char *bank_url, +   uint64_t debit_account_no, +   uint64_t credit_account_no, +   const char *auth_username, +   const char *auth_password, +   const char *subject);  /** @@ -290,24 +420,65 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,   *   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, -                                              const char *amount, -                                              const char *bank_url, -                                              uint64_t debit_account_no, -                                              uint64_t credit_account_no, -                                              const char *auth_username, -                                              const char *auth_password, -                                              const char *ref); +TALER_TESTING_cmd_fakebank_transfer_with_ref +  (const char *label, +   const char *amount, +   const char *bank_url, +   uint64_t debit_account_no, +   uint64_t credit_account_no, +   const char *auth_username, +   const char *auth_password, +   const char *ref);  /**   * Execute taler-exchange-wirewatch process.   * + * @param label command label + * @param config_filanem configuration filename. + * + * @return the command.   */  struct TALER_TESTING_Command  TALER_TESTING_cmd_exec_wirewatch (const char *label,                                    const char *config_filename); +/** + * Execute taler-exchange-aggregator process. + * + * @param label command label + * @param config_filename configuration filename + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_aggregator (const char *label, +                                   const char *config_filename); + +/** + * Execute taler-exchange-keyup process. + * + * @param label command label + * @param config_filename configuration filename + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_keyup (const char *label, +                              const char *config_filename); + +/** + * Execute taler-auditor-sign process. + * + * @param label command label + * @param config_filename configuration filename + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_exec_auditor_sign (const char *label, +                                     const char *config_filename); +  /**   * Create withdraw command. @@ -315,12 +486,12 @@ TALER_TESTING_cmd_exec_wirewatch (const char *label,   * @return NULL on failure   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_withdraw_amount (const char *label, -                                   struct TALER_EXCHANGE_Handle *exchange, -                                   const char *reserve_reference, -                                   const char *amount, -                                   unsigned int expected_response_code); - +TALER_TESTING_cmd_withdraw_amount +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *reserve_reference, +   const char *amount, +   unsigned int expected_response_code);  /** @@ -328,21 +499,340 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,   *   */  struct TALER_TESTING_Command -TALER_TESTING_cmd_withdraw_denomination (const char *label, -                                         struct TALER_EXCHANGE_Handle *exchange, -                                         const char *reserve_reference, -                                         const struct TALER_EXCHANGE_DenomPublicKey *dk, -                                         unsigned int expected_response_code); +TALER_TESTING_cmd_withdraw_denomination +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *reserve_reference, +   const struct TALER_EXCHANGE_DenomPublicKey *dk, +   unsigned int expected_response_code); + +/** + * Create a /wire command. + * + * @param label the command label. + * @param exchange the exchange to connect to. + * @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 to be executed by the interpreter. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_wire (const char *label, +                        struct TALER_EXCHANGE_Handle *exchange, +                        const char *expected_method, +                        const char *expected_fee, +                        unsigned int expected_response_code); -/* ********************** Generic trait logic for implementing traits ******************* */ + +/** + * Create a /reserve/status command. + * + * @param label the command label. + * @param exchange the exchange to connect to. + * @param reserve_reference reference to the reserve to check. + * @param expected_balance balance expected to be at the + * referenced reserve. + * @param expected_response_code expected HTTP response code. + * + * @return the command to be executed by the interpreter. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_status (const char *label, +                          struct TALER_EXCHANGE_Handle *exchange, +                          const char *reserve_reference, +                          const char *expected_balance, +                          unsigned int expected_response_code); + +/** + * Create a deposit command. + * + * @param label command label + * @param exchange exchange connection + * @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 wire_details bank details of the merchant performing the + *        deposit + * @param contract_terms contract terms to be signed over by the + *        coin + * @param refund_deadline refund deadline + * @param amount how much is going to be deposited + * @param expected_response_code which HTTP status code we expect + *        in the response + * + * @return the deposit command to be run by the interpreter + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_deposit  +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *coin_reference, +   unsigned int coin_index, +   char *wire_details, +   const char *contract_terms, +   struct GNUNET_TIME_Relative refund_deadline, +   const char *amount, +   unsigned int expected_response_code); + + +/** + * Create a "refresh melt" command. + * + * @param label command label + * @param exchange connection to the exchange + * @param amount Fixme + * @param coin_reference reference to a command that will provide + *        a coin to refresh + * @param expected_response_code expected HTTP code + */ + +struct TALER_TESTING_Command +TALER_TESTING_cmd_refresh_melt +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *amount, +   const char *coin_reference, +   unsigned int expected_response_code); + + +/** + * Create a "refresh reveal" command. + * + * @param label command label + * @param exchange connection to the exchange + * @param melt_reference reference to a "refresh melt" command + * @param expected_response_code expected HTTP response code + * + * @return the "refresh reveal" command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_refresh_reveal +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *melt_reference, +   unsigned int expected_response_code); + + +/** + * Create a "refresh link" command. + * + * @param label command label + * @param exchange connection to the exchange + * @param melt_reference reference to a "refresh melt" command + * @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, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *reveal_reference, +   unsigned int expected_response_code); + + +/** + * Create a /track/transaction command. + * + * @param label the command label. + * @param exchange the exchange to connect to. + * @param transaction_reference reference to a deposit operation. + * @param coin_index index of the coin involved in the transaction + * @param expected_response_code expected HTTP response code. + * @param bank_transfer_reference which #OC_CHECK_BANK_TRANSFER + *        wtid should this match? NULL +   * for none + * + * @return the command to be executed by the interpreter. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_track_transaction +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *transaction_reference, +   unsigned int coin_index, +   unsigned int expected_response_code, +   const char *bank_transfer_reference); + +/** + * Make a /track/transfer command, expecting the transfer + * not being done (yet). + * + * @param label the command label + * @param exchange connection to the exchange + * @param wtid_reference reference to any command which can provide + *        a wtid + * @param index in case there are multiple wtid offered, this + *        parameter selects a particular one + * @param expected_response_code expected HTTP response code + * + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_track_transfer_empty +  (const char *label, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *wtid_reference, +   unsigned int index, +   unsigned int expected_response_code); + + +/** + * Make a /track/transfer command, specifying which amount and + * wire fee are expected. + * + * @param label the command label + * @param exchange connection to the exchange + * @param wtid_reference reference to any command which can provide + *        a wtid + * @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_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, +   struct TALER_EXCHANGE_Handle *exchange, +   const char *wtid_reference, +   unsigned int index, +   unsigned int expected_response_code, +   const char *expected_total_amount, +   const char *expected_wire_fee); + +/** + * Command to check whether a particular wire transfer has been + * made or not. + * + * @param label the command label + * @param exchange_base_url base url of the exchange (Fixme: why?) + * @param amount the amount expected to be transferred + * @param debit_account the account that gave money + * @param credit_account 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, +   const char *amount, +   unsigned int debit_account, +   unsigned int credit_account); + +/** + * Check bank's balance is zero. + * + * @param credit_account the account that received money + * + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_bank_empty (const char *label); + +/** + * Create a /refund test command. + * + * @param label command label + * @param expected_response_code expected HTTP status code + * @param refund_amount the amount to ask a refund for + * @param refund_fee expected refund fee + * @param coin_reference reference to a command that can + *        provide a coin to be refunded. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_refund (const char *label, +                          unsigned int expected_response_code, +                          const char *refund_amount, +                          const char *refund_fee, +                          const char *deposit_reference); + + +/** + * Make a /payback command. + * + * @param label the command label + * @param expected_response_code expected HTTP status code + * @param coin_reference reference to any command which offers + *        a reserve private key plus a coin to be paid back. + * @param amount denomination to pay back. + * + * @return a /revoke command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_payback (const char *label, +                           unsigned int expected_response_code, +                           const char *coin_reference, +                           const char *amount); + + +/** + * Make a /revoke 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 trait + * @param config_filename configuration file name. + * + * @return a /revoke command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_revoke (const char *label, +                          unsigned int expected_response_code, +                          const char *coin_reference, +                          const char *config_filename); + +/** + * Send a signal to a process. + * + * @param label command label + * @param process handle to the process + * @param signal signal to send + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_signal (const char *label, +                          struct GNUNET_OS_Process *process, +                          int signal); + +/** + * Make a "check keys" command. + * + * @param label command label + * @param generation FIXME + * @param num_denom_keys FIXME + * @param exchange connection to the exchange + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_keys +  (const char *label, +   unsigned int generation, +   unsigned int num_denom_keys, +   struct TALER_EXCHANGE_Handle *exchange); + +/* *** Generic trait logic for implementing traits ********* */  /**   * A trait.   */  struct TALER_TESTING_Trait  { -  const char *selector; +  unsigned int index;    const char *trait_name; @@ -359,150 +849,406 @@ int  TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,                           void **ret,                           const char *trait, -                         const char *selector); +                         unsigned int index); -/* ****************** Specific traits supported by this component *************** */ +/* ****** Specific traits supported by this component ******* */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_reserve_priv (const char *selector, -                                       const struct TALER_ReservePrivateKeyP *reserve_priv); +TALER_TESTING_make_trait_reserve_priv +  (unsigned int index, +   const struct TALER_ReservePrivateKeyP *reserve_priv);  /**   * Obtain a reserve private key from a @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer + * @param selector which coin to pick if @a cmd has multiple on + * offer   * @param reserve_priv[out] set to the private key of the reserve   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd, -                                      const char *selector, -                                      struct TALER_ReservePrivateKeyP **reserve_priv); - +TALER_TESTING_get_trait_reserve_priv +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct TALER_ReservePrivateKeyP **reserve_priv);  /**   * Obtain location where a command stores a pointer to a process   *   * @param cmd command to extract trait from - * @param selector which process to pick if @a cmd has multiple on offer - * @param coin_priv[out] set to address of the pointer to the process + * @param selector which process to pick if @a cmd has multiple + * on offer + * @param coin_priv[out] set to address of the pointer to the + * process   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd, -                                 const char *selector, -                                 struct GNUNET_OS_Process ***processp); +TALER_TESTING_get_trait_process +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct GNUNET_OS_Process ***processp); +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_process +  (unsigned int index, +   struct GNUNET_OS_Process **processp); +/** + * @param selector FIXME + */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_process (const char *selector, -                                  struct GNUNET_OS_Process **processp); +TALER_TESTING_make_trait_coin_priv +  (unsigned int index, +   const struct TALER_CoinSpendPrivateKeyP *coin_priv); +/** + * Obtain a coin private key from a @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param coin_priv[out] 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, +   struct TALER_CoinSpendPrivateKeyP **coin_priv);  /** - * @param selector + * @param selector a "tag" to associate the object with + * @param blinding_key which object should be returned + * + * @return the trait, to be put in the traits array of the command   */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_coin_priv (const char *selector, -                                    const struct TALER_CoinSpendPrivateKeyP *coin_priv); +TALER_TESTING_make_trait_blinding_key +  (unsigned int index, +   const struct TALER_DenominationBlindingKeyP *blinding_key); + +/** + * Obtain a coin's blinding key from a @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param blinding_key[out] set to the blinding key of the coin + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_blinding_key +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct TALER_DenominationBlindingKeyP **blinding_key); +/** + * @param selector a "tag" to associate the object with + * @param pdk which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_denom_pub +  (unsigned int index, +   const struct TALER_EXCHANGE_DenomPublicKey *dpk);  /**   * Obtain a coin private key from a @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer - * @param coin_priv[out] set to the private key of the coin + * @param selector which coin to pick if @a cmd has multiple on + *        offer + * @param dpk[out] set to a denomination key of the coin   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd, -                                   const char *selector, -                                   struct TALER_CoinSpendPrivateKeyP **coin_priv); +TALER_TESTING_get_trait_denom_pub +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct TALER_EXCHANGE_DenomPublicKey **dpk); +/** + * Obtain a coin denomination signature from a @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param sig[out] set to a denomination signature over the coin + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_denom_sig +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct TALER_DenominationSignature **dpk);  /**   * @param selector   */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_blinding_key (const char *selector, -                                       const struct TALER_DenominationBlindingKeyP *blinding_key); +TALER_TESTING_make_trait_denom_sig +  (unsigned int index, +   const struct TALER_DenominationSignature *sig); +/** + * @param selector associate the object with this "tag" + * @param i which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_uint +  (unsigned int index, +   const unsigned int *i);  /** - * Obtain a coin's blinding key from a @a cmd. + * Obtain a "number" value from @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer - * @param blinding_key[out] set to the blinding key of the coin + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param n[out] set to the number coming from @a cmd.   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd, -                                      const char *selector, -                                      struct TALER_DenominationBlindingKeyP **blinding_key); +TALER_TESTING_get_trait_uint +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   unsigned int **n); +/** + * Information about a fresh coin generated by the refresh + * operation. FIXME: should go away from here! + */ +struct FreshCoin +{ +  /** +   * 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. +   */ +  const struct TALER_EXCHANGE_DenomPublicKey *pk; +  /** +   * Set (by the interpreter) to the exchange's signature over the +   * coin's public key. +   */ +  struct TALER_DenominationSignature sig; + +  /** +   * Set (by the interpreter) to the coin's private key. +   */ +  struct TALER_CoinSpendPrivateKeyP coin_priv; +};  /** - * @param selector + * @param selector associate the object with this "tag" + * @param fresh_coins array of fresh coins to return + * + * @return the trait, to be put in the traits array of the command   */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_denom_pub (const char *selector, -                                    const struct TALER_EXCHANGE_DenomPublicKey *dpk); +TALER_TESTING_make_trait_fresh_coins +  (unsigned int index, +   struct FreshCoin *fresh_coins); + +/** + * Obtain a "number" value from @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param fresh_coins[out] will point to array of fresh coins + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_fresh_coins +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   struct FreshCoin **fresh_coins); +  /** - * Obtain a coin private key from a @a cmd. + * Obtain contract terms from @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer - * @param dpk[out] set to a denomination key of the coin + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param contract_terms[out] where to write the contract + *        terms.   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd, -                                   const char *selector, -                                   struct TALER_EXCHANGE_DenomPublicKey **dpk); +TALER_TESTING_get_trait_contract_terms +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   const char **contract_terms); + +/** + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param contract_terms contract terms to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_contract_terms +  (unsigned int index, +   const char *contract_terms);  /** - * Obtain a coin denomination signature from a @a cmd. + * Obtain wire details from @a cmd.   *   * @param cmd command to extract trait from - * @param selector which coin to pick if @a cmd has multiple on offer - * @param sig[out] set to a denomination signature over the coin + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param wire_details[out] where to write the wire details.   * @return #GNUNET_OK on success   */  int -TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd, -                                   const char *selector, -                                   struct TALER_DenominationSignature **dpk); +TALER_TESTING_get_trait_wire_details +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   const char **wire_details);  /** - * @param selector + * Offer wire details in a trait. + * + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param wire_details wire details to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_wire_details +  (unsigned int index, +   const char *wire_details); + +/** + * Obtain a private key from a "peer".  Used e.g. to obtain + * a merchant's priv to sign a /track request. + * + * @param index (tipically zero) which key to return if they + *        exist in an array. + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param priv[out] set to the key coming from @a cmd. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_peer_key +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   const struct GNUNET_CRYPTO_EddsaPrivateKey **priv); + + +/** + * @param index (tipically zero) which key to return if they + *        exist in an array. + * @param priv which object should be returned + * + * @return the trait, to be put in the traits array of the command   */  struct TALER_TESTING_Trait -TALER_TESTING_make_trait_denom_sig (const char *selector, -                                    const struct TALER_DenominationSignature *sig); +TALER_TESTING_make_trait_peer_key +  (unsigned int index, +   struct GNUNET_CRYPTO_EddsaPrivateKey *priv); +/** + * Obtain a transfer subject from @a cmd. + * + * @param cmd command to extract trait from + * @param index always (?) zero, as one command sticks + *        to one bank transfer + * @param transfer_subject[out] where to write the wire details. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_transfer_subject +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   char **transfer_subject); +/** + * Offer wire details in a trait. + * + * @param index always (?) zero, as one command sticks + *        to one bank account + * @param wire_details wire details to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_transfer_subject +  (unsigned int index, +   char *transfer_subject); +/** + * 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 wtid[out] 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, +   struct TALER_WireTransferIdentifierRawP **wtid); +/** + * @param index associate the object with this index + * @param wtid which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_wtid +  (unsigned int index, +   struct TALER_WireTransferIdentifierRawP *wtid); +/** + * Offer amount in a trait. + * + * @param index which amount is to be picked, in case + *        multiple are offered. + * @param amount the amount to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_amount +  (unsigned int index, +   const char *amount); +/** + * Obtain an amount from @a cmd. + * + * @param cmd command to extract trait from + * @param index which amount is to be picked, in case + *        multiple are offered. + * @param amount[out] where to write the wire details. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_amount +  (const struct TALER_TESTING_Command *cmd, +   unsigned int index, +   const char **amount);  #endif | 
