From 528faeab81ba240329140eb1587ccc542736c1dc Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 15 Sep 2018 22:20:07 +0200 Subject: [PATCH] fix #5315, including more testing --- src/exchange-lib/Makefile.am | 29 +- src/exchange-lib/exchange_api_handle.c | 52 +- src/exchange-lib/test_exchange_api.c | 4035 ----------------- .../test_exchange_api_keys_cherry_picking.c | 803 ---- ...test_exchange_api_keys_cherry_picking.conf | 12 +- ...est_exchange_api_keys_cherry_picking_new.c | 25 +- src/exchange-lib/testing_api_cmd_check_keys.c | 11 +- src/exchange-tools/taler-exchange-keyup.c | 14 +- src/exchange/taler-exchange-httpd_keystate.c | 9 + src/include/taler_exchange_service.h | 15 +- 10 files changed, 83 insertions(+), 4922 deletions(-) delete mode 100644 src/exchange-lib/test_exchange_api.c delete mode 100644 src/exchange-lib/test_exchange_api_keys_cherry_picking.c diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am index 9dbd2256c..8ac43ea8c 100644 --- a/src/exchange-lib/Makefile.am +++ b/src/exchange-lib/Makefile.am @@ -48,6 +48,7 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_refund.c \ testing_api_cmd_status.c \ testing_api_cmd_deposit.c \ + testing_api_cmd_sleep.c \ testing_api_cmd_refresh.c \ testing_api_cmd_track.c \ testing_api_cmd_bank_check.c \ @@ -95,9 +96,7 @@ endif check_PROGRAMS = \ test_exchange_api_keys_cherry_picking_new \ - test_exchange_api_new \ - test_exchange_api_keys_cherry_picking \ - test_exchange_api + test_exchange_api_new if HAVE_TWISTER check_PROGRAMS += \ @@ -126,19 +125,6 @@ AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH= TESTS = \ $(check_PROGRAMS) -test_exchange_api_SOURCES = \ - test_exchange_api.c -test_exchange_api_LDADD = \ - libtalerexchange.la \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/bank-lib/libtalerfakebank.la \ - $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetcurl \ - -lgnunetutil \ - -ljansson - test_exchange_api_new_SOURCES = \ test_exchange_api_new.c test_exchange_api_new_LDADD = \ @@ -166,17 +152,6 @@ test_exchange_api_keys_cherry_picking_new_LDADD = \ -lgnunetutil \ -ljansson -test_exchange_api_keys_cherry_picking_SOURCES = \ - test_exchange_api_keys_cherry_picking.c -test_exchange_api_keys_cherry_picking_LDADD = \ - libtalerexchange.la \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetcurl \ - -lgnunetutil \ - -ljansson - EXTRA_DIST = \ test_exchange_api_home/.local/share/taler/exchange/offline-keys/master.priv \ test_exchange_api_home/.config/taler/test.json \ diff --git a/src/exchange-lib/exchange_api_handle.c b/src/exchange-lib/exchange_api_handle.c index ba1f8c217..83dda20f6 100644 --- a/src/exchange-lib/exchange_api_handle.c +++ b/src/exchange-lib/exchange_api_handle.c @@ -426,14 +426,15 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, &kv.auditor_url_hash); kv.master = key_data->master_pub; len = json_array_size (keys); - auditor->denom_keys = GNUNET_new_array (len, - const struct TALER_EXCHANGE_DenomPublicKey *); + auditor->denom_key_offsets = GNUNET_new_array (len, + unsigned int); i = 0; off = 0; json_array_foreach (keys, i, key) { struct TALER_AuditorSignatureP auditor_sig; struct GNUNET_HashCode denom_h; const struct TALER_EXCHANGE_DenomPublicKey *dk; + unsigned int dk_off; struct GNUNET_JSON_Specification kspec[] = { GNUNET_JSON_spec_fixed_auto ("denom_pub_h", &denom_h), @@ -451,6 +452,7 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, continue; } dk = NULL; + dk_off = UINT_MAX; for (unsigned int j=0;jnum_denom_keys;j++) { if (0 == memcmp (&denom_h, @@ -458,6 +460,7 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, sizeof (struct GNUNET_HashCode))) { dk = &key_data->denom_keys[j]; + dk_off = j; break; } } @@ -492,7 +495,7 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } - auditor->denom_keys[off] = dk; + auditor->denom_key_offsets[off] = dk_off; off++; } auditor->num_denom_keys = off; @@ -628,7 +631,8 @@ decode_keys_json (const json_t *resp_obj, unsigned int index; EXITIF (NULL == (denom_keys_array = - json_object_get (resp_obj, "denoms"))); + json_object_get (resp_obj, + "denoms"))); EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); index = 0; @@ -694,6 +698,7 @@ decode_keys_json (const json_t *resp_obj, for (unsigned int j=0;jnum_auditors;j++) { struct TALER_EXCHANGE_AuditorInformation *aix = &key_data->auditors[j]; + if (0 == memcmp (&ai.auditor_pub, &aix->auditor_pub, sizeof (struct TALER_AuditorPublicKeyP))) @@ -701,12 +706,12 @@ decode_keys_json (const json_t *resp_obj, found = true; /* Merge denomination key signatures of downloaded /keys into existing auditor information 'aix'. */ - GNUNET_array_grow (aix->denom_keys, + GNUNET_array_grow (aix->denom_key_offsets, aix->num_denom_keys, aix->num_denom_keys + ai.num_denom_keys); - memcpy (&aix->denom_keys[aix->num_denom_keys - ai.num_denom_keys], - ai.denom_keys, - ai.num_denom_keys * sizeof (struct TALER_EXCHANGE_DenomPublicKey *)); + memcpy (&aix->denom_key_offsets[aix->num_denom_keys - ai.num_denom_keys], + ai.denom_key_offsets, + ai.num_denom_keys * sizeof (unsigned int)); break; } } @@ -762,7 +767,7 @@ free_key_data (struct TALER_EXCHANGE_Keys *key_data) 0); for (unsigned int i=0;inum_auditors;i++) { - GNUNET_array_grow (key_data->auditors[i].denom_keys, + GNUNET_array_grow (key_data->auditors[i].denom_key_offsets, key_data->auditors[i].num_denom_keys, 0); GNUNET_free (key_data->auditors[i].auditor_url); @@ -827,10 +832,11 @@ keys_completed_cb (void *cls, struct TALER_EXCHANGE_Keys kd_old; enum TALER_EXCHANGE_VersionCompatibility vc; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received keys from URL `%s' with status %ld.\n", kr->url, response_code); + json_dumpf (resp_obj, stderr, 0); kd_old = exchange->key_data; memset (&kd, 0, @@ -878,17 +884,12 @@ keys_completed_cb (void *cls, anew->auditor_pub = aold->auditor_pub; anew->auditor_url = GNUNET_strdup (aold->auditor_url); - GNUNET_array_grow (anew->denom_keys, + GNUNET_array_grow (anew->denom_key_offsets, anew->num_denom_keys, aold->num_denom_keys); - for (unsigned int j=0;jnum_denom_keys;j++) - { - /* offsets will map 1:1 */ - unsigned int off = aold->denom_keys[j] - kd_old.denom_keys; - /* FIXME(#5315): this should not be an assert! */ - GNUNET_assert (off < kd_old.num_denom_keys); - anew->denom_keys[j] = &kd.denom_keys[off]; - } + memcpy (anew->denom_key_offsets, + aold->denom_key_offsets, + aold->num_denom_keys * sizeof (unsigned int)); } if (GNUNET_OK != @@ -921,10 +922,10 @@ keys_completed_cb (void *cls, NULL, vc); if (NULL != exchange->key_data_raw) - { - json_decref (exchange->key_data_raw); - exchange->key_data_raw = NULL; - } + { + json_decref (exchange->key_data_raw); + exchange->key_data_raw = NULL; + } free_key_data (&kd_old); return; } @@ -1157,13 +1158,10 @@ request_keys (void *cls) kr = GNUNET_new (struct KeysRequest); kr->exchange = exchange; if (GNUNET_YES == - MAH_handle_is_ready (exchange) && !TALER_EXCHANGE_API_DISABLE_CHERRYPICKING) + MAH_handle_is_ready (exchange)) { char *arg; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Doing cherry-picking\n"); - GNUNET_asprintf (&arg, "/keys?last_issue_date=%llu", (unsigned long long) exchange->key_data.last_denom_issue_date.abs_value_us / 1000000LLU); diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c deleted file mode 100644 index a96e49963..000000000 --- a/src/exchange-lib/test_exchange_api.c +++ /dev/null @@ -1,4035 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2017 GNUnet e.V. and Inria - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see -*/ -/** - * @file exchange/test_exchange_api.c - * @brief testcase to test exchange's HTTP API interface - * @author Sree Harsha Totakura - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_signatures.h" -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include -#include -#include "taler_bank_service.h" -#include "taler_fakebank_lib.h" - - -/** - * Is the configuration file is set to include wire format 'test'? - */ -#define WIRE_TEST 1 - -/** - * Is the configuration file is set to include wire format 'ebics'? - * Requires that EBICS /history function is implemented, which it - * is currently not. Once it is, set ENABLE_CREDIT to YES in the - * configuration and then set this option to 1. - */ -#define WIRE_EBICS 0 - -/** - * Account number of the exchange at the bank. - */ -#define EXCHANGE_ACCOUNT_NO 2 - -/** - * Main execution context for the main loop. - */ -static struct GNUNET_CURL_Context *ctx; - -/** - * Handle to access the exchange. - */ -static struct TALER_EXCHANGE_Handle *exchange; - -/** - * Context for running the CURL event loop. - */ -static struct GNUNET_CURL_RescheduleContext *rc; - -/** - * Handle to the exchange process. - */ -static struct GNUNET_OS_Process *exchanged; - -/** - * Task run on timeout. - */ -static struct GNUNET_SCHEDULER_Task *timeout_task; - -/** - * Handle to our fakebank. - */ -static struct TALER_FAKEBANK_Handle *fakebank; - -/** - * Result of the testcases, #GNUNET_OK on success - */ -static int result; - - -/** - * Opcodes for the interpreter. - */ -enum OpCode -{ - /** - * Termination code, stops the interpreter loop (with success). - */ - OC_END = 0, - - /** - * Add funds to a reserve by (faking) incoming wire transfer. - */ - OC_ADMIN_ADD_INCOMING, - - /** - * Check status of a reserve. - */ - OC_WITHDRAW_STATUS, - - /** - * Withdraw a coin from a reserve. - */ - OC_WITHDRAW_SIGN, - - /** - * Deposit a coin (pay with it). - */ - OC_DEPOSIT, - - /** - * Melt a (set of) coins. - */ - OC_REFRESH_MELT, - - /** - * Complete melting session by withdrawing melted coins. - */ - OC_REFRESH_REVEAL, - - /** - * Verify exchange's /refresh/link by linking original private key to - * results from #OC_REFRESH_REVEAL step. - */ - OC_REFRESH_LINK, - - /** - * Verify the exchange's /wire-method. - */ - OC_WIRE, - - /** - * Verify exchange's /track/transfer method. - */ - OC_WIRE_DEPOSITS, - - /** - * Verify exchange's /track/transaction method. - */ - OC_DEPOSIT_WTID, - - /** - * Run the aggregator to execute deposits. - */ - OC_RUN_AGGREGATOR, - - /** - * Run the wirewatcher to check for incoming transactions. - */ - OC_RUN_WIREWATCH, - - /** - * Check that the fakebank has received a certain transaction. - */ - OC_CHECK_BANK_TRANSFER, - - /** - * Check that the fakebank has not received any other transactions. - */ - OC_CHECK_BANK_TRANSFERS_EMPTY, - - /** - * Refund some deposit. - */ - OC_REFUND, - - /** - * Revoke some denomination key. - */ - OC_REVOKE, - - /** - * Payback some coin. - */ - OC_PAYBACK - -}; - - -/** - * 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_ref; - -}; - - -/** - * Information about a fresh coin generated by the refresh operation. - */ -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; - -}; - - -/** - * Details for a exchange operation to execute. - */ -struct Command -{ - /** - * Opcode of the command. - */ - enum OpCode oc; - - /** - * Label for the command, can be NULL. - */ - const char *label; - - /** - * Which response code do we expect for this command? - */ - unsigned int expected_response_code; - - /** - * Details about the command. - */ - union - { - - /** - * Information for a #OC_ADMIN_ADD_INCOMING command. - */ - struct - { - - /** - * Label to another admin_add_incoming command if we - * should deposit into an existing reserve, NULL if - * a fresh reserve should be created. - */ - const char *reserve_reference; - - /** - * String describing the amount to add to the reserve. - */ - const char *amount; - - /** - * Wire transfer subject. NULL to use public key corresponding - * to @e reserve_priv or @e reserve_reference. Should only be - * set manually to test invalid wire transfer subjects. - */ - const char *subject; - - /** - * Sender (debit) account number. - */ - uint64_t debit_account_no; - - /** - * Receiver (credit) account number. - */ - uint64_t credit_account_no; - - /** - * Username to use for authentication. - */ - const char *auth_username; - - /** - * Password to use for authentication. - */ - const char *auth_password; - - /** - * Set (by the interpreter) to the reserve's private key - * we used to fill the reserve. - */ - struct TALER_ReservePrivateKeyP reserve_priv; - - /** - * Set to the API's handle during the operation. - */ - struct TALER_BANK_AdminAddIncomingHandle *aih; - - /** - * Set to the wire transfer's unique ID. - */ - uint64_t serial_id; - - } admin_add_incoming; - - /** - * Information for a #OC_WITHDRAW_STATUS command. - */ - struct - { - - /** - * Label to the #OC_ADMIN_ADD_INCOMING command which - * created the reserve. - */ - const char *reserve_reference; - - /** - * Set to the API's handle during the operation. - */ - struct TALER_EXCHANGE_ReserveStatusHandle *wsh; - - /** - * Expected reserve balance. - */ - const char *expected_balance; - - } reserve_status; - - /** - * Information for a #OC_WITHDRAW_SIGN command. - */ - struct - { - - /** - * Which reserve should we withdraw from? - */ - const char *reserve_reference; - - /** - * String describing the denomination value we should withdraw. - * A corresponding denomination key must exist in the exchange's - * offerings. Can be NULL if @e pk is set instead. - */ - const char *amount; - - /** - * If @e amount is NULL, this specifies the denomination key to - * use. Otherwise, this will be set (by the interpreter) to the - * denomination PK matching @e amount. - */ - 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; - - /** - * Private key material of the coin, set by the interpreter. - */ - struct TALER_PlanchetSecretsP ps; - - /** - * Withdraw handle (while operation is running). - */ - struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; - - } reserve_withdraw; - - /** - * Information for a #OC_DEPOSIT command. - */ - struct - { - - /** - * Amount to deposit. - */ - const char *amount; - - /** - * Reference to a reserve_withdraw operation for a coin to - * be used for the /deposit operation. - */ - const char *coin_ref; - - /** - * If this @e coin_ref refers to an operation that generated - * an array of coins, this value determines which coin to use. - */ - unsigned int coin_idx; - - /** - * JSON string describing the merchant's "wire details". - */ - const 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 of the merchant, - * if @e refund_deadline is non-zero. - */ - struct TALER_MerchantPrivateKeyP merchant_priv; - - /** - * Deposit handle while operation is running. - */ - struct TALER_EXCHANGE_DepositHandle *dh; - - } deposit; - - /** - * Information for a #OC_REFRESH_MELT command. - */ - struct - { - - /** - * Information about coins to be melted. - */ - struct MeltDetails melted_coin; - - /** - * Denominations of the fresh coins to withdraw. - */ - const char **fresh_amounts; - - /** - * Array of the public keys corresponding to - * the @e fresh_amounts, set by the interpreter. - */ - const struct TALER_EXCHANGE_DenomPublicKey **fresh_pks; - - /** - * Melt handle while operation is running. - */ - struct TALER_EXCHANGE_RefreshMeltHandle *rmh; - - /** - * Data used in the refresh operation, set by the interpreter. - */ - char *refresh_data; - - /** - * Number of bytes in @e refresh_data, set by the interpreter. - */ - size_t refresh_data_length; - - /** - * Set by the interpreter (upon completion) to the noreveal - * index selected by the exchange. - */ - uint16_t noreveal_index; - - } refresh_melt; - - /** - * Information for a #OC_REFRESH_REVEAL command. - */ - struct - { - - /** - * Melt operation this is the matching reveal for. - */ - const char *melt_ref; - - /** - * 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; - - } refresh_reveal; - - /** - * Information for a #OC_REFRESH_LINK command. - */ - struct - { - - /** - * Reveal operation this is the matching link for. - */ - const char *reveal_ref; - - /** - * Link handle while operation is running. - */ - struct TALER_EXCHANGE_RefreshLinkHandle *rlh; - - /** - * Which of the melted coins should be used for the linkage? - */ - unsigned int coin_idx; - - } refresh_link; - - /** - * Information for the /wire command. - */ - struct { - - /** - * Handle to the wire request. - */ - struct TALER_EXCHANGE_WireHandle *wh; - - /** - * Format we expect to see, others will be *ignored*. - */ - const char *format; - - /** - * Expected wire fee. - */ - const char *expected_fee; - - } wire; - - /** - * Information for the /track/transfer's command. - */ - struct { - - /** - * Handle to the wire deposits request. - */ - struct TALER_EXCHANGE_TrackTransferHandle *wdh; - - /** - * Reference to a command providing a WTID. If set, we use the - * WTID from that command. The command can be either an - * #OC_DEPOSIT_WTID or an #OC_CHECK_BANK_TRANSFER. In the - * case of the bank transfer, we check that the total amount - * claimed by the exchange matches the total amount transferred - * by the bank. In the case of a /track/transaction, we check - * that the wire details match. - */ - const char *wtid_ref; - - /** - * WTID to use (used if @e wtid_ref is NULL). - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * What is the expected total amount? Only used if - * @e expected_response_code was #MHD_HTTP_OK. - */ - const char *total_amount_expected; - - /** - * What is the expected wire fee? Only used if - * @e expected_response_code was #MHD_HTTP_OK. - */ - const char *wire_fee_expected; - - - /* TODO: may want to add list of deposits we expected - to see aggregated here in the future. */ - - } wire_deposits; - - /** - * Information for the /track/transaction command. - */ - struct { - - /** - * Handle to the deposit wtid request. - */ - struct TALER_EXCHANGE_TrackTransactionHandle *dwh; - - /** - * Which /deposit operation should we obtain WTID data for? - */ - const char *deposit_ref; - - /** - * Which #OC_CHECK_BANK_TRANSFER wtid should this match? NULL for none. - */ - const char *bank_transfer_ref; - - /** - * Wire transfer identifier, set if #MHD_HTTP_OK was the response code. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - } deposit_wtid; - - struct { - - /** - * Process for the aggregator. - */ - struct GNUNET_OS_Process *aggregator_proc; - - /** - * ID of task called whenever we get a SIGCHILD. - */ - struct GNUNET_SCHEDULER_Task *child_death_task; - - } run_aggregator; - - struct { - - /** - * Process for the wirewatcher. - */ - struct GNUNET_OS_Process *wirewatch_proc; - - /** - * ID of task called whenever we get a SIGCHILD. - */ - struct GNUNET_SCHEDULER_Task *child_death_task; - - } run_wirewatch; - - struct { - - /** - * Which amount do we expect to see transferred? - */ - const char *amount; - - /** - * Which account do we expect to be debited? - */ - uint64_t account_debit; - - /** - * Which account do we expect to be credited? - */ - uint64_t account_credit; - - /** - * Which exchange base URL is expected? - */ - const char *exchange_base_url; - - /** - * Set (!) to the wire transfer subject observed. - */ - char *subject; - - } check_bank_transfer; - - struct { - - /** - * Amount that should be refunded. - */ - const char *amount; - - /** - * Expected refund fee. - */ - const char *fee; - - /** - * Reference to the corresponding deposit operation. - * Used to obtain proposal details, merchant keys, - * fee structure, etc. - */ - const char *deposit_ref; - - /** - * Refund transaction identifier. - */ - uint64_t rtransaction_id; - - /** - * Handle to the refund operation (while it is ongoing). - */ - struct TALER_EXCHANGE_RefundHandle *rh; - - } refund; - - struct { - - /** - * Reference to a _coin's_ withdraw operation where the coin's denomination key - * is the denomination key to be revoked. - */ - const char *ref; - - /** - * Process for the revocation process. - */ - struct GNUNET_OS_Process *revoke_proc; - - /** - * ID of task called whenever we get a SIGCHILD. - */ - struct GNUNET_SCHEDULER_Task *child_death_task; - - } revoke; - - struct { - - /** - * Reference to the _coin's_ withdraw operation. - */ - const char *ref; - - /** - * Amount that should be paid back. - */ - const char *amount; - - /** - * Handle to the ongoing /payback operation. - */ - struct TALER_EXCHANGE_PaybackHandle *ph; - - } payback; - - - } details; - -}; - - -/** - * State of the interpreter loop. - */ -struct InterpreterState -{ - /** - * Keys from the exchange. - */ - const struct TALER_EXCHANGE_Keys *keys; - - /** - * Commands the interpreter will run. - */ - struct Command *commands; - - /** - * Interpreter task (if one is scheduled). - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Instruction pointer. Tells #interpreter_run() which - * instruction to run next. - */ - unsigned int ip; - -}; - - -/** - * Pipe used to communicate child death via signal. - */ -static struct GNUNET_DISK_PipeHandle *sigpipe; - - -/** - * The testcase failed, return with an error code. - * - * @param is interpreter state to clean up - */ -static void -fail (struct InterpreterState *is) -{ - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Find a command by label. - * - * @param is interpreter state to search - * @param label label to look for - * @return NULL if command was not found - */ -static const struct Command * -find_command (const struct InterpreterState *is, - const char *label) -{ - unsigned int i; - const struct Command *cmd; - - if (NULL == label) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Attempt to lookup command for empty label\n"); - return NULL; - } - for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++) - if ( (NULL != cmd->label) && - (0 == strcmp (cmd->label, - label)) ) - return cmd; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command not found: %s\n", - label); - return NULL; -} - - -/** - * Run the main interpreter loop that performs exchange operations. - * - * @param cls contains the `struct InterpreterState` - */ -static void -interpreter_run (void *cls); - - -/** - * Run the next command with the interpreter. - * - * @param is current interpeter state. - */ -static void -next_command (struct InterpreterState *is) -{ - if (GNUNET_SYSERR == result) - return; /* ignore, we already failed! */ - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * 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 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) - */ -static void -add_incoming_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t serial_id, - const json_t *full_response) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.admin_add_incoming.aih = NULL; - cmd->details.admin_add_incoming.serial_id = serial_id; - if (MHD_HTTP_OK != http_status) - { - GNUNET_break (0); - fail (is); - return; - } - next_command (is); -} - - -/** - * Check if the given historic event @a h corresponds to the given - * command @a cmd. - * - * @param h event in history - * @param cmd an #OC_ADMIN_ADD_INCOMING command - * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not - */ -static int -compare_admin_add_incoming_history (const struct TALER_EXCHANGE_ReserveHistory *h, - const struct Command *cmd) -{ - struct TALER_Amount amount; - - if (TALER_EXCHANGE_RTT_DEPOSIT != h->type) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.admin_add_incoming.amount, - &amount)); - if (0 != TALER_amount_cmp (&amount, - &h->amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Check if the given historic event @a h corresponds to the given - * command @a cmd. - * - * @param h event in history - * @param cmd an #OC_WITHDRAW_SIGN command - * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not - */ -static int -compare_reserve_withdraw_history (const struct TALER_EXCHANGE_ReserveHistory *h, - const struct Command *cmd) -{ - struct TALER_Amount amount; - struct TALER_Amount amount_with_fee; - - if (TALER_EXCHANGE_RTT_WITHDRAWAL != h->type) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.reserve_withdraw.amount, - &amount)); - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&amount_with_fee, - &amount, - &cmd->details.reserve_withdraw.pk->fee_withdraw)); - if (0 != TALER_amount_cmp (&amount_with_fee, - &h->amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Check if the given historic event @a h corresponds to the given - * command @a cmd. - * - * @param h event in history - * @param cmd an #OC_WITHDRAW_SIGN command - * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not - */ -static int -compare_reserve_payback_history (const struct TALER_EXCHANGE_ReserveHistory *h, - const struct Command *cmd) -{ - struct TALER_Amount amount; - - if (TALER_EXCHANGE_RTT_PAYBACK != h->type) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.payback.amount, - &amount)); - if (0 != TALER_amount_cmp (&amount, - &h->amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called with the result of a /reserve/status 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 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 - */ -static 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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - struct Command *rel; - const struct Command *xrel; - unsigned int i; - unsigned int j; - struct TALER_Amount amount; - - cmd->details.reserve_status.wsh = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s\n", - http_status, - cmd->label); - GNUNET_break (0); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - /* FIXME: note that history events may come in a different - order than the commands. However, for now this works... */ - j = 0; - for (i=0;iip;i++) - { - switch ((rel = &is->commands[i])->oc) - { - case OC_ADMIN_ADD_INCOMING: - if ( ( (NULL != rel->label) && - (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->label) ) ) || - ( (NULL != rel->details.admin_add_incoming.reserve_reference) && - (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->details.admin_add_incoming.reserve_reference) ) ) ) - { - if ( (j >= history_length) || - (GNUNET_OK != - compare_admin_add_incoming_history (&history[j], - rel)) ) - { - GNUNET_break (0); - fail (is); - return; - } - j++; - } - break; - case OC_WITHDRAW_SIGN: - if (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->details.reserve_withdraw.reserve_reference)) - { - if ( (j >= history_length) || - (GNUNET_OK != - compare_reserve_withdraw_history (&history[j], - rel)) ) - { - GNUNET_break (0); - fail (is); - return; - } - j++; - } - break; - case OC_PAYBACK: - xrel = find_command (is, - rel->details.payback.ref); - GNUNET_assert (NULL != xrel); - if (0 == strcmp (cmd->details.reserve_status.reserve_reference, - xrel->details.reserve_withdraw.reserve_reference)) - { - if ( (j >= history_length) || - (GNUNET_OK != - compare_reserve_payback_history (&history[j], - rel)) ) - { - GNUNET_break (0); - fail (is); - return; - } - j++; - } - break; - default: - /* unreleated, just skip */ - break; - } - } - if (j != history_length) - { - GNUNET_break (0); - fail (is); - return; - } - if (NULL != cmd->details.reserve_status.expected_balance) - { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.reserve_status.expected_balance, - &amount)); - if (0 != TALER_amount_cmp (&amount, - balance)) - { - GNUNET_break (0); - fail (is); - return; - } - } - break; - default: - /* Unsupported status code (by test harness) */ - GNUNET_break (0); - break; - } - next_command (is); -} - - -/** - * Function called upon completion of our /reserve/withdraw 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 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) - */ -static void -reserve_withdraw_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_DenominationSignature *sig, - const json_t *full_response) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.reserve_withdraw.wsh = NULL; - if (cmd->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); - GNUNET_break (0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - if (NULL == sig) - { - GNUNET_break (0); - fail (is); - return; - } - cmd->details.reserve_withdraw.sig.rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - break; - case MHD_HTTP_FORBIDDEN: - /* nothing to check */ - break; - case MHD_HTTP_NOT_FOUND: - /* nothing to check */ - break; - default: - /* Unsupported status code (by test harness) */ - GNUNET_break (0); - break; - } - next_command (is); -} - - -/** - * 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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.deposit.dh = NULL; - if (cmd->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 (obj, stderr, 0); - fail (is); - return; - } - next_command (is); -} - - -/** - * 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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.refresh_melt.rmh = NULL; - if (cmd->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); - fail (is); - return; - } - cmd->details.refresh_melt.noreveal_index = noreveal_index; - next_command (is); -} - - -/** - * 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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - unsigned int i; - - cmd->details.refresh_reveal.rrh = NULL; - if (cmd->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); - fail (is); - return; - } - ref = find_command (is, - cmd->details.refresh_reveal.melt_ref); - GNUNET_assert (NULL != ref); - cmd->details.refresh_reveal.num_fresh_coins = num_coins; - switch (http_status) - { - case MHD_HTTP_OK: - cmd->details.refresh_reveal.fresh_coins - = GNUNET_new_array (num_coins, - struct FreshCoin); - for (i=0;idetails.refresh_reveal.fresh_coins[i]; - - fc->pk = ref->details.refresh_melt.fresh_pks[i]; - fc->coin_priv = coin_privs[i]; - fc->sig.rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sigs[i].rsa_signature); - } - break; - default: - break; - } - next_command (is); -} - - -/** - * 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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - unsigned int found; - - cmd->details.refresh_link.rlh = NULL; - if (cmd->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); - fail (is); - return; - } - ref = find_command (is, - cmd->details.refresh_link.reveal_ref); - GNUNET_assert (NULL != ref); - switch (http_status) - { - case MHD_HTTP_OK: - /* check that number of coins returned matches */ - if (num_coins != ref->details.refresh_reveal.num_fresh_coins) - { - GNUNET_break (0); - fail (is); - return; - } - /* check that the coins match */ - for (unsigned int i=0;idetails.refresh_reveal.fresh_coins[j]; - if ( (0 == memcmp (&coin_privs[i], - &fc->coin_priv, - sizeof (struct TALER_CoinSpendPrivateKeyP))) && - (0 == GNUNET_CRYPTO_rsa_signature_cmp (fc->sig.rsa_signature, - sigs[i].rsa_signature)) && - (0 == GNUNET_CRYPTO_rsa_public_key_cmp (fc->pk->key.rsa_public_key, - pubs[i].rsa_public_key)) ) - { - found++; - break; - } - } - if (found != num_coins) - { - fprintf (stderr, - "Only %u/%u coins match expectations\n", - found, - num_coins); - GNUNET_break (0); - fail (is); - return; - } - break; - default: - break; - } - next_command (is); -} - - -/** - * Task triggered whenever we receive a SIGCHLD (child - * process died). - * - * @param cls closure, NULL if we need to self-restart - */ -static void -maint_child_death (void *cls) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct GNUNET_DISK_FileHandle *pr; - char c[16]; - - switch (cmd->oc) { - case OC_RUN_AGGREGATOR: - cmd->details.run_aggregator.child_death_task = NULL; - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); - GNUNET_OS_process_wait (cmd->details.run_aggregator.aggregator_proc); - GNUNET_OS_process_destroy (cmd->details.run_aggregator.aggregator_proc); - cmd->details.run_aggregator.aggregator_proc = NULL; - break; - case OC_RUN_WIREWATCH: - cmd->details.run_wirewatch.child_death_task = NULL; - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); - GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc); - GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc); - cmd->details.run_wirewatch.wirewatch_proc = NULL; - break; - case OC_REVOKE: - cmd->details.revoke.child_death_task = NULL; - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); - GNUNET_OS_process_wait (cmd->details.revoke.revoke_proc); - GNUNET_OS_process_destroy (cmd->details.revoke.revoke_proc); - cmd->details.revoke.revoke_proc = NULL; - /* trigger reload of denomination key information */ - GNUNET_break (0 == - GNUNET_OS_process_kill (exchanged, - SIGUSR1)); - sleep (5); /* make sure signal was received and processed */ - break; - default: - GNUNET_break (0); - fail (is); - return; - } - next_command (is); -} - - -/** - * Find denomination key matching the given amount. - * - * @param keys array of keys to search - * @param amount coin value to look for - * @return NULL if no matching key was found - */ -static const struct TALER_EXCHANGE_DenomPublicKey * -find_pk (const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_Amount *amount) -{ - struct GNUNET_TIME_Absolute now; - struct TALER_EXCHANGE_DenomPublicKey *pk; - char *str; - - now = GNUNET_TIME_absolute_get (); - for (unsigned int i=0;inum_denom_keys;i++) - { - pk = &keys->denom_keys[i]; - if ( (0 == TALER_amount_cmp (amount, - &pk->value)) && - (now.abs_value_us >= pk->valid_from.abs_value_us) && - (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) ) - return pk; - } - /* do 2nd pass to check if expiration times are to blame for failure */ - str = TALER_amount_to_string (amount); - for (unsigned int i=0;inum_denom_keys;i++) - { - pk = &keys->denom_keys[i]; - if ( (0 == TALER_amount_cmp (amount, - &pk->value)) && - ( (now.abs_value_us < pk->valid_from.abs_value_us) || - (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Have denomination key for `%s', but with wrong expiration range %llu vs [%llu,%llu)\n", - str, - (unsigned long long) now.abs_value_us, - (unsigned long long) pk->valid_from.abs_value_us, - (unsigned long long) pk->withdraw_valid_until.abs_value_us); - GNUNET_free (str); - return NULL; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No denomination key for amount %s found\n", - str); - GNUNET_free (str); - return NULL; -} - - -#if LEGACY -/* Tests the *old* /wire API, the _modern_ testcase was adapted, - but little point in right now adapting the old testcase */ -/** - * Function called with information about the wire fees - * for each wire method. - * - * @param cls closure - * @param wire_method name of the wire method (i.e. "ebics") - * @param fees fee structure for this method - */ -static void -check_fee_cb (void *cls, - const char *wire_method, - const struct TALER_EXCHANGE_WireAggregateFees *fees) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - struct TALER_Amount expected_amount; - - GNUNET_break ( (0 == strcasecmp ("x-taler-bank", - wire_method)) || - (0 == strcasecmp ("ebics", - wire_method)) ); - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.wire.expected_fee, - &expected_amount)) - { - GNUNET_break (0); - fail (is); - return; - } - while (NULL != fees) - { - if (0 != TALER_amount_cmp (&fees->wire_fee, - &expected_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fee missmatch to command %s\n", - cmd->label); - fail (is); - return; - } - fees = fees->next; - } -} -#endif - - -/** - * 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 accounts_len length of the @a accounts array - * @param accounts list of wire accounts of the exchange, NULL on error - */ -static void -wire_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - unsigned int accounts_len, - const struct TALER_EXCHANGE_WireAccount *accounts) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.wire.wh = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u to command %s\n", - http_status, - cmd->label); -#if LEGACY - json_dumpf (obj, stderr, 0); -#endif - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - { -#if LEGACY - json_t *method; - - method = json_object_get (obj, - cmd->details.wire.format); - if (NULL == method) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expected method `%s' not included in response to command %s\n", - cmd->details.wire.format, - cmd->label); - json_dumpf (obj, stderr, 0); - fail (is); - return; - } - if (GNUNET_OK != - TALER_EXCHANGE_wire_get_fees (&TALER_EXCHANGE_get_keys (exchange)->master_pub, - obj, - &check_fee_cb, - is)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fee extraction in command %s failed\n", - cmd->label); - json_dumpf (obj, stderr, 0); - fail (is); - return; - } -#endif - } - break; - default: - break; - } - next_command (is); -} - - -/** - * 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 -wire_deposits_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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - struct TALER_Amount expected_amount; - - cmd->details.wire_deposits.wdh = NULL; - if (cmd->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); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.wire_deposits.total_amount_expected, - &expected_amount)) - { - GNUNET_break (0); - fail (is); - return; - } - if (0 != TALER_amount_cmp (total_amount, - &expected_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Total amount missmatch to command %s\n", - cmd->label); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.wire_deposits.wire_fee_expected, - &expected_amount)) - { - GNUNET_break (0); - 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); - fail (is); - return; - } - ref = find_command (is, - cmd->details.wire_deposits.wtid_ref); - GNUNET_assert (NULL != ref); - switch (ref->oc) - { - case OC_DEPOSIT_WTID: - if (NULL != ref->details.deposit_wtid.deposit_ref) - { - const struct Command *dep; - struct GNUNET_HashCode hw; - json_t *wire; - - dep = find_command (is, - ref->details.deposit_wtid.deposit_ref); - GNUNET_assert (NULL != dep); - wire = json_loads (dep->details.deposit.wire_details, - JSON_REJECT_DUPLICATES, - NULL); - GNUNET_assert (GNUNET_OK == - TALER_JSON_wire_signature_hash (wire, - &hw)); - json_decref (wire); - if (0 != memcmp (&hw, - h_wire, - sizeof (struct GNUNET_HashCode))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire hash missmatch to command %s\n", - cmd->label); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - } - break; - case OC_CHECK_BANK_TRANSFER: - if (GNUNET_OK != - TALER_string_to_amount (ref->details.check_bank_transfer.amount, - &expected_amount)) - { - GNUNET_break (0); - fail (is); - return; - } - if (0 != TALER_amount_cmp (total_amount, - &expected_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Total amount missmatch to command %s\n", - cmd->label); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - break; - default: - GNUNET_break (0); - fail (is); - return; - } - break; - default: - break; - } - next_command (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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.deposit_wtid.dwh = NULL; - if (cmd->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); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - cmd->details.deposit_wtid.wtid = *wtid; - if (NULL != cmd->details.deposit_wtid.bank_transfer_ref) - { - const struct Command *ref; - char *ws; - - ws = GNUNET_STRINGS_data_to_string_alloc (wtid, - sizeof (*wtid)); - - - ref = find_command (is, - cmd->details.deposit_wtid.bank_transfer_ref); - GNUNET_assert (NULL != ref); - if (0 != strcmp (ws, - ref->details.check_bank_transfer.subject)) - { - GNUNET_break (0); - GNUNET_free (ws); - fail (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; - } - next_command (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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.refund.rh = NULL; - if (cmd->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 (obj, stderr, 0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - break; - default: - break; - } - next_command (is); -} - - - -/** - * 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 InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *withdraw; - const struct Command *reserve; - struct TALER_Amount expected_amount; - struct TALER_ReservePublicKeyP rp; - - cmd->details.payback.ph = NULL; - if (cmd->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); - fail (is); - return; - } - withdraw = find_command (is, - cmd->details.payback.ref); - GNUNET_assert (NULL != withdraw); - reserve = find_command (is, - withdraw->details.reserve_withdraw.reserve_reference); - GNUNET_assert (NULL != reserve); - GNUNET_CRYPTO_eddsa_key_get_public (&reserve->details.admin_add_incoming.reserve_priv.eddsa_priv, - &rp.eddsa_pub); - switch (http_status) - { - case MHD_HTTP_OK: - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.payback.amount, - &expected_amount)) - { - GNUNET_break (0); - 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); - fail (is); - return; - } - if (0 != memcmp (reserve_pub, - &rp, - sizeof (rp))) - { - GNUNET_break (0); - fail (is); - return; - } - break; - default: - break; - } - next_command (is); -} - - -/** - * Given a command that is used to withdraw coins, - * extract the corresponding public key of the coin. - * - * @param coin command relating to coin withdrawal or refresh - * @param idx index to use if we got multiple coins from the @a coin command - * @param[out] coin_pub where to store the public key of the coin - */ -static void -get_public_key_from_coin_command (const struct Command *coin, - unsigned int idx, - struct TALER_CoinSpendPublicKeyP *coin_pub) -{ - switch (coin->oc) - { - case OC_WITHDRAW_SIGN: - GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.ps.coin_priv.eddsa_priv, - &coin_pub->eddsa_pub); - break; - case OC_REFRESH_REVEAL: - { - const struct FreshCoin *fc; - - GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins); - fc = &coin->details.refresh_reveal.fresh_coins[idx]; - - GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, - &coin_pub->eddsa_pub); - } - break; - default: - GNUNET_assert (0); - } -} - - -/** - * Run the main interpreter loop that performs exchange operations. - * - * @param cls contains the `struct InterpreterState` - */ -static void -interpreter_run (void *cls) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount amount; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - is->task = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { - fprintf (stderr, - "Test aborted by shutdown request\n"); - fail (is); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running command `%s'\n", - cmd->label); - switch (cmd->oc) - { - case OC_END: - result = GNUNET_OK; - GNUNET_SCHEDULER_shutdown (); - return; - case OC_ADMIN_ADD_INCOMING: - { - char *subject; - struct TALER_BANK_AuthenticationData auth; - - if (NULL != - cmd->details.admin_add_incoming.subject) - { - subject = GNUNET_strdup (cmd->details.admin_add_incoming.subject); - } - else - { - /* Use reserve public key as subject */ - if (NULL != - cmd->details.admin_add_incoming.reserve_reference) - { - ref = find_command (is, - cmd->details.admin_add_incoming.reserve_reference); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - cmd->details.admin_add_incoming.reserve_priv - = ref->details.admin_add_incoming.reserve_priv; - } - else - { - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv; - GNUNET_free (priv); - } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub, - sizeof (reserve_pub)); - } - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.admin_add_incoming.amount, - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.admin_add_incoming.amount, - is->ip); - fail (is); - return; - } - auth.method = TALER_BANK_AUTH_BASIC; - auth.details.basic.username = (char *) cmd->details.admin_add_incoming.auth_username; - auth.details.basic.password = (char *) cmd->details.admin_add_incoming.auth_password; - cmd->details.admin_add_incoming.aih - = TALER_BANK_admin_add_incoming (ctx, - "http://localhost:8082/", /* bank URL */ - &auth, - "http://localhost:8081/", /* exchange URL */ - subject, - &amount, - cmd->details.admin_add_incoming.debit_account_no, - cmd->details.admin_add_incoming.credit_account_no, - &add_incoming_cb, - is); - GNUNET_free (subject); - if (NULL == cmd->details.admin_add_incoming.aih) - { - GNUNET_break (0); - fail (is); - return; - } - } - return; - case OC_WITHDRAW_STATUS: - GNUNET_assert (NULL != - cmd->details.reserve_status.reserve_reference); - ref = find_command (is, - cmd->details.reserve_status.reserve_reference); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - cmd->details.reserve_status.wsh - = TALER_EXCHANGE_reserve_status (exchange, - &reserve_pub, - &reserve_status_cb, - is); - return; - case OC_WITHDRAW_SIGN: - GNUNET_assert (NULL != - cmd->details.reserve_withdraw.reserve_reference); - ref = find_command (is, - cmd->details.reserve_withdraw.reserve_reference); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - if (NULL != cmd->details.reserve_withdraw.amount) - { - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.reserve_withdraw.amount, - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.reserve_withdraw.amount, - is->ip); - fail (is); - return; - } - cmd->details.reserve_withdraw.pk = find_pk (is->keys, - &amount); - } - if (NULL == cmd->details.reserve_withdraw.pk) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to determine denomination key at %u\n", - is->ip); - fail (is); - return; - } - - TALER_planchet_setup_random (&cmd->details.reserve_withdraw.ps); - cmd->details.reserve_withdraw.wsh - = TALER_EXCHANGE_reserve_withdraw (exchange, - cmd->details.reserve_withdraw.pk, - &ref->details.admin_add_incoming.reserve_priv, - &cmd->details.reserve_withdraw.ps, - &reserve_withdraw_cb, - is); - if (NULL == cmd->details.reserve_withdraw.wsh) - { - GNUNET_break (0); - fail (is); - return; - } - return; - case OC_DEPOSIT: - { - struct GNUNET_HashCode h_contract_terms; - const struct TALER_CoinSpendPrivateKeyP *coin_priv; - const struct TALER_EXCHANGE_DenomPublicKey *coin_pk; - const struct TALER_DenominationSignature *coin_pk_sig; - struct TALER_CoinSpendPublicKeyP coin_pub; - 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 *priv; - struct TALER_MerchantPublicKeyP merchant_pub; - json_t *contract_terms; - json_t *wire; - - GNUNET_assert (NULL != - cmd->details.deposit.coin_ref); - ref = find_command (is, - cmd->details.deposit.coin_ref); - GNUNET_assert (NULL != ref); - switch (ref->oc) - { - case OC_WITHDRAW_SIGN: - coin_priv = &ref->details.reserve_withdraw.ps.coin_priv; - coin_pk = ref->details.reserve_withdraw.pk; - coin_pk_sig = &ref->details.reserve_withdraw.sig; - break; - case OC_REFRESH_REVEAL: - { - const struct FreshCoin *fc; - unsigned int idx; - - idx = cmd->details.deposit.coin_idx; - GNUNET_assert (idx < ref->details.refresh_reveal.num_fresh_coins); - fc = &ref->details.refresh_reveal.fresh_coins[idx]; - - coin_priv = &fc->coin_priv; - coin_pk = fc->pk; - coin_pk_sig = &fc->sig; - } - break; - default: - GNUNET_assert (0); - } - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.deposit.amount, - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.deposit.amount, - is->ip); - fail (is); - return; - } - contract_terms = json_loads (cmd->details.deposit.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", - cmd->details.deposit.contract_terms, - is->ip, - cmd->label); - fail (is); - return; - } - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (contract_terms, - &h_contract_terms)); - json_decref (contract_terms); - wire = json_loads (cmd->details.deposit.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", - cmd->details.deposit.wire_details, - is->ip, - cmd->label); - fail (is); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin_pub.eddsa_pub); - - priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.deposit.merchant_priv.eddsa_priv = *priv; - GNUNET_free (priv); - if (0 != cmd->details.deposit.refund_deadline.rel_value_us) - { - refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline); - wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (cmd->details.deposit.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 (&cmd->details.deposit.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_wire_signature_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, - &coin_pk->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)); - } - cmd->details.deposit.dh - = TALER_EXCHANGE_deposit (exchange, - &amount, - wire_deadline, - wire, - &h_contract_terms, - &coin_pub, - coin_pk_sig, - &coin_pk->key, - timestamp, - &merchant_pub, - refund_deadline, - &coin_sig, - &deposit_cb, - is); - if (NULL == cmd->details.deposit.dh) - { - GNUNET_break (0); - json_decref (wire); - fail (is); - return; - } - json_decref (wire); - return; - } - case OC_REFRESH_MELT: - { - unsigned int num_fresh_coins; - - cmd->details.refresh_melt.noreveal_index = UINT16_MAX; - for (num_fresh_coins=0; - NULL != cmd->details.refresh_melt.fresh_amounts[num_fresh_coins]; - num_fresh_coins++) ; - - cmd->details.refresh_melt.fresh_pks - = GNUNET_new_array (num_fresh_coins, - const struct TALER_EXCHANGE_DenomPublicKey *); - { - struct TALER_CoinSpendPrivateKeyP melt_priv; - struct TALER_Amount melt_amount; - struct TALER_DenominationSignature melt_sig; - struct TALER_EXCHANGE_DenomPublicKey melt_pk; - struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins]; - unsigned int i; - - const struct MeltDetails *md = &cmd->details.refresh_melt.melted_coin; - ref = find_command (is, - md->coin_ref); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); - - melt_priv = ref->details.reserve_withdraw.ps.coin_priv; - if (GNUNET_OK != - TALER_string_to_amount (md->amount, - &melt_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - md->amount, - is->ip); - fail (is); - return; - } - melt_sig = ref->details.reserve_withdraw.sig; - melt_pk = *ref->details.reserve_withdraw.pk; - for (i=0;idetails.refresh_melt.fresh_amounts[i], - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.reserve_withdraw.amount, - is->ip); - fail (is); - return; - } - if (NULL == - (cmd->details.refresh_melt.fresh_pks[i] - = find_pk (is->keys, - &amount))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to find denomination key for amount `%s' at %u\n", - cmd->details.reserve_withdraw.amount, - is->ip); - fail (is); - return; - } - fresh_pks[i] = *cmd->details.refresh_melt.fresh_pks[i]; - } - cmd->details.refresh_melt.refresh_data - = TALER_EXCHANGE_refresh_prepare (&melt_priv, - &melt_amount, - &melt_sig, - &melt_pk, - GNUNET_YES, - num_fresh_coins, - fresh_pks, - &cmd->details.refresh_melt.refresh_data_length); - if (NULL == cmd->details.refresh_melt.refresh_data) - { - GNUNET_break (0); - fail (is); - return; - } - cmd->details.refresh_melt.rmh - = TALER_EXCHANGE_refresh_melt (exchange, - cmd->details.refresh_melt.refresh_data_length, - cmd->details.refresh_melt.refresh_data, - &melt_cb, - is); - if (NULL == cmd->details.refresh_melt.rmh) - { - GNUNET_break (0); - fail (is); - return; - } - } - } - return; - case OC_REFRESH_REVEAL: - ref = find_command (is, - cmd->details.refresh_reveal.melt_ref); - GNUNET_assert (NULL != ref); - cmd->details.refresh_reveal.rrh - = TALER_EXCHANGE_refresh_reveal (exchange, - ref->details.refresh_melt.refresh_data_length, - ref->details.refresh_melt.refresh_data, - ref->details.refresh_melt.noreveal_index, - &reveal_cb, - is); - if (NULL == cmd->details.refresh_reveal.rrh) - { - GNUNET_break (0); - fail (is); - return; - } - return; - case OC_REFRESH_LINK: - /* find reveal command */ - ref = find_command (is, - cmd->details.refresh_link.reveal_ref); - GNUNET_assert (NULL != ref); - /* find melt command */ - ref = find_command (is, - ref->details.refresh_reveal.melt_ref); - GNUNET_assert (NULL != ref); - /* find reserve_withdraw command */ - { - const struct MeltDetails *md; - - md = &ref->details.refresh_melt.melted_coin; - ref = find_command (is, - md->coin_ref); - GNUNET_assert (NULL != ref); - } - GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); - /* finally, use private key from withdraw sign command */ - cmd->details.refresh_link.rlh - = TALER_EXCHANGE_refresh_link (exchange, - &ref->details.reserve_withdraw.ps.coin_priv, - &link_cb, - is); - if (NULL == cmd->details.refresh_link.rlh) - { - GNUNET_break (0); - fail (is); - return; - } - return; - case OC_WIRE: - cmd->details.wire.wh = TALER_EXCHANGE_wire (exchange, - &wire_cb, - is); - return; - case OC_WIRE_DEPOSITS: - if (NULL != cmd->details.wire_deposits.wtid_ref) - { - ref = find_command (is, - cmd->details.wire_deposits.wtid_ref); - GNUNET_assert (NULL != ref); - switch (ref->oc) - { - case OC_DEPOSIT_WTID: - cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid; - break; - case OC_CHECK_BANK_TRANSFER: - GNUNET_assert (GNUNET_OK == - GNUNET_STRINGS_string_to_data (ref->details.check_bank_transfer.subject, - strlen (ref->details.check_bank_transfer.subject), - &cmd->details.wire_deposits.wtid, - sizeof (cmd->details.wire_deposits.wtid))); - break; - default: - GNUNET_break (0); - fail (is); - return; - } - } - cmd->details.wire_deposits.wdh - = TALER_EXCHANGE_track_transfer (exchange, - &cmd->details.wire_deposits.wtid, - &wire_deposits_cb, - is); - return; - case OC_DEPOSIT_WTID: - { - struct GNUNET_HashCode h_wire; - struct GNUNET_HashCode h_contract_terms; - json_t *wire; - json_t *contract_terms; - const struct Command *coin; - struct TALER_CoinSpendPublicKeyP coin_pub; - - ref = find_command (is, - cmd->details.deposit_wtid.deposit_ref); - GNUNET_assert (NULL != ref); - coin = find_command (is, - ref->details.deposit.coin_ref); - GNUNET_assert (NULL != coin); - get_public_key_from_coin_command (coin, - ref->details.deposit.coin_idx, - &coin_pub); - wire = json_loads (ref->details.deposit.wire_details, - JSON_REJECT_DUPLICATES, - NULL); - GNUNET_assert (NULL != wire); - GNUNET_assert (GNUNET_OK == - TALER_JSON_wire_signature_hash (wire, - &h_wire)); - json_decref (wire); - contract_terms = json_loads (ref->details.deposit.contract_terms, - JSON_REJECT_DUPLICATES, - NULL); - GNUNET_assert (NULL != contract_terms); - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (contract_terms, - &h_contract_terms)); - json_decref (contract_terms); - cmd->details.deposit_wtid.dwh - = TALER_EXCHANGE_track_transaction (exchange, - &ref->details.deposit.merchant_priv, - &h_wire, - &h_contract_terms, - &coin_pub, - &deposit_wtid_cb, - is); - } - return; - case OC_RUN_AGGREGATOR: - { - const struct GNUNET_DISK_FileHandle *pr; - - cmd->details.run_aggregator.aggregator_proc - = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-aggregator", - "taler-exchange-aggregator", - "-c", "test_exchange_api.conf", - "-t", /* exit when done */ - NULL); - if (NULL == cmd->details.run_aggregator.aggregator_proc) - { - GNUNET_break (0); - fail (is); - return; - } - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - cmd->details.run_aggregator.child_death_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, - &maint_child_death, is); - return; - } - case OC_RUN_WIREWATCH: - { - const struct GNUNET_DISK_FileHandle *pr; - - cmd->details.run_wirewatch.wirewatch_proc - = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-wirewatch", - "taler-exchange-wirewatch", - "-c", "test_exchange_api.conf", - "-T", /* exit when done */ - NULL); - if (NULL == cmd->details.run_wirewatch.wirewatch_proc) - { - GNUNET_break (0); - fail (is); - return; - } - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - cmd->details.run_wirewatch.child_death_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, - &maint_child_death, is); - return; - } - case OC_CHECK_BANK_TRANSFER: - { - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.check_bank_transfer.amount, - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.reserve_withdraw.amount, - is->ip); - fail (is); - return; - } - if (GNUNET_OK != - TALER_FAKEBANK_check (fakebank, - &amount, - cmd->details.check_bank_transfer.account_debit, - cmd->details.check_bank_transfer.account_credit, - cmd->details.check_bank_transfer.exchange_base_url, - &cmd->details.check_bank_transfer.subject)) - { - GNUNET_break (0); - fail (is); - return; - } - next_command (is); - return; - } - case OC_CHECK_BANK_TRANSFERS_EMPTY: - { - if (GNUNET_OK != - TALER_FAKEBANK_check_empty (fakebank)) - { - GNUNET_break (0); - fail (is); - return; - } - next_command (is); - return; - } - case OC_REFUND: - { - const struct Command *coin; - struct GNUNET_HashCode h_contract_terms; - json_t *contract_terms; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_Amount refund_fee; - - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.refund.amount, - &amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.refund.amount, - is->ip); - fail (is); - return; - } - if (GNUNET_OK != - TALER_string_to_amount (cmd->details.refund.fee, - &refund_fee)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - cmd->details.refund.fee, - is->ip); - fail (is); - return; - } - ref = find_command (is, - cmd->details.refund.deposit_ref); - GNUNET_assert (NULL != ref); - contract_terms = json_loads (ref->details.deposit.contract_terms, - JSON_REJECT_DUPLICATES, - NULL); - GNUNET_assert (NULL != contract_terms); - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (contract_terms, - &h_contract_terms)); - json_decref (contract_terms); - - coin = find_command (is, - ref->details.deposit.coin_ref); - GNUNET_assert (NULL != coin); - get_public_key_from_coin_command (coin, - ref->details.deposit.coin_idx, - &coin_pub); - cmd->details.refund.rh - = TALER_EXCHANGE_refund (exchange, - &amount, - &refund_fee, - &h_contract_terms, - &coin_pub, - cmd->details.refund.rtransaction_id, - &ref->details.deposit.merchant_priv, - &refund_cb, - is); - if (NULL == cmd->details.refund.rh) - { - GNUNET_break (0); - fail (is); - return; - } - return; - } - case OC_REVOKE: - { - const struct GNUNET_DISK_FileHandle *pr; - char *dhks; - const struct Command *ref; - - ref = find_command (is, - cmd->details.revoke.ref); - GNUNET_assert (NULL != ref); - dhks = GNUNET_STRINGS_data_to_string_alloc (&ref->details.reserve_withdraw.pk->h_key, - sizeof (struct GNUNET_HashCode)); - cmd->details.revoke.revoke_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", - "-r", dhks, - NULL); - GNUNET_free (dhks); - if (NULL == cmd->details.revoke.revoke_proc) - { - GNUNET_break (0); - fail (is); - return; - } - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - cmd->details.revoke.child_death_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, - &maint_child_death, - is); - return; - } - case OC_PAYBACK: - { - const struct Command *ref; - - ref = find_command (is, - cmd->details.payback.ref); - GNUNET_assert (NULL != ref); - cmd->details.payback.ph - = TALER_EXCHANGE_payback (exchange, - ref->details.reserve_withdraw.pk, - &ref->details.reserve_withdraw.sig, - &ref->details.reserve_withdraw.ps, - &payback_cb, - is); - return; - } - - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown instruction %d at %u (%s)\n", - cmd->oc, - is->ip, - cmd->label); - fail (is); - return; - } -} - - -/** - * Signal handler called for SIGCHLD. Triggers the - * respective handler by writing to the trigger pipe. - */ -static void -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))); - errno = old_errno; /* restore errno */ -} - - -/** - * Function run when the test terminates (good or bad) with timeout. - * - * @param cls NULL - */ -static void -do_timeout (void *cls) -{ - timeout_task = NULL; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Function run when the test terminates (good or bad). - * Cleans up our state. - * - * @param cls the interpreter state. - */ -static void -do_shutdown (void *cls) -{ - struct InterpreterState *is = cls; - struct Command *cmd; - - fprintf (stderr, - "Executing shutdown at `%s'\n", - is->commands[is->ip].label); - - for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++) - { - switch (cmd->oc) - { - case OC_END: - GNUNET_assert (0); - break; - case OC_ADMIN_ADD_INCOMING: - if (NULL != cmd->details.admin_add_incoming.aih) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_BANK_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih); - cmd->details.admin_add_incoming.aih = NULL; - } - break; - case OC_WITHDRAW_STATUS: - if (NULL != cmd->details.reserve_status.wsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_reserve_status_cancel (cmd->details.reserve_status.wsh); - cmd->details.reserve_status.wsh = NULL; - } - break; - case OC_WITHDRAW_SIGN: - if (NULL != cmd->details.reserve_withdraw.wsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh); - cmd->details.reserve_withdraw.wsh = NULL; - } - if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature); - cmd->details.reserve_withdraw.sig.rsa_signature = NULL; - } - break; - case OC_DEPOSIT: - if (NULL != cmd->details.deposit.dh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_deposit_cancel (cmd->details.deposit.dh); - cmd->details.deposit.dh = NULL; - } - break; - case OC_REFRESH_MELT: - if (NULL != cmd->details.refresh_melt.rmh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_refresh_melt_cancel (cmd->details.refresh_melt.rmh); - cmd->details.refresh_melt.rmh = NULL; - } - GNUNET_free_non_null (cmd->details.refresh_melt.fresh_pks); - cmd->details.refresh_melt.fresh_pks = NULL; - GNUNET_free_non_null (cmd->details.refresh_melt.refresh_data); - cmd->details.refresh_melt.refresh_data = NULL; - cmd->details.refresh_melt.refresh_data_length = 0; - break; - case OC_REFRESH_REVEAL: - if (NULL != cmd->details.refresh_reveal.rrh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_refresh_reveal_cancel (cmd->details.refresh_reveal.rrh); - cmd->details.refresh_reveal.rrh = NULL; - } - { - unsigned int j; - struct FreshCoin *fresh_coins; - - fresh_coins = cmd->details.refresh_reveal.fresh_coins; - for (j=0;jdetails.refresh_reveal.num_fresh_coins;j++) - GNUNET_CRYPTO_rsa_signature_free (fresh_coins[j].sig.rsa_signature); - } - GNUNET_free_non_null (cmd->details.refresh_reveal.fresh_coins); - cmd->details.refresh_reveal.fresh_coins = NULL; - cmd->details.refresh_reveal.num_fresh_coins = 0; - break; - case OC_REFRESH_LINK: - if (NULL != cmd->details.refresh_link.rlh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_refresh_link_cancel (cmd->details.refresh_link.rlh); - cmd->details.refresh_link.rlh = NULL; - } - break; - case OC_WIRE: - if (NULL != cmd->details.wire.wh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_wire_cancel (cmd->details.wire.wh); - cmd->details.wire.wh = NULL; - } - break; - case OC_WIRE_DEPOSITS: - if (NULL != cmd->details.wire_deposits.wdh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_track_transfer_cancel (cmd->details.wire_deposits.wdh); - cmd->details.wire_deposits.wdh = NULL; - } - break; - case OC_DEPOSIT_WTID: - if (NULL != cmd->details.deposit_wtid.dwh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_track_transaction_cancel (cmd->details.deposit_wtid.dwh); - cmd->details.deposit_wtid.dwh = NULL; - } - break; - case OC_RUN_AGGREGATOR: - if (NULL != cmd->details.run_aggregator.aggregator_proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (cmd->details.run_aggregator.aggregator_proc, - SIGKILL)); - GNUNET_OS_process_wait (cmd->details.run_aggregator.aggregator_proc); - GNUNET_OS_process_destroy (cmd->details.run_aggregator.aggregator_proc); - cmd->details.run_aggregator.aggregator_proc = NULL; - } - if (NULL != cmd->details.run_aggregator.child_death_task) - { - GNUNET_SCHEDULER_cancel (cmd->details.run_aggregator.child_death_task); - cmd->details.run_aggregator.child_death_task = NULL; - } - break; - case OC_RUN_WIREWATCH: - if (NULL != cmd->details.run_wirewatch.wirewatch_proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (cmd->details.run_wirewatch.wirewatch_proc, - SIGKILL)); - GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc); - GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc); - cmd->details.run_wirewatch.wirewatch_proc = NULL; - } - if (NULL != cmd->details.run_wirewatch.child_death_task) - { - GNUNET_SCHEDULER_cancel (cmd->details.run_wirewatch.child_death_task); - cmd->details.run_wirewatch.child_death_task = NULL; - } - break; - case OC_CHECK_BANK_TRANSFER: - GNUNET_free_non_null (cmd->details.check_bank_transfer.subject); - cmd->details.check_bank_transfer.subject = NULL; - break; - case OC_CHECK_BANK_TRANSFERS_EMPTY: - break; - case OC_REFUND: - if (NULL != cmd->details.refund.rh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_refund_cancel (cmd->details.refund.rh); - cmd->details.refund.rh = NULL; - } - break; - case OC_REVOKE: - if (NULL != cmd->details.revoke.revoke_proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (cmd->details.revoke.revoke_proc, - SIGKILL)); - GNUNET_OS_process_wait (cmd->details.revoke.revoke_proc); - GNUNET_OS_process_destroy (cmd->details.revoke.revoke_proc); - cmd->details.revoke.revoke_proc = NULL; - } - if (NULL != cmd->details.revoke.child_death_task) - { - GNUNET_SCHEDULER_cancel (cmd->details.revoke.child_death_task); - cmd->details.revoke.child_death_task = NULL; - } - break; - case OC_PAYBACK: - if (NULL != cmd->details.payback.ph) - { - TALER_EXCHANGE_payback_cancel (cmd->details.payback.ph); - cmd->details.payback.ph = NULL; - } - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown instruction %d at %u (%s)\n", - cmd->oc, - i, - cmd->label); - break; - } - } - if (NULL != is->task) - { - GNUNET_SCHEDULER_cancel (is->task); - is->task = NULL; - } - GNUNET_free (is); - if (NULL != fakebank) - { - TALER_FAKEBANK_stop (fakebank); - fakebank = NULL; - } - if (NULL != exchange) - { - TALER_EXCHANGE_disconnect (exchange); - exchange = NULL; - } - if (NULL != ctx) - { - GNUNET_CURL_fini (ctx); - ctx = NULL; - } - if (NULL != rc) - { - GNUNET_CURL_gnunet_rc_destroy (rc); - rc = NULL; - } - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } -} - - -/** - * Functions of this type are called to provide the retrieved signing and - * denomination keys of the exchange. No TALER_EXCHANGE_*() functions should be called - * in this callback. - * - * @param cls closure - * @param keys information about keys of the exchange - * @param vc version compatibility - */ -static void -cert_cb (void *cls, - const struct TALER_EXCHANGE_Keys *keys, - enum TALER_EXCHANGE_VersionCompatibility vc) -{ - struct InterpreterState *is = cls; - - /* check that keys is OK */ -#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0) - ERR (NULL == keys); - ERR (0 == keys->num_sign_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u signing keys\n", - keys->num_sign_keys); - ERR (0 == keys->num_denom_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u denomination keys\n", - keys->num_denom_keys); -#undef ERR - - /* run actual tests via interpreter-loop */ - is->keys = keys; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure - */ -static void -run (void *cls) -{ - struct InterpreterState *is; - static const char *melt_fresh_amounts_1[] = { - "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", - /* 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*/ - NULL - }; - static struct Command commands[] = - { - /* *************** start of /wire testing ************** */ - -#if WIRE_TEST - { .oc = OC_WIRE, - .label = "wire-test", - /* expecting 'test' method in response */ - .expected_response_code = MHD_HTTP_OK, - .details.wire.format = "x-taler-bank", - .details.wire.expected_fee = "EUR:0.01" }, -#endif -#if WIRE_EBICS - { .oc = OC_WIRE, - .label = "wire-sepa", - /* expecting 'ebics' method in response */ - .expected_response_code = MHD_HTTP_OK, - .details.wire.format = "ebics", - .details.wire.expected_fee = "EUR:0.01" }, -#endif - /* *************** end of /wire testing ************** */ - -#if WIRE_TEST - /* None of this works if 'test' is not allowed as we do - /admin/add/incoming with format 'test' */ - - /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-1", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:5.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-1" }, - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "create-reserve-1", - .details.reserve_withdraw.amount = "EUR:5" }, - /* Check that deposit and withdraw operation are in history, and - that the balance is now at zero */ - { .oc = OC_WITHDRAW_STATUS, - .label = "withdraw-status-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_status.reserve_reference = "create-reserve-1", - .details.reserve_status.expected_balance = "EUR:0" }, - /* Try to deposit the 5 EUR coin (in full) */ - { .oc = OC_DEPOSIT, - .label = "deposit-simple", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" }, - - /* Try to overdraw funds ... */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-2", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.reserve_withdraw.reserve_reference = "create-reserve-1", - .details.reserve_withdraw.amount = "EUR:5" }, - - /* Try to double-spend the 5 EUR coin with different wire details */ - { .oc = OC_DEPOSIT, - .label = "deposit-double-1", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" }, - /* Try to double-spend the 5 EUR coin at the same merchant (but different - transaction ID) */ - { .oc = OC_DEPOSIT, - .label = "deposit-double-2", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" }, - /* Try to double-spend the 5 EUR coin at the same merchant (but different - proposal) */ - { .oc = OC_DEPOSIT, - .label = "deposit-double-3", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }" }, - - /* ***************** /refresh testing ******************** */ - - /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "refresh-create-reserve-1", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 424, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user424", - .details.admin_add_incoming.auth_password = "pass424", - .details.admin_add_incoming.amount = "EUR:5.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-2" }, - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "refresh-withdraw-coin-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "refresh-create-reserve-1", - .details.reserve_withdraw.amount = "EUR:5" }, - /* 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) */ - { .oc = OC_DEPOSIT, - .label = "refresh-deposit-partial", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:1", - .details.deposit.coin_ref = "refresh-withdraw-coin-1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:1\" } ] }" }, - - /* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ - - { .oc = OC_REFRESH_MELT, - .label = "refresh-melt-1", - .expected_response_code = MHD_HTTP_OK, - .details.refresh_melt.melted_coin = { - .amount = "EUR:4", - .coin_ref = "refresh-withdraw-coin-1" }, - .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 }, - - - /* Complete (successful) melt operation, and withdraw the coins */ - { .oc = OC_REFRESH_REVEAL, - .label = "refresh-reveal-1", - .expected_response_code = MHD_HTTP_OK, - .details.refresh_reveal.melt_ref = "refresh-melt-1" }, - - /* do it again to check idempotency */ - { .oc = OC_REFRESH_REVEAL, - .label = "refresh-reveal-1-idempotency", - .expected_response_code = MHD_HTTP_OK, - .details.refresh_reveal.melt_ref = "refresh-melt-1" }, - - /* Test that /refresh/link works */ - { .oc = OC_REFRESH_LINK, - .label = "refresh-link-1", - .expected_response_code = MHD_HTTP_OK, - .details.refresh_link.reveal_ref = "refresh-reveal-1" }, - - - /* Test successfully spending coins from the refresh operation: - first EUR:1 */ - { .oc = OC_DEPOSIT, - .label = "refresh-deposit-refreshed-1a", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:1", - .details.deposit.coin_ref = "refresh-reveal-1-idempotency", - .details.deposit.coin_idx = 0, - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }" }, - - /* Test successfully spending coins from the refresh operation: - finally EUR:0.1 */ - { .oc = OC_DEPOSIT, - .label = "refresh-deposit-refreshed-1b", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:0.1", - .details.deposit.coin_ref = "refresh-reveal-1", - .details.deposit.coin_idx = 4, - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }" }, - - /* Test running a failing melt operation (same operation again must fail) */ - { .oc = OC_REFRESH_MELT, - .label = "refresh-melt-failing", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.refresh_melt.melted_coin = { - .amount = "EUR:4", - .coin_ref = "refresh-withdraw-coin-1" }, - .details.refresh_melt.fresh_amounts = melt_fresh_amounts_1 }, - - // 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. */ - { .oc = OC_DEPOSIT_WTID, - .label = "deposit-wtid-found", - .expected_response_code = MHD_HTTP_ACCEPTED, - .details.deposit_wtid.deposit_ref = "deposit-simple" }, - /* 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. */ - { .oc = OC_DEPOSIT_WTID, - .label = "deposit-wtid-failing", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.deposit_wtid.deposit_ref = "deposit-double-2" }, - /* Try resolving an undefined (all zeros) WTID; this - should fail as obviously the exchange didn't use that - WTID value for any transaction. */ - { .oc = OC_WIRE_DEPOSITS, - .label = "wire-deposit-failing", - .expected_response_code = 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! */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator" }, - - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-499c", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:4.98", - .details.check_bank_transfer.account_debit = 2, - .details.check_bank_transfer.account_credit = 42 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-99c1", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:0.98", - .details.check_bank_transfer.account_debit = 2, - .details.check_bank_transfer.account_credit = 42 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-99c2", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:0.98", - .details.check_bank_transfer.account_debit = 2, - .details.check_bank_transfer.account_credit = 42 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-9c", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:0.08", - .details.check_bank_transfer.account_debit = 2, - .details.check_bank_transfer.account_credit = 43 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-aai-1", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:5.01", - .details.check_bank_transfer.account_debit = 42, - .details.check_bank_transfer.account_credit = 2 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-aai-2", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:5.01", - .details.check_bank_transfer.account_debit = 424, - .details.check_bank_transfer.account_credit = 2 - }, - - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty" }, - { .oc = OC_DEPOSIT_WTID, - .label = "deposit-wtid-ok", - .expected_response_code = MHD_HTTP_OK, - .details.deposit_wtid.deposit_ref = "deposit-simple", - .details.deposit_wtid.bank_transfer_ref = "check_bank_transfer-499c" }, - - { .oc = OC_WIRE_DEPOSITS, - .label = "wire-deposits-success-bank", - .expected_response_code = MHD_HTTP_OK, - .details.wire_deposits.wtid_ref = "check_bank_transfer-99c1", - .details.wire_deposits.total_amount_expected = "EUR:0.98", - .details.wire_deposits.wire_fee_expected = "EUR:0.01" }, - - { .oc = OC_WIRE_DEPOSITS, - .label = "wire-deposits-success-wtid", - .expected_response_code = MHD_HTTP_OK, - .details.wire_deposits.wtid_ref = "deposit-wtid-ok", - .details.wire_deposits.total_amount_expected = "EUR:4.98", - .details.wire_deposits.wire_fee_expected = "EUR:0.01" }, - - /* ************** End of tracking API testing************* */ - - /* ************** Test /refund API ************* */ - - - /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-r1", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:5.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-3" }, - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-r1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "create-reserve-r1", - .details.reserve_withdraw.amount = "EUR:5" }, - /* Spend 5 EUR of the 5 EUR coin (in full) - (merchant would receive EUR:4.99 due to 1 ct deposit fee) */ - { .oc = OC_DEPOSIT, - .label = "deposit-refund-1", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-r1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }", - .details.deposit.refund_deadline = { 60LL * 1000 * 1000 } /* 60 s */, - }, - /* Run transfers. Should do nothing as refund deadline blocks it */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-refund" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-aai-3", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:5.01", - .details.check_bank_transfer.account_debit = 42, - .details.check_bank_transfer.account_credit = 2 - }, - /* check that aggregator didn't do anything, as expected */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check-refund-not-run" }, - /* Trigger refund */ - { .oc = OC_REFUND, - .label = "refund-ok", - .expected_response_code = MHD_HTTP_OK, - .details.refund.amount = "EUR:5", - .details.refund.fee = "EUR:0.01", - .details.refund.deposit_ref = "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) */ - { .oc = OC_DEPOSIT, - .label = "deposit-refund-2", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:4.99", - .details.deposit.coin_ref = "withdraw-coin-r1", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"more ice cream\", \"value\":\"EUR:5\" } ] }", - }, - /* Run transfers. This will do the transfer as refund deadline was 0 */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-3" }, - /* Check that deposit did run */ - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-pre-refund", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:4.97", - .details.check_bank_transfer.account_debit = 2, - .details.check_bank_transfer.account_credit = 42 - }, - /* Run failing refund, as past deadline & aggregation */ - { .oc = OC_REFUND, - .label = "refund-fail", - .expected_response_code = MHD_HTTP_GONE, - .details.refund.amount = "EUR:4.99", - .details.refund.fee = "EUR:0.01", - .details.refund.deposit_ref = "deposit-refund-2", - }, - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check-empty-after-refund" }, - - - /* Test refunded coins are never executed, even past - refund deadline */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-rb", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:5.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-3b" }, - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-rb", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "create-reserve-rb", - .details.reserve_withdraw.amount = "EUR:5" }, - /* Spend 5 EUR of the 5 EUR coin (in full) - (merchant would receive EUR:4.99 due to 1 ct deposit fee) */ - { .oc = OC_DEPOSIT, - .label = "deposit-refund-1b", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:5", - .details.deposit.coin_ref = "withdraw-coin-rb", - .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }", - .details.deposit.refund_deadline = { 0 }, - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }" - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-aai-3b", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:5.01", - .details.check_bank_transfer.account_debit = 42, - .details.check_bank_transfer.account_credit = 2 - }, - /* Trigger refund (before aggregator had a chance to execute - deposit, even though refund deadline was zero) */ - { .oc = OC_REFUND, - .label = "refund-ok-fast", - .expected_response_code = MHD_HTTP_OK, - .details.refund.amount = "EUR:5", - .details.refund.fee = "EUR:0.01", - .details.refund.deposit_ref = "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. */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-3b" }, - /* check that aggregator didn't do anything, as expected */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "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, - then withdraw a coin and then have it be paid back. */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "payback-create-reserve-1", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:5.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-4" }, - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "payback-withdraw-coin-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "payback-create-reserve-1", - .details.reserve_withdraw.amount = "EUR:5" }, - { .oc = OC_REVOKE, - .label = "revoke-1", - .expected_response_code = MHD_HTTP_OK, - .details.revoke.ref = "payback-withdraw-coin-1" }, - { .oc = OC_PAYBACK, - .label = "payback-1", - .expected_response_code = MHD_HTTP_OK, - .details.payback.ref = "payback-withdraw-coin-1", - .details.payback.amount = "EUR:5" }, - /* Check the money is back with the reserve */ - { .oc = OC_WITHDRAW_STATUS, - .label = "payback-reserve-status-1", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_status.reserve_reference = "payback-create-reserve-1", - .details.reserve_status.expected_balance = "EUR:5.00" }, - - - /* 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...) */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "payback-create-reserve-2", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:2.02" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-5" }, - /* Withdraw a 1 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "payback-withdraw-coin-2a", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "payback-create-reserve-2", - .details.reserve_withdraw.amount = "EUR:1" }, - /* Withdraw a 1 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "payback-withdraw-coin-2b", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference = "payback-create-reserve-2", - .details.reserve_withdraw.amount = "EUR:1" }, - - { .oc = OC_DEPOSIT, - .label = "payback-deposit-partial", - .expected_response_code = MHD_HTTP_OK, - .details.deposit.amount = "EUR:0.5", - .details.deposit.coin_ref = "payback-withdraw-coin-2a", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice cream\", \"value\":1 } ] }" }, - { .oc = OC_REVOKE, - .label = "revoke-2", - .expected_response_code = MHD_HTTP_OK, - .details.revoke.ref = "payback-withdraw-coin-2a" }, - { .oc = OC_PAYBACK, - .label = "payback-2", - .expected_response_code = MHD_HTTP_OK, - .details.payback.ref = "payback-withdraw-coin-2a", - .details.payback.amount = "EUR:0.5" }, - { .oc = OC_PAYBACK, - .label = "payback-2b", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.payback.ref = "payback-withdraw-coin-2a", - .details.payback.amount = "EUR:0.5" }, - { .oc = OC_DEPOSIT, - .label = "payback-deposit-revoked", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.deposit.amount = "EUR:1", - .details.deposit.coin_ref = "payback-withdraw-coin-2b", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice cream\", \"value\":1 } ] }" }, - - /* 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! */ - { .oc = OC_DEPOSIT, - .label = "payback-deposit-partial-after-payback", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.deposit.amount = "EUR:0.5", - .details.deposit.coin_ref = "payback-withdraw-coin-2a", - .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", - .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"extra ice cream\", \"value\":1 } ] }" }, - - - /* Test that revoked coins cannot be withdrawn */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "payback-create-reserve-3", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:1.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-6" }, - { .oc = OC_WITHDRAW_SIGN, - .label = "payback-withdraw-coin-3-revoked", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.reserve_withdraw.reserve_reference = "payback-create-reserve-3", - .details.reserve_withdraw.amount = "EUR:1" }, - - /* check that we are empty before the rejection test */ - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-pr1", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:5.01", - .details.check_bank_transfer.account_debit = 42, - .details.check_bank_transfer.account_credit = 2 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-pr2", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:2.02", - .details.check_bank_transfer.account_debit = 42, - .details.check_bank_transfer.account_credit = 2 - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-pr3", - .details.check_bank_transfer.exchange_base_url = "http://localhost:8081/", - .details.check_bank_transfer.amount = "EUR:1.01", - .details.check_bank_transfer.account_debit = 42, - .details.check_bank_transfer.account_credit = 2 - }, - - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check-empty-again" }, - - /* Test rejection of bogus wire transfers */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "bogus-subject", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.subject = "not a reserve public key", - .details.admin_add_incoming.debit_account_no = 42, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user42", - .details.admin_add_incoming.auth_password = "pass42", - .details.admin_add_incoming.amount = "EUR:1.01" }, - /* Run wirewatch to observe rejection */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-7" }, - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check-empty-from-reject" }, - - - /* ************** End of payback API testing************* */ -#endif - - { .oc = OC_END } - }; - - is = GNUNET_new (struct InterpreterState); - is->commands = commands; - - ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, - &rc); - GNUNET_assert (NULL != ctx); - rc = GNUNET_CURL_gnunet_rc_create (ctx); - fakebank = TALER_FAKEBANK_start (8082); - exchange = TALER_EXCHANGE_connect (ctx, - "http://localhost:8081", - &cert_cb, - is, - TALER_EXCHANGE_OPTION_END); - GNUNET_assert (NULL != exchange); - timeout_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 300), - &do_timeout, NULL); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); -} - - -/** - * Remove files from previous runs - */ -static void -cleanup_files () -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - char *dir; - - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - "test_exchange_api.conf")) - { - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - exit (77); - } - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "keydir", - &dir)); - if (GNUNET_YES == - GNUNET_DISK_directory_test (dir, - GNUNET_NO)) - GNUNET_break (GNUNET_OK == - GNUNET_DISK_directory_remove (dir)); - GNUNET_free (dir); - GNUNET_CONFIGURATION_destroy (cfg); -} - - -/** - * Main function for the testcase for the exchange API. - * - * @param argc expected to be 1 - * @param argv expected to only contain the program name - */ -int -main (int argc, - char * const *argv) -{ - struct GNUNET_OS_Process *proc; - struct GNUNET_SIGNAL_Context *shc_chld; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - unsigned int iter; - - /* These might get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api", - "INFO", - NULL); - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - 8081)) - { - fprintf (stderr, - "Required port %u not available, skipping.\n", - 8081); - return 77; - } - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - 8082)) - { - fprintf (stderr, - "Required port %u not available, skipping.\n", - 8082); - return 77; - } - cleanup_files (); - - 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", - "-o", "auditor.in", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); - return 77; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - - 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", - "-u", "http://auditor/", - "-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG", - "-r", "auditor.in", - "-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); - return 77; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-dbinit", - "taler-exchange-dbinit", - "-c", "test_exchange_api.conf", - "-r", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-dbinit`, is your PATH correct?\n"); - return 77; - } - if (GNUNET_SYSERR == - GNUNET_OS_process_wait_status (proc, - &type, - &code)) - { - GNUNET_break (0); - GNUNET_OS_process_destroy (proc); - return 1; - } - GNUNET_OS_process_destroy (proc); - if ( (type == GNUNET_OS_PROCESS_EXITED) && - (0 != code) ) - { - fprintf (stderr, - "Failed to setup database\n"); - return 77; - } - if ( (type != GNUNET_OS_PROCESS_EXITED) || - (0 != code) ) - { - fprintf (stderr, - "Unexpected error running `taler-exchange-dbinit'!\n"); - return 1; - } - exchanged = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-httpd", - "taler-exchange-httpd", - "-c", "test_exchange_api.conf", - "-i", - NULL); - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `taler-exchange-httpd' to be ready"); - iter = 0; - do - { - if (10 == iter) - { - fprintf (stderr, - "Failed to launch `taler-exchange-httpd' (or `wget')\n"); - GNUNET_OS_process_kill (exchanged, - SIGTERM); - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); - return 77; - } - fprintf (stderr, "."); - sleep (1); - iter++; - } - while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null")); - fprintf (stderr, "\n"); - - result = GNUNET_NO; - 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); - GNUNET_SCHEDULER_run (&run, NULL); - GNUNET_SIGNAL_handler_uninstall (shc_chld); - shc_chld = NULL; - GNUNET_DISK_pipe_close (sigpipe); - GNUNET_break (0 == - GNUNET_OS_process_kill (exchanged, - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (exchanged)); - GNUNET_OS_process_destroy (exchanged); - return (GNUNET_OK == result) ? 0 : 1; -} - -/* end of test_exchange_api.c */ diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.c b/src/exchange-lib/test_exchange_api_keys_cherry_picking.c deleted file mode 100644 index 0dc0c540a..000000000 --- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.c +++ /dev/null @@ -1,803 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2017 GNUnet e.V. and Inria - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see -*/ -/** - * @file exchange/test_exchange_api_keys_cherry_picking.c - * @brief testcase to test exchange's /keys cherry picking ability - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_signatures.h" -#include "taler_exchange_service.h" -#include "taler_json_lib.h" -#include -#include - - -/** - * Main execution context for the main loop. - */ -static struct GNUNET_CURL_Context *ctx; - -/** - * Handle to access the exchange. - */ -static struct TALER_EXCHANGE_Handle *exchange; - -/** - * Context for running the CURL event loop. - */ -static struct GNUNET_CURL_RescheduleContext *rc; - -/** - * Handle to the exchange process. - */ -static struct GNUNET_OS_Process *exchanged; - -/** - * Task run on timeout. - */ -static struct GNUNET_SCHEDULER_Task *timeout_task; - -/** - * Result of the testcases, #GNUNET_OK on success - */ -static int result; - - -/** - * Opcodes for the interpreter. - */ -enum OpCode -{ - /** - * Termination code, stops the interpreter loop (with success). - */ - OC_END = 0, - - /** - * Run a process. - */ - OC_RUN_PROCESS, - - /** - * Signal the exchange to reload the keys. - */ - OC_SIGNAL_EXCHANGE, - - /** - * Check the /keys. - */ - OC_CHECK_KEYS - -}; - - -/** - * Details for a exchange operation to execute. - */ -struct Command -{ - /** - * Opcode of the command. - */ - enum OpCode oc; - - /** - * Label for the command, can be NULL. - */ - const char *label; - - /** - * Details about the command. - */ - union - { - - struct { - - /** - * Binary to execute. - */ - const char *binary; - - /** - * Command-line arguments for the process to be run. - */ - char *const *argv; - - /** - * Process handle. - */ - struct GNUNET_OS_Process *proc; - - /** - * ID of task called whenever we get a SIGCHILD. - */ - struct GNUNET_SCHEDULER_Task *child_death_task; - - } run_process; - - struct { - - /** - * Expected number of denomination keys. - */ - unsigned int num_denom_keys; - - /** - * Which generation of /keys are we verifying here? - * Used to make sure we got the right number of - * interactions. - */ - unsigned int generation; - - } check_keys; - - } details; - -}; - - -/** - * State of the interpreter loop. - */ -struct InterpreterState -{ - /** - * Keys from the exchange. - */ - const struct TALER_EXCHANGE_Keys *keys; - - /** - * Commands the interpreter will run. - */ - struct Command *commands; - - /** - * Interpreter task (if one is scheduled). - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Instruction pointer. Tells #interpreter_run() which - * instruction to run next. - */ - unsigned int ip; - - /** - * 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; - -}; - - -/** - * Pipe used to communicate child death via signal. - */ -static struct GNUNET_DISK_PipeHandle *sigpipe; - - -/** - * The testcase failed, return with an error code. - * - * @param is interpreter state to clean up - */ -static void -fail (struct InterpreterState *is) -{ - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Run the main interpreter loop that performs exchange operations. - * - * @param cls contains the `struct InterpreterState` - */ -static void -interpreter_run (void *cls); - - -/** - * Run the next command with the interpreter. - * - * @param is current interpeter state. - */ -static void -next_command (struct InterpreterState *is) -{ - if (GNUNET_SYSERR == result) - return; /* ignore, we already failed! */ - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * Task triggered whenever we receive a SIGCHLD (child - * process died). - * - * @param cls closure, NULL if we need to self-restart - */ -static void -maint_child_death (void *cls) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct GNUNET_DISK_FileHandle *pr; - char c[16]; - - switch (cmd->oc) { - case OC_RUN_PROCESS: - cmd->details.run_process.child_death_task = NULL; - pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); - GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); - GNUNET_OS_process_wait (cmd->details.run_process.proc); - GNUNET_OS_process_destroy (cmd->details.run_process.proc); - cmd->details.run_process.proc = NULL; - break; - default: - GNUNET_break (0); - fail (is); - return; - } - next_command (is); -} - - -/** - * Run the main interpreter loop that performs exchange operations. - * - * @param cls contains the `struct InterpreterState` - */ -static void -interpreter_run (void *cls) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - is->task = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { - fprintf (stderr, - "Test aborted by shutdown request\n"); - fail (is); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running command `%s'\n", - cmd->label); - switch (cmd->oc) - { - case OC_END: - result = GNUNET_OK; - GNUNET_SCHEDULER_shutdown (); - return; - case OC_RUN_PROCESS: - { - const struct GNUNET_DISK_FileHandle *pr; - - cmd->details.run_process.proc - = GNUNET_OS_start_process_vap (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - cmd->details.run_process.binary, - cmd->details.run_process.argv); - if (NULL == cmd->details.run_process.proc) - { - GNUNET_break (0); - fail (is); - return; - } - pr = GNUNET_DISK_pipe_handle (sigpipe, - GNUNET_DISK_PIPE_END_READ); - cmd->details.run_process.child_death_task - = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - pr, - &maint_child_death, - is); - return; - } - case OC_SIGNAL_EXCHANGE: - { - GNUNET_break (0 == - GNUNET_OS_process_kill (exchanged, - SIGUSR1)); - /* give exchange time to process the signal */ - sleep (1); - next_command (is); - return; - } - case OC_CHECK_KEYS: - { - if (is->key_generation < cmd->details.check_keys.generation) - { - /* Go back to waiting for /keys signal! */ - is->working = GNUNET_NO; - GNUNET_break (0 == - TALER_EXCHANGE_check_keys_current (exchange, - GNUNET_YES).abs_value_us); - return; - } - if (is->key_generation > cmd->details.check_keys.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); - fail (is); - return; - } - /* /keys was updated, let's check they were OK! */ - if (cmd->details.check_keys.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); - fail (is); - return; - } - next_command (is); - return; - } - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown instruction %d at %u (%s)\n", - cmd->oc, - is->ip, - cmd->label); - fail (is); - return; - } -} - - -/** - * Signal handler called for SIGCHLD. Triggers the - * respective handler by writing to the trigger pipe. - */ -static void -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))); - errno = old_errno; /* restore errno */ -} - - -/** - * Function run when the test terminates (good or bad) with timeout. - * - * @param cls NULL - */ -static void -do_timeout (void *cls) -{ - timeout_task = NULL; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Function run when the test terminates (good or bad). - * Cleans up our state. - * - * @param cls the interpreter state. - */ -static void -do_shutdown (void *cls) -{ - struct InterpreterState *is = cls; - struct Command *cmd; - - for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++) - { - switch (cmd->oc) - { - case OC_END: - GNUNET_assert (0); - break; - case OC_RUN_PROCESS: - if (NULL != cmd->details.run_process.proc) - { - GNUNET_break (0 == - GNUNET_OS_process_kill (cmd->details.run_process.proc, - SIGKILL)); - GNUNET_OS_process_wait (cmd->details.run_process.proc); - GNUNET_OS_process_destroy (cmd->details.run_process.proc); - cmd->details.run_process.proc = NULL; - } - if (NULL != cmd->details.run_process.child_death_task) - { - GNUNET_SCHEDULER_cancel (cmd->details.run_process.child_death_task); - cmd->details.run_process.child_death_task = NULL; - } - break; - case OC_SIGNAL_EXCHANGE: - /* nothing to do */ - break; - case OC_CHECK_KEYS: - /* nothing to do */ - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown instruction %d at %u (%s)\n", - cmd->oc, - i, - cmd->label); - break; - } - } - if (NULL != is->task) - { - GNUNET_SCHEDULER_cancel (is->task); - is->task = NULL; - } - GNUNET_free (is); - if (NULL != exchange) - { - TALER_EXCHANGE_disconnect (exchange); - exchange = NULL; - } - if (NULL != ctx) - { - GNUNET_CURL_fini (ctx); - ctx = NULL; - } - if (NULL != rc) - { - GNUNET_CURL_gnunet_rc_destroy (rc); - rc = NULL; - } - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } -} - - -/** - * Functions of this type are called to provide the retrieved signing and - * denomination keys of the exchange. No TALER_EXCHANGE_*() functions should be called - * in this callback. - * - * @param cls closure - * @param keys information about keys of the exchange - * @param vc version compatibility - */ -static void -cert_cb (void *cls, - const struct TALER_EXCHANGE_Keys *keys, - enum TALER_EXCHANGE_VersionCompatibility vc) -{ - struct InterpreterState *is = cls; - - /* check that keys is OK */ -#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0) - ERR (NULL == keys); - ERR (0 == keys->num_sign_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u signing keys\n", - keys->num_sign_keys); - ERR (0 == keys->num_denom_keys); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %u denomination keys\n", - keys->num_denom_keys); -#undef ERR - - /* run actual tests via interpreter-loop */ - is->keys = keys; - if (GNUNET_YES == is->working) - return; - is->working = GNUNET_YES; - is->key_generation++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure - */ -static void -run (void *cls) -{ - struct InterpreterState *is; - static char *keyup[] = { - "taler-exchange-keyup", - "-c", "test_exchange_api_keys_cherry_picking_extended.conf", - "-o", "auditor.in", - NULL - }; - static char *auditorsign[] = { - "taler-auditor-sign", - "-c", "test_exchange_api_keys_cherry_picking.conf", - "-u", "http://auditor/", - "-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG", - "-r", "auditor.in", - "-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out", - NULL - }; - static struct Command commands[] = - { - /* Test signal handling by itself */ - { .oc = OC_SIGNAL_EXCHANGE }, - /* Check we got /keys properly */ - { .oc = OC_CHECK_KEYS, - .details.check_keys.generation = 1, - .details.check_keys.num_denom_keys = 4 - }, - /* Generate more keys */ - { .oc = OC_RUN_PROCESS, - .details.run_process.binary = "taler-exchange-keyup", - .details.run_process.argv = keyup - }, - /* Auditor-sign them */ - { .oc = OC_RUN_PROCESS, - .details.run_process.binary = "taler-auditor-sign", - .details.run_process.argv = auditorsign - }, - /* Load new keys into exchange via signal */ - { .oc = OC_SIGNAL_EXCHANGE }, - /* Re-download and check /keys */ - { .oc = OC_CHECK_KEYS, - .details.check_keys.generation = 2, -#if TALER_EXCHANGE_API_DISABLE_CHERRYPICKING - .details.check_keys.num_denom_keys = 12 -#else - .details.check_keys.num_denom_keys = 8 -#endif - }, - { .oc = OC_END } - }; - - is = GNUNET_new (struct InterpreterState); - is->commands = commands; - - ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, - &rc); - GNUNET_assert (NULL != ctx); - rc = GNUNET_CURL_gnunet_rc_create (ctx); - exchange = TALER_EXCHANGE_connect (ctx, - "http://localhost:8081", - &cert_cb, - is, - TALER_EXCHANGE_OPTION_END); - GNUNET_assert (NULL != exchange); - timeout_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 300), - &do_timeout, - NULL); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - is); -} - - -/** - * Remove files from previous runs - */ -static void -cleanup_files () -{ - struct GNUNET_CONFIGURATION_Handle *cfg; - char *dir; - - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - "test_exchange_api.conf")) - { - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - exit (77); - } - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "keydir", - &dir)); - if (GNUNET_YES == - GNUNET_DISK_directory_test (dir, - GNUNET_NO)) - GNUNET_break (GNUNET_OK == - GNUNET_DISK_directory_remove (dir)); - GNUNET_free (dir); - GNUNET_CONFIGURATION_destroy (cfg); -} - - -/** - * Main function for the testcase for the exchange API. - * - * @param argc expected to be 1 - * @param argv expected to only contain the program name - */ -int -main (int argc, - char * const *argv) -{ - struct GNUNET_OS_Process *proc; - struct GNUNET_SIGNAL_Context *shc_chld; - enum GNUNET_OS_ProcessStatusType type; - unsigned long code; - unsigned int iter; - - /* These might get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-exchange-api-keys-cherry-picking", - "INFO", - NULL); - if (GNUNET_OK != - GNUNET_NETWORK_test_port_free (IPPROTO_TCP, - 8081)) - { - fprintf (stderr, - "Required port %u not available, skipping.\n", - 8081); - return 77; - } - cleanup_files (); - - 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_keys_cherry_picking.conf", - "-o", "auditor.in", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); - return 77; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - - 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_keys_cherry_picking.conf", - "-u", "http://auditor/", - "-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG", - "-r", "auditor.in", - "-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-keyup`, is your PATH correct?\n"); - return 77; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - - proc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-dbinit", - "taler-exchange-dbinit", - "-c", "test_exchange_api_keys_cherry_picking.conf", - "-r", - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-dbinit`, is your PATH correct?\n"); - return 77; - } - if (GNUNET_SYSERR == - GNUNET_OS_process_wait_status (proc, - &type, - &code)) - { - GNUNET_break (0); - GNUNET_OS_process_destroy (proc); - return 1; - } - GNUNET_OS_process_destroy (proc); - if ( (type == GNUNET_OS_PROCESS_EXITED) && - (0 != code) ) - { - fprintf (stderr, - "Failed to setup database\n"); - return 77; - } - if ( (type != GNUNET_OS_PROCESS_EXITED) || - (0 != code) ) - { - fprintf (stderr, - "Unexpected error running `taler-exchange-dbinit'!\n"); - return 1; - } - exchanged = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-httpd", - "taler-exchange-httpd", - "-c", "test_exchange_api_keys_cherry_picking.conf", - "-i", - NULL); - /* give child time to start and bind against the socket */ - fprintf (stderr, - "Waiting for `taler-exchange-httpd' to be ready"); - iter = 0; - do - { - if (10 == iter) - { - fprintf (stderr, - "Failed to launch `taler-exchange-httpd' (or `wget')\n"); - GNUNET_OS_process_kill (exchanged, - SIGTERM); - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); - return 77; - } - fprintf (stderr, "."); - sleep (1); - iter++; - } - while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null")); - fprintf (stderr, "\n"); - result = GNUNET_NO; - 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); - GNUNET_SCHEDULER_run (&run, NULL); - GNUNET_SIGNAL_handler_uninstall (shc_chld); - shc_chld = NULL; - GNUNET_DISK_pipe_close (sigpipe); - GNUNET_break (0 == - GNUNET_OS_process_kill (exchanged, - SIGTERM)); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (exchanged)); - GNUNET_OS_process_destroy (exchanged); - return (GNUNET_OK == result) ? 0 : 1; -} - -/* end of test_exchange_api_keys_cherry_picking.c */ 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 ba82c1c34..212e03d6b 100644 --- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf +++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf @@ -11,14 +11,14 @@ CURRENCY = EUR [exchange] # how long is one signkey valid? -signkey_duration = 4 weeks +signkey_duration = 5 seconds # how long are the signatures with the signkey valid? legal_duration = 2 years # how long do we provide to clients denomination and signing keys # ahead of time? -lookahead_provide = 4 weeks 1 day +lookahead_provide = 30 seconds # Keep it short so we can prolong later! LOOKAHEAD_SIGN = 60 s @@ -132,8 +132,8 @@ CLOSING-FEE-2026 = EUR:0.01 [coin_eur_ct_1] value = EUR:0.01 -duration_overlap = 5 s -duration_withdraw = 35 s +duration_overlap = 1 s +duration_withdraw = 25 s duration_spend = 40 s duration_legal = 60 s fee_withdraw = EUR:0.00 @@ -144,8 +144,8 @@ rsa_keysize = 1024 [coin_eur_ct_2] value = EUR:0.02 -duration_overlap = 5 s -duration_withdraw = 35 s +duration_overlap = 1 s +duration_withdraw = 25 s duration_spend = 40 s duration_legal = 60 s fee_withdraw = EUR:0.01 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 index d1fd25cf2..8c82f6381 100644 --- a/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c +++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c @@ -71,6 +71,9 @@ run (void *cls, 1, 4, is->exchange), + /* sleep a bit */ + TALER_TESTING_cmd_sleep ("sleep", + 25), /* 1st keyup happens at start-up */ TALER_TESTING_cmd_exec_keyup ("keyup-2", CONFIG_FILE_EXTENDED), @@ -81,11 +84,23 @@ run (void *cls, SIGUSR1), TALER_TESTING_cmd_check_keys ("check-keys-2", 2, -#if TALER_EXCHANGE_API_DISABLE_CHERRYPICKING - 12, -#else - 8, -#endif + 6, + is->exchange), + + /* sleep a bit */ + TALER_TESTING_cmd_sleep ("sleep", + 24), + /* Do 2nd keyup */ + TALER_TESTING_cmd_exec_keyup ("keyup-3", + CONFIG_FILE_EXTENDED), + TALER_TESTING_cmd_exec_auditor_sign ("sign-keys-2", + CONFIG_FILE), + TALER_TESTING_cmd_signal ("trigger-keys-reload-2", + is->exchanged, + SIGUSR1), + TALER_TESTING_cmd_check_keys ("check-keys-3", + 3, + 10, is->exchange), TALER_TESTING_cmd_end () }; diff --git a/src/exchange-lib/testing_api_cmd_check_keys.c b/src/exchange-lib/testing_api_cmd_check_keys.c index 56b062a10..8d91d3ece 100644 --- a/src/exchange-lib/testing_api_cmd_check_keys.c +++ b/src/exchange-lib/testing_api_cmd_check_keys.c @@ -71,8 +71,8 @@ check_keys_run (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "cmd `%s', key generation: %d\n", - cmd->label, is->key_generation); - + cmd->label, + is->key_generation); if (is->key_generation < cks->generation) { /* Go back to waiting for /keys signal! */ @@ -99,10 +99,11 @@ check_keys_run (void *cls, { /* Did not get the expected number of denomination keys! */ GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got %u keys in step %s\n", + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Got %u keys in step %s, expected %u\n", is->keys->num_denom_keys, - cmd->label); + cmd->label, + cks->num_denom_keys); TALER_TESTING_interpreter_fail (is); return; } diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index 9946eac49..22c72d5cc 100644 --- a/src/exchange-tools/taler-exchange-keyup.c +++ b/src/exchange-tools/taler-exchange-keyup.c @@ -639,6 +639,15 @@ get_cointype_params (const char *ct, return GNUNET_SYSERR; } GNUNET_TIME_round_rel (¶ms->duration_overlap); + if (params->duration_overlap.rel_value_us >= + params->duration_withdraw.rel_value_us) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + ct, + "duration_overlap", + "duration_overlap must be smaller than duration_withdraw!"); + return GNUNET_SYSERR; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (kcfg, ct, @@ -814,8 +823,9 @@ exchange_keys_update_cointype (void *cls, { dkf = get_cointype_file (&p, p.anchor); - GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + GNUNET_break (GNUNET_YES != + GNUNET_DISK_file_test (dkf)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Generating denomination key for type `%s', start %s at %s\n", coin_alias, GNUNET_STRINGS_absolute_time_to_string (p.anchor), diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c index 4d1f8888f..ce2eafa71 100644 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ b/src/exchange/taler-exchange-httpd_keystate.c @@ -1192,6 +1192,10 @@ build_keys_response (const struct ResponseFactoryContext *rfc, krd->cherry_pick_date = cherry_pick_date; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Creating /keys for cherry pick date %s\n", + GNUNET_STRINGS_absolute_time_to_string (cherry_pick_date)); + /* Initialize `rbc` */ memset (&rbc, 0, @@ -2062,6 +2066,11 @@ TEH_KS_handler_keys (struct TEH_RequestHandler *rh, sizeof (struct KeysResponseData), &krd_search_comparator); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Filtering /keys by cherry pick date %s found entry %u/%u\n", + GNUNET_STRINGS_absolute_time_to_string (last_issue_date), + (unsigned int) (krd - key_state->krd_array), + key_state->krd_array_length); if ( (NULL == krd) && (key_state->krd_array_length > 0) ) { diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 98223fa6c..d48dd8629 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -30,14 +30,6 @@ /* ********************* /keys *********************** */ - -/** - * Temporarily disable cherrypicking until it's fixed. - * See #5315. - */ -#define TALER_EXCHANGE_API_DISABLE_CHERRYPICKING 1 - - /** * List of possible options to be passed to * #TALER_EXCHANGE_connect(). @@ -170,11 +162,10 @@ struct TALER_EXCHANGE_AuditorInformation /** * Array of length @a num_denom_keys with the denomination - * keys audited by this auditor. Note that the array - * elements point to the same locations as the entries - * in the key's main `denom_keys` array. + * keys audited by this auditor. Offsets into the + * key's main `denom_keys` array. */ - const struct TALER_EXCHANGE_DenomPublicKey **denom_keys; + unsigned int *denom_key_offsets; };