From 07b2c92b570ee2d2dc9aa5a944ad15adf491fc69 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 8 Jun 2019 21:45:14 +0200 Subject: remove obsolete, duplicate test logic, rename _new.c to just .c --- src/lib/Makefile.am | 21 +- src/lib/test_merchant_api.c | 5995 ++++++--------------------------------- src/lib/test_merchant_api_new.c | 1083 ------- 3 files changed, 938 insertions(+), 6161 deletions(-) delete mode 100644 src/lib/test_merchant_api_new.c diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index b14e1764..9b168a7e 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -77,7 +77,6 @@ endif if HAVE_TALERFAKEBANK check_PROGRAMS = \ - test_merchant_api_new \ test_merchant_api if HAVE_TWISTER @@ -110,30 +109,14 @@ test_merchant_api_twisted_LDADD = \ -ljansson \ -ltalertwister -test_merchant_api_new_SOURCES = \ - test_merchant_api_new.c -test_merchant_api_new_LDADD = \ - $(top_srcdir)/src/backenddb/libtalermerchantdb.la \ - libtalermerchant.la \ - $(LIBGCRYPT_LIBS) \ - -ltalertesting \ - -ltalermerchanttesting \ - -ltalerfakebank \ - -ltalerbank \ - -ltalerexchange \ - -ltalerjson \ - -ltalerutil \ - -lgnunetjson \ - -lgnunetcurl \ - -lgnunetutil \ - -ljansson - test_merchant_api_SOURCES = \ test_merchant_api.c test_merchant_api_LDADD = \ $(top_srcdir)/src/backenddb/libtalermerchantdb.la \ libtalermerchant.la \ $(LIBGCRYPT_LIBS) \ + -ltalertesting \ + -ltalermerchanttesting \ -ltalerfakebank \ -ltalerbank \ -ltalerexchange \ diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c index 344112d9..67df2b93 100644 --- a/src/lib/test_merchant_api.c +++ b/src/lib/test_merchant_api.c @@ -1,4269 +1,204 @@ /* This file is part of TALER - Copyright (C) 2014-2017 Taler Systems SA + Copyright (C) 2014-2018 Taler Systems SA - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free Software - Foundation; either version 2.1, or (at your option) any later version. + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU Lesser General Public License along with - TALER; see the file COPYING.LGPL. If not, see + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + */ -/** - * @file merchant/test_merchant_api.c - * @brief testcase to test merchant's HTTP API interface - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include -#include -#include -#include -#include -#include -#include "taler_merchant_service.h" -#include "taler_merchantdb_lib.h" -#include -#include -#include - -/** - * URL under which the merchant is reachable during the testcase. - */ -#define MERCHANT_URL "http://localhost:8080" - -/** - * URL under which the exchange is reachable during the testcase. - */ -#define EXCHANGE_URL "http://localhost:8081/" - -/** - * Account number of the exchange at the bank. - */ -#define EXCHANGE_ACCOUNT_NO 2 - -/** - * Account number of the merchant at the bank. - */ -#define MERCHANT_ACCOUNT_NO 3 - -/** - * Customer account number. - */ -#define TIP_ACCOUNT_NO 62 - -/** - * Customer account number. - */ -#define USER_ACCOUNT_NO 63 - -/** - * URL of the bank. - */ -#define BANK_URL "http://localhost:8082/" - -/** - * On which port do we run the (fake) bank? - */ -#define BANK_PORT 8082 - -/** - * Max size allowed for an order. - */ -#define ORDER_MAX_SIZE 1000 - -#define RND_BLK(ptr) \ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) - - -/** - * Handle to database. - */ -static struct TALER_MERCHANTDB_Plugin *db; - -/** - * Configuration handle. - */ -struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Handle to access the exchange. - */ -static struct TALER_EXCHANGE_Handle *exchange; - -/** - * Main execution context for the main loop of the exchange. - */ -static struct GNUNET_CURL_Context *ctx; - -/** - * Array of instances to test against - */ -static char **instances; - -/** - * How many merchant instances this test runs - */ -static unsigned int ninstances = 0; - -/** - * Current instance - */ -static char *instance; - -/** - * Current instance key - */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *instance_priv; - -/** - * Current instance being tested - */ -static unsigned int instance_idx = 0; - -/** - * Task run on timeout. - */ -static struct GNUNET_SCHEDULER_Task *timeout_task; - -/** - * Context for running the #ctx's event loop. - */ -static struct GNUNET_CURL_RescheduleContext *rc; - -/** - * Handle to the fake bank service we run for the - * aggregator. - */ -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, - - /** - * Issue a GET /proposal to the backend. - */ - OC_PROPOSAL_LOOKUP, - - /** - * 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, - - /** - * Issue a PUT /proposal to the backend. - */ - OC_PROPOSAL, - - /** - * Pay with coins. - */ - OC_PAY, - - /** - * Resume pay operation with additional coins. - */ - OC_PAY_AGAIN, - - /** - * Abort payment with coins, requesting refund. - */ - OC_PAY_ABORT, - - /** - * Abort payment with coins, executing refund. - */ - OC_PAY_ABORT_REFUND, - - /** - * 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, - - /** - * Retrieve deposit details for a given wire transfer - */ - OC_TRACK_TRANSFER, - - /** - * Retrieve wire transfer details for a given transaction - */ - OC_TRACK_TRANSACTION, - - /** - * Test getting transactions based on timestamp - */ - OC_HISTORY, - - /** - * Test the increase of a order refund - */ - OC_REFUND_INCREASE, - - /** - * Test refund lookup - */ - OC_REFUND_LOOKUP, - - /** - * Authorize a tip. - */ - OC_TIP_AUTHORIZE, - - /** - * Pickup a tip. - */ - OC_TIP_PICKUP, - - /** - * Check pay status. - */ - OC_CHECK_PAYMENT, - - /** - * Query tip stats. - */ - OC_TIP_QUERY, - -}; - - -/** - * 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; - -}; - - -/** - * State of the interpreter loop. - */ -struct InterpreterState; - - -/** - * Internal withdraw handle used when withdrawing tips. - */ -struct WithdrawHandle -{ - - /** - * Withdraw operation this handle represents. - */ - struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; - - /** - * Interpreter state we are part of. - */ - struct InterpreterState *is; - - /** - * Offset of this withdraw operation in the current @e is command. - */ - unsigned int off; - -}; - - -/** - * 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; - - /** - * Instance to use if we are filling a tipping-reserve. In this - * case, @e reserve_priv is filled from the configuration instead - * of at random. Usually NULL (for random @e reserve_priv). - */ - const char *instance; - - /** - * 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 OC_PROPOSAL_LOOKUP command. - */ - struct - { - - /** - * Reference to the proposal we want to lookup. - */ - const char *proposal_reference; - - struct TALER_MERCHANT_ProposalLookupOperation *plo; - - } proposal_lookup; - - /** - * 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; - - /** - * Set (by the interpreter) to the planchet's secrets. - */ - struct TALER_PlanchetSecretsP ps; - - /** - * Withdraw handle (while operation is running). - */ - struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; - - } reserve_withdraw; - - /** - * Information for an #OC_PROPOSAL command. - */ - struct - { - - /** - * The order. - * It's dynamically generated because we need different transaction_id - * for different merchant instances. - */ - char order[ORDER_MAX_SIZE]; - - /** - * Handle to the active PUT /proposal operation, or NULL. - */ - struct TALER_MERCHANT_ProposalOperation *po; - - /** - * Handle to the active GET /proposal operation, or NULL. - */ - struct TALER_MERCHANT_ProposalLookupOperation *plo; - - /** - * Full contract in JSON, set by the /contract operation. - * FIXME: verify in the code that this bit is actually proposal - * data and not the whole proposal. - */ - json_t *contract_terms; - - /** - * Proposal's signature. - */ - struct TALER_MerchantSignatureP merchant_sig; - - /** - * Proposal data's hashcode. - */ - struct GNUNET_HashCode hash; - - /** - * The nonce set by the customer looking up the contract - * the first time. - */ - struct GNUNET_CRYPTO_EddsaPublicKey nonce; - - } proposal; - - /** - * Information for a #OC_PAY command. - */ - struct - { - - /** - * Reference to the contract. - */ - const char *contract_ref; - - /** - * ";"-separated list of references to withdrawn coins to be used - * in the payment. Each reference has the syntax "LABEL[/NUMBER]" - * where NUMBER refers to a particular coin (in case multiple coins - * were created in a step). - */ - char *coin_ref; - - /** - * Amount to pay (from the coin, including fee). - */ - const char *amount_with_fee; - - /** - * Amount to pay (from the coin, excluding fee). The sum of the - * deltas between all @e amount_with_fee and the @e - * amount_without_fee must be less than max_fee, and the sum of - * the @e amount_with_fee must be larger than the @e - * total_amount. - */ - const char *amount_without_fee; - - /** - * Refund fee to use for each coin (only relevant if we - * exercise /pay's abort functionality). - */ - const char *refund_fee; - - /** - * Pay handle while operation is running. - */ - struct TALER_MERCHANT_Pay *ph; - - /** - * Hashcode of the proposal data associated to this payment. - */ - struct GNUNET_HashCode h_contract_terms; - - /** - * Merchant's public key - */ - struct TALER_MerchantPublicKeyP merchant_pub; - - } pay; - - struct { - - /** - * Reference to the (incomplete) pay operation that is to be - * resumed. - */ - char *pay_ref; - - /** - * ";"-separated list of references to additional withdrawn - * coins to be used in the payment. Each reference has the - * syntax "LABEL[/NUMBER]" where NUMBER refers to a particular - * coin (in case multiple coins were created in a step). - */ - char *coin_ref; - - /** - * Pay handle while operation is running. - */ - struct TALER_MERCHANT_Pay *ph; - - } pay_again; - - struct { - - /** - * Reference to the pay operation that is to be aborted. - */ - char *pay_ref; - - /** - * Pay handle while operation is running. - */ - struct TALER_MERCHANT_Pay *ph; - - /** - * Set in #pay_refund_cb to number of refunds obtained. - */ - unsigned int num_refunds; - - /** - * Array of @e num_refund refunds obtained. - */ - struct TALER_MERCHANT_RefundEntry *res; - - /** - * Set to the hash of the original contract. - */ - struct GNUNET_HashCode h_contract; - - /** - * Set to the merchant's public key. - */ - struct TALER_MerchantPublicKeyP merchant_pub; - - } pay_abort; - - struct { - - /** - * Reference to the pay_abort command to be refunded. - */ - const char *abort_ref; - - /** - * Number of the coin of @e abort_ref to be refunded. - */ - unsigned int num_coin; - - /** - * Refund amount to use. - */ - const char *refund_amount; - - /** - * Refund fee to expect. - */ - const char *refund_fee; - - /** - * Handle to the refund operation. - */ - struct TALER_EXCHANGE_RefundHandle *rh; - - } pay_abort_refund; - - 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; - - /** - * Set (!) to the wire transfer subject observed. - */ - char *subject; - - } check_bank_transfer; - - struct { - - /** - * #OC_CHECK_BANK_TRANSFER command from which we should grab - * the WTID. - */ - char *check_bank_ref; - - /** - * #OC_PAY command which we expect in the result. - * Since we are tracking a bank transaction, we want to know - * which (Taler) deposit is associated with the bank - * transaction being tracked now. - */ - char *expected_pay_ref; - - /** - * Handle to a /track/transfer operation - */ - struct TALER_MERCHANT_TrackTransferHandle *tdo; - - } track_transfer; - - struct { - - /** - * #OC_PAY command from which we should grab - * the WTID. - */ - char *pay_ref; - - /** - * #OC_CHECK_BANK_TRANSFER command which we expect in the result. - */ - char *expected_transfer_ref; - - /** - * Wire fee we expect to pay for this transaction. - */ - const char *wire_fee; - - /** - * Handle to a /track/transaction operation - */ - struct TALER_MERCHANT_TrackTransactionHandle *tth; - - } track_transaction; - - struct { - - /** - * Date we want retrieved transactions younger than - */ - struct GNUNET_TIME_Absolute date; - - /** - * How many "rows" we expect in the result - */ - unsigned int nresult; - - /** - * Handle to /history request - */ - struct TALER_MERCHANT_HistoryOperation *ho; - - /** - * The backend will return records with row_id - * less than this value. - */ - unsigned long long start; - - /** - * The backend will return at most `nrows` records. - */ - long long nrows; - - } history; - - struct { - /** - * Reference to the order we want reimbursed - */ - char *order_id; - - /** - * Handle to a refund increase operation - */ - struct TALER_MERCHANT_RefundIncreaseOperation *rio; - - /** - * Amount to refund - */ - const char *refund_amount; - - /** - * Reason for refunding - */ - const char *reason; - - /** - * Refund fee (MUST match the value given in config) - */ - const char *refund_fee; - - } refund_increase; - - struct { - - /** - * Reference to the order whose refund was increased - */ - char *order_id; - - /** - * Handle to the operation - */ - struct TALER_MERCHANT_RefundLookupOperation *rlo; - - /** - * Used to retrieve the asked refund amount. - * This information helps the callback to mock a GET /refund - * response and match it against what the backend actually - * responded. - */ - char *increase_ref; - - /** - * Used to retrieve the number and denomination of coins - * used to pay for the related contract. - * This information helps the callback to mock a GET /refund - * response and match it against what the backend actually - * responded. - */ - char *pay_ref; - - } refund_lookup; - - struct { - - /** - * Specify the instance (to succeed, this must match a prior - * enable action and the respective wire transfer's instance). - */ - const char *instance; - - /** - * Reason to use for enabling the tip (required by the API, but not - * yet really useful as we do not have a way to read back the - * justifications stored in the merchant's DB). - */ - const char *justification; - - /** - * How much should the tip be? - */ - const char *amount; - - /** - * Handle for the ongoing operation. - */ - struct TALER_MERCHANT_TipAuthorizeOperation *tao; - - /** - * Unique ID for the authorized tip, set by the interpreter. - */ - struct GNUNET_HashCode tip_id; - - /** - * When does the authorization expire? - */ - struct GNUNET_TIME_Absolute tip_expiration; - - /** - * EC expected for the operation. - */ - enum TALER_ErrorCode expected_ec; - - } tip_authorize; - - struct { - - /** - * Reference to operation that authorized the tip. Used - * to obtain the `tip_id`. - */ - const char *authorize_ref; - - /** - * Set to non-NULL to a label of another pick up operation - * that we should replay. - */ - const char *replay_ref; - - /** - * Number of coins we pick up. - */ - unsigned int num_coins; - - /** - * Array of @e num_coins denominations of the coins we pick up. - */ - const char **amounts; - - /** - * Handle for the ongoing operation. - */ - struct TALER_MERCHANT_TipPickupOperation *tpo; - - /** - * Temporary data structure to store the blinding keys while the - * pickup operation runs. - */ - struct TALER_PlanchetSecretsP *psa; - - /** - * Array of denomination keys matching the @e amounts. - */ - const struct TALER_EXCHANGE_DenomPublicKey **dks; - - /** - * Temporary data structure of @e num_coins entries for the - * withdraw operations. - */ - struct WithdrawHandle *withdraws; - - /** - * Set (by the interpreter) to an array of @a num_coins signatures - * created from the (successful) tip operation. - */ - struct TALER_DenominationSignature *sigs; - - /** - * EC expected for the operation. - */ - enum TALER_ErrorCode expected_ec; - - } tip_pickup; - - struct { - /** - * Expected available amount (in string format). - * NULL to skip check. - */ - char *expected_amount_available; - - /** - * Expected picked up amount (in string format). - * NULL to skip check. - */ - char *expected_amount_picked_up; - - /** - * Expected authorized amount (in string format). - * NULL to skip check. - */ - char *expected_amount_authorized; - - /** - * Handle for the ongoing operation. - */ - struct TALER_MERCHANT_TipQueryOperation *tqo; - - /** - * Merchant instance to use for tipping. - */ - char *instance; - - } tip_query; - - struct { - - /** - * Reference for the contract we want to check. - */ - const char *contract_ref; - - /** - * Whether to expect the payment to be settled or not. - */ - int expect_paid; - - /** - * Operation handle for the /check-payment request, - * NULL if operation is not running. - */ - struct TALER_MERCHANT_CheckPaymentOperation *cpo; - - } check_payment; - - } 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; - -/** - * Return instance private key from config - * - * @param config configuration handle - * @param instance instance name - * @return pointer to private key, NULL on error - */ -struct GNUNET_CRYPTO_EddsaPrivateKey * -get_instance_priv (struct GNUNET_CONFIGURATION_Handle *config, - const char *instance) -{ - char *config_section; - char *filename; - struct GNUNET_CRYPTO_EddsaPrivateKey *ret; - - (void) GNUNET_asprintf (&config_section, - "instance-%s", - instance); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (config, - config_section, - "KEYFILE", - &filename)) - { - GNUNET_break (0); - GNUNET_free (config_section); - return NULL; - } - GNUNET_free (config_section); - if (NULL == - (ret = GNUNET_CRYPTO_eddsa_key_create_from_file (filename))) - GNUNET_break (0); - GNUNET_free (filename); - return ret; -} - -/** - * The testcase failed, return with an error code. - * - * @param is interpreter state to clean up - */ -static void -fail (struct InterpreterState *is) -{ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Interpreter failed at step %s (#%u)\n", - is->commands[is->ip].label, - is->ip); - 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) -{ - const struct Command *cmd; - - if (NULL == label) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Attempt to lookup command for empty label\n"); - return NULL; - } - for (unsigned int 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) -{ - 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 timestamp time stamp when the transfer was made effective at the bank - * @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, - struct GNUNET_TIME_Absolute timestamp, - 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); -} - -/** - * Parse given JSON object to absolute time. - * - * @param root the json object representing data - * @param[out] ret where to write the data - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -static int -parse_abs_time (json_t *root, - struct GNUNET_TIME_Absolute *ret) -{ - const char *val; - unsigned long long int tval; - - val = json_string_value (root); - if (NULL == val) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if ( (0 == strcasecmp (val, - "/forever/")) || - (0 == strcasecmp (val, - "/end of time/")) || - (0 == strcasecmp (val, - "/never/")) ) - { - *ret = GNUNET_TIME_UNIT_FOREVER_ABS; - return GNUNET_OK; - } - if (1 != sscanf (val, - "/Date(%llu)/", - &tval)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* Time is in seconds in JSON, but in microseconds in GNUNET_TIME_Absolute */ - ret->abs_value_us = tval * 1000LL * 1000LL; - if ( (ret->abs_value_us) / 1000LL / 1000LL != tval) - { - /* Integer overflow */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Callback for a /history request. It's up to this function how - * to render the array containing transactions details (FIXME link to - * documentation) - * - * @param cls closure - * @param http_status HTTP status returned by the merchant backend - * @param ec taler-specific error code - * @param json actual body containing history - */ -static void -history_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *json) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - unsigned int nresult; - struct GNUNET_TIME_Absolute last_timestamp; - struct GNUNET_TIME_Absolute entry_timestamp; - - cmd->details.history.ho = NULL; - if (MHD_HTTP_OK != http_status) - { - fail (is); - return; - } - nresult = json_array_size (json); - if (nresult != cmd->details.history.nresult) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected number of history entries. Got %d, expected %d\n", - nresult, - cmd->details.history.nresult); - fail (is); - return; - } - - last_timestamp = GNUNET_TIME_absolute_get (); - last_timestamp = GNUNET_TIME_absolute_add (last_timestamp, - GNUNET_TIME_UNIT_DAYS); - json_t *entry; - json_t *timestamp; - size_t index; - json_array_foreach (json, index, entry) - { - timestamp = json_object_get (entry, "timestamp"); - if (GNUNET_OK != parse_abs_time (timestamp, &entry_timestamp)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Bad timestamp given\n"); - fail (is); - return; - } - /* entry_timestamp should always become last_timestamp */ - entry_timestamp = GNUNET_TIME_absolute_max (last_timestamp, entry_timestamp); - if (last_timestamp.abs_value_us != entry_timestamp.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "History entries are NOT sorted from younger to older\n"); - 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; -} - - -/** - * 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[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; - 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 (%d) to command %s\n", - http_status, - ec, - cmd->label); - GNUNET_break (0); - json_dumpf (json, stderr, 0); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - j = 0; - for (unsigned int i=0;iip;i++) - { - switch ((rel = &is->commands[i])->oc) - { - case OC_ADMIN_ADD_INCOMING: - /** - * If the command being iterated over filled a reserve AND - * it is the one referenced by the current "history command" - * ... - */ - 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) ) ) ) - { - /** - * ... then make sure the history element mentions a "deposit - * operation" on that reserve. - */ - if (GNUNET_OK != compare_admin_add_incoming_history (&history[j], - rel)) - { - GNUNET_break (0); - fail (is); - return; - } - j++; - } - break; - case OC_WITHDRAW_SIGN: - /** - * If the command being iterated over emptied a reserve AND - * it is the one referenced by the current "history command" - * ... - */ - if (0 == strcmp (cmd->details.reserve_status.reserve_reference, - rel->details.reserve_withdraw.reserve_reference)) - { - /** - * ... then make sure the history element mentions a "withdraw - * operation" on that reserve. - */ - if (GNUNET_OK != compare_reserve_withdraw_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 - * @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 (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - if (NULL == sig) - { - GNUNET_break (0); - fail (is); - return; - } - /** - * NOTE: this assert is OK on the second instance run because the - * interpreter is "cleaned" by cleanup_state() - */ - GNUNET_assert (NULL == cmd->details.reserve_withdraw.sig.rsa_signature); - cmd->details.reserve_withdraw.sig.rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - break; - case MHD_HTTP_PAYMENT_REQUIRED: - /* nothing to check */ - break; - default: - /* Unsupported status code (by test harness) */ - GNUNET_break (0); - break; - } - next_command (is); -} - - -/** - * Callback for GET /proposal issued at backend. - * Used to initialize the proposal after it was created. - * - * @param cls closure - * @param http_status HTTP status code we got - * @param json full response we got - */ -static void -proposal_lookup_initial_cb (void *cls, - unsigned int http_status, - const json_t *json, - const json_t *contract_terms, - const struct TALER_MerchantSignatureP *sig, - const struct GNUNET_HashCode *hash) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - if (cmd->expected_response_code != http_status) - { - fail (is); - return; - } - - cmd->details.proposal.hash = *hash; - cmd->details.proposal.merchant_sig = *sig; - cmd->details.proposal.contract_terms = json_deep_copy (contract_terms); - - cmd->details.proposal.plo = NULL; - next_command (is); -} - - -/** - * Callback that works POST /proposal's output. - * - * @param cls closure - * @param http_status HTTP response code, 200 indicates success; - * 0 if the backend's reply is bogus (fails to follow the protocol) - * @param ec taler-specific error code - * @param obj the full received JSON reply, or - * error details if the request failed - * @param contract_terms the order + additional information provided by the - * backend, NULL on error. - * @param sig merchant's signature over the contract, NULL on error - * @param h_contract hash of the contract, NULL on error - */ -static void -proposal_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj, - const char *order_id) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.proposal.po = NULL; - switch (http_status) - { - case MHD_HTTP_OK: - break; - default: { - char *s = json_dumps (obj, JSON_COMPACT); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected status code from /proposal: %u (%d). Step %u, response: %s\n", - http_status, - ec, - is->ip, - s); - GNUNET_free_non_null (s); - fail (is); - } - return; - } - - if (NULL == (cmd->details.proposal.plo - = TALER_MERCHANT_proposal_lookup (ctx, - MERCHANT_URL, - order_id, - instance, - &cmd->details.proposal.nonce, - proposal_lookup_initial_cb, - is))) - { - GNUNET_break (0); - fail (is); - } -} - - -/** - * Process POST /refund (increase) response - * - * @param cls closure - * @param http_status HTTP status code - * @param ec taler-specific error object - * @param obj response body; is NULL on success. - */ -static void -refund_increase_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.refund_increase.rio = NULL; - if (MHD_HTTP_OK != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Refund increase failed\n"); - fail (is); - return; - } - next_command (is); -} - - -/** - * Callback that frees all the elements in the hashmap - * - * @param cls closure, NULL - * @param key current key - * @param value a `struct TALER_Amount` - * @return always #GNUNET_YES (continue to iterate) - */ -static int -hashmap_free (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct TALER_Amount *refund_amount = value; - - GNUNET_free (refund_amount); - return GNUNET_YES; -} - - -/** - * Process GET /refund (increase) response. - * - * @param cls closure - * @param http_status HTTP status code - * @param ec taler-specific error object - * @param obj response body; is NULL on error. - */ -static void -refund_lookup_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct GNUNET_CONTAINER_MultiHashMap *map; - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - size_t index; - json_t *elem; - const char *error_name; - unsigned int error_line; - struct GNUNET_HashCode h_coin_pub; - char *icoin_ref; - char *icoin_refs; - const struct Command *icoin; - const struct Command *pay; - struct TALER_CoinSpendPublicKeyP icoin_pub; - struct GNUNET_HashCode h_icoin_pub; - struct TALER_Amount *iamount; - struct TALER_Amount acc; - const struct Command *increase; - struct TALER_Amount refund_amount; - const json_t *arr; - - cmd->details.refund_lookup.rlo = NULL; - if (MHD_HTTP_OK != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Refund lookup failed\n"); - fail (is); - return; - } - - map = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); - arr = json_object_get (obj, - "refund_permissions"); - if (NULL == arr) - { - GNUNET_break (0); - fail (is); - return; - } - json_array_foreach (arr, index, elem) - { - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_Amount *irefund_amount - = GNUNET_new (struct TALER_Amount); - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub), - TALER_JSON_spec_amount ("refund_amount", irefund_amount), - GNUNET_JSON_spec_end () - }; - - GNUNET_assert (GNUNET_OK == - GNUNET_JSON_parse (elem, - spec, - &error_name, - &error_line)); - GNUNET_CRYPTO_hash (&coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - &h_coin_pub); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put (map, - &h_coin_pub, - irefund_amount, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - }; - - /* Retrieve coins used to pay, from #OC_PAY command */ - GNUNET_assert (NULL != (pay = - find_command (is, - cmd->details.refund_lookup.pay_ref))); - icoin_refs = GNUNET_strdup (pay->details.pay.coin_ref); - GNUNET_assert (GNUNET_OK == - TALER_amount_get_zero ("EUR", - &acc)); - for (icoin_ref = strtok (icoin_refs, ";"); - NULL != icoin_ref; - icoin_ref = strtok (NULL, ";")) - { - GNUNET_assert (NULL != (icoin = - find_command (is, - icoin_ref))); - GNUNET_CRYPTO_eddsa_key_get_public (&icoin->details.reserve_withdraw.ps.coin_priv.eddsa_priv, - &icoin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&icoin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - &h_icoin_pub); - /* Can be NULL: not all coins are involved in refund */ - iamount = GNUNET_CONTAINER_multihashmap_get (map, - &h_icoin_pub); - if (NULL == iamount) - continue; - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&acc, - &acc, - iamount)); - } - GNUNET_free (icoin_refs); - /* Check if refund has been 100% covered */ - GNUNET_assert (increase = - find_command (is, - cmd->details.refund_lookup.increase_ref)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (increase->details.refund_increase.refund_amount, - &refund_amount)); - GNUNET_CONTAINER_multihashmap_iterate (map, - &hashmap_free, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (map); - if (0 != TALER_amount_cmp (&acc, - &refund_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Incomplete refund: expected '%s', got '%s'\n", - TALER_amount_to_string (&refund_amount), - TALER_amount_to_string (&acc)); - fail (is); - return; - } - next_command (is); -} - - -/** - * Function called with the result of a /pay 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 object - * @param obj the received JSON reply, should be kept as proof (and, in case of errors, - * be forwarded to the customer) - */ -static void -pay_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - struct GNUNET_CRYPTO_EddsaSignature sig; - const char *error_name; - unsigned int error_line; - - cmd->details.pay.ph = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - if (MHD_HTTP_OK == http_status) - { - /* Check signature */ - struct PaymentResponsePS mr; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("sig", &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &cmd->details.pay.h_contract_terms), - GNUNET_JSON_spec_end () - }; - GNUNET_assert (GNUNET_OK == - GNUNET_JSON_parse (obj, - spec, - &error_name, - &error_line)); - mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK); - mr.purpose.size = htonl (sizeof (mr)); - mr.h_contract_terms = cmd->details.pay.h_contract_terms; - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK, - &mr.purpose, - &sig, - &cmd->details.pay.merchant_pub.eddsa_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Merchant signature given in response to /pay invalid\n"); - fail (is); - return; - } - } - next_command (is); -} - - -/** - * Function called with the result of a /pay again 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 object - * @param obj the received JSON reply, should be kept as proof (and, in case of errors, - * be forwarded to the customer) - */ -static void -pay_again_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - struct GNUNET_CRYPTO_EddsaSignature sig; - const char *error_name; - unsigned int error_line; - const struct Command *pref; - - cmd->details.pay_again.ph = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - GNUNET_assert (NULL != (pref = find_command - (is, - cmd->details.pay_again.pay_ref))); - if (MHD_HTTP_OK == http_status) - { - struct PaymentResponsePS mr; - /* Check signature */ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &mr.h_contract_terms), - GNUNET_JSON_spec_end () - }; - - GNUNET_assert (GNUNET_OK == - GNUNET_JSON_parse (obj, - spec, - &error_name, - &error_line)); - mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK); - mr.purpose.size = htonl (sizeof (mr)); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK, - &mr.purpose, - &sig, - &pref->details.pay.merchant_pub.eddsa_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Merchant signature given in response to /pay invalid\n"); - fail (is); - return; - } - } - 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; - default: - GNUNET_break (0); - fail (is); - return; - } - next_command (is); -} - - -/** - * Callback for a /track/transfer operation - * - * @param cls closure for this function - * @param http_status HTTP response code returned by the server - * @param ec taler-specific error code - * @param sign_key exchange key used to sign @a json, or NULL - * @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 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 details_length length of the @a details array - * @param details array with details about the combined transactions - */ -static void -track_transfer_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_ExchangePublicKeyP *sign_key, - const json_t *json, - const struct GNUNET_HashCode *h_wire, - const struct TALER_Amount *total_amount, - unsigned int details_length, - const struct TALER_MERCHANT_TrackTransferDetails *details) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.track_transfer.tdo = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - switch (http_status) - { - case MHD_HTTP_OK: - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unhandled HTTP status.\n"); - } - next_command (is); -} - - -/** - * Callback for GET /proposal issued at backend. Just check - * whether response code is as expected. - * - * @param cls closure - * @param http_status HTTP status code we got - * @param json full response we got - */ -static void -proposal_lookup_cb (void *cls, - unsigned int http_status, - const json_t *json, - const json_t *contract_terms, - const struct TALER_MerchantSignatureP *sig, - const struct GNUNET_HashCode *hash) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.proposal_lookup.plo = NULL; - if (cmd->expected_response_code != http_status) - fail (is); - next_command (is); -} - - -/** - * Callback for GET /proposal issued at backend. Just check - * whether response code is as expected. - * - * @param cls closure - * @param http_status HTTP status code we got - * @param json full response we got - */ -static void -check_payment_cb (void *cls, - unsigned int http_status, - const json_t *obj, - int paid, - int refunded, - struct TALER_Amount *refund_amount, - const char *payment_redirect_url) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check payment: expected paid: %s: %d\n", - cmd->label, - cmd->details.check_payment.expect_paid); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check payment: paid: %d\n", - paid); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check payment: url: %s\n", - payment_redirect_url); - - cmd->details.check_payment.cpo = NULL; - if (paid != cmd->details.check_payment.expect_paid) - { - GNUNET_break (0); - fail (is); - return; - } - - if (cmd->expected_response_code != http_status) - fail (is); - next_command (is); -} - - -/** - * Callback to process a GET /tip-query request - * - * @param cls closure - * @param http_status HTTP status code for this request - * @param ec Taler-specific error code - * @param raw raw response body - */ -static void -tip_query_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *raw, - struct GNUNET_TIME_Absolute reserve_expiration, - struct TALER_ReservePublicKeyP *reserve_pub, - struct TALER_Amount *amount_authorized, - struct TALER_Amount *amount_available, - struct TALER_Amount *amount_picked_up) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - struct TALER_Amount a; - - cmd->details.tip_query.tqo = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Tip query callback at command %u/%s(%u)\n", - is->ip, - cmd->label, - cmd->oc); - - GNUNET_assert (NULL != reserve_pub); - GNUNET_assert (NULL != amount_authorized); - GNUNET_assert (NULL != amount_available); - GNUNET_assert (NULL != amount_picked_up); - - if (cmd->details.tip_query.expected_amount_available) - { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.tip_query.expected_amount_available, &a)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected available %s, actual %s\n", - TALER_amount_to_string (&a), - TALER_amount_to_string (amount_available)); - GNUNET_assert (0 == TALER_amount_cmp (amount_available, &a)); - } - - if (cmd->details.tip_query.expected_amount_authorized) - { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.tip_query.expected_amount_authorized, &a)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected authorized %s, actual %s\n", - TALER_amount_to_string (&a), - TALER_amount_to_string (amount_authorized)); - GNUNET_assert (0 == TALER_amount_cmp (amount_authorized, &a)); - } - - if (cmd->details.tip_query.expected_amount_picked_up) - { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.tip_query.expected_amount_picked_up, &a)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected picked_up %s, actual %s\n", - TALER_amount_to_string (&a), - TALER_amount_to_string (amount_picked_up)); - GNUNET_assert (0 == TALER_amount_cmp (amount_picked_up, &a)); - } - - if (cmd->expected_response_code != http_status) - fail (is); - 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 - * @param json original json reply + * @file exchange/test_merchant_api_new.c + * @brief testcase to test exchange's HTTP API interface + * @author Sree Harsha Totakura + * @author Christian Grothoff + * @author Marcello Stanisci */ -static void -track_transaction_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *json) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.track_transaction.tth = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - if (MHD_HTTP_OK != http_status) - fail (is); - next_command (is); -} +#include "platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "taler_merchant_testing_lib.h" /** - * Callback for a /tip-authorize request. Returns the result of - * the operation. - * - * @param cls closure - * @param http_status HTTP status returned by the merchant backend - * @param ec taler-specific error code - * @param tip_id which tip ID should be used to pickup the tip - * @param tip_expiration when does the tip expire (needs to be picked up before this time) - * @param exchange_url at what exchange can the tip be picked up + * Configuration file we use. One (big) configuration is used + * for the various components for this test. */ -static void -tip_authorize_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct GNUNET_HashCode *tip_id, - struct GNUNET_TIME_Absolute tip_expiration, - const char *exchange_url) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.tip_authorize.tao = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - if (cmd->details.tip_authorize.expected_ec != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected error code %d (%u) to command %s\n", - ec, - http_status, - cmd->label); - fail (is); - return; - } - if ( (MHD_HTTP_OK == http_status) && - (TALER_EC_NONE == ec) ) - { - if (0 != strcmp (exchange_url, - EXCHANGE_URL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected exchange URL %s to command %s\n", - exchange_url, - cmd->label); - fail (is); - return; - } - cmd->details.tip_authorize.tip_id = *tip_id; - cmd->details.tip_authorize.tip_expiration = tip_expiration; - } - next_command (is); -} - +#define CONFIG_FILE "test_merchant_api.conf" /** - * Callbacks of this type are used to serve the result of submitting a - * withdraw request to a exchange. - * - * @param cls closure, a `struct WithdrawHandle *` - * @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) + * Exchange base URL. Could also be taken from config. */ -static void -pickup_withdraw_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_DenominationSignature *sig, - const json_t *full_response) -{ - struct WithdrawHandle *wh = cls; - struct InterpreterState *is = wh->is; - struct Command *cmd = &is->commands[is->ip]; - - wh->wsh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Withdraw operation %u completed with %u (%d)\n", - wh->off, - http_status, - ec); - GNUNET_assert (wh->off < cmd->details.tip_pickup.num_coins); - if ( (MHD_HTTP_OK != http_status) || - (TALER_EC_NONE != ec) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s when withdrawing\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - if (NULL == cmd->details.tip_pickup.sigs) - cmd->details.tip_pickup.sigs = GNUNET_new_array (cmd->details.tip_pickup.num_coins, - struct TALER_DenominationSignature); - GNUNET_assert (NULL == cmd->details.tip_pickup.sigs[wh->off].rsa_signature); - cmd->details.tip_pickup.sigs[wh->off].rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - for (unsigned int i=0;idetails.tip_pickup.num_coins;i++) - if (NULL != cmd->details.tip_pickup.withdraws[wh->off].wsh) - return; /* still some ops ongoing */ - GNUNET_free (cmd->details.tip_pickup.withdraws); - cmd->details.tip_pickup.withdraws = NULL; - next_command (is); -} +#define EXCHANGE_URL "http://localhost:8081/" +static const char *pickup_amounts_1[] = {"EUR:5", NULL}; /** - * Callback for a /tip-pickup request. Returns the result of - * the operation. - * - * @param cls closure - * @param http_status HTTP status returned by the merchant backend, "200 OK" on success - * @param ec taler-specific error code - * @param reserve_pub public key of the reserve that made the @a reserve_sigs, NULL on error - * @param num_reserve_sigs length of the @a reserve_sigs array, 0 on error - * @param reserve_sigs array of signatures authorizing withdrawals, NULL on error - * @param json original json response + * URL of the fakebank. */ -static void -pickup_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_ReservePublicKeyP *reserve_pub, - unsigned int num_reserve_sigs, - const struct TALER_ReserveSignatureP *reserve_sigs, - const json_t *json) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.tip_pickup.tpo = NULL; - if (http_status != cmd->expected_response_code) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - if (ec != cmd->details.tip_pickup.expected_ec) - { - GNUNET_break (0); - fail (is); - return; - } - - if ( (MHD_HTTP_OK != http_status) || - (TALER_EC_NONE != ec) ) - { - next_command (is); - return; - } - if (num_reserve_sigs != cmd->details.tip_pickup.num_coins) - { - GNUNET_break (0); - fail (is); - return; - } - - /* pickup successful, now withdraw! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Obtained %u signatures for withdrawal from picking up a tip\n", - num_reserve_sigs); - GNUNET_assert (NULL == cmd->details.tip_pickup.withdraws); - cmd->details.tip_pickup.withdraws - = GNUNET_new_array (num_reserve_sigs, - struct WithdrawHandle); - for (unsigned int i=0;idetails.tip_pickup.withdraws[i]; - - wh->off = i; - wh->is = is; - GNUNET_assert ( (NULL == wh->wsh) && - ( (NULL == cmd->details.tip_pickup.sigs) || - (NULL == cmd->details.tip_pickup.sigs[wh->off].rsa_signature) ) ); - wh->wsh = TALER_EXCHANGE_reserve_withdraw2 (exchange, - cmd->details.tip_pickup.dks[i], - &reserve_sigs[i], - reserve_pub, - &cmd->details.tip_pickup.psa[i], - &pickup_withdraw_cb, - wh); - if (NULL == wh->wsh) - { - GNUNET_break (0); - fail (is); - return; - } - } - if (0 == num_reserve_sigs) - next_command (is); -} - +static char *fakebank_url; /** - * 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 + * Merchant base URL. */ -static const struct TALER_EXCHANGE_DenomPublicKey * -find_pk (const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_Amount *amount) -{ - struct GNUNET_TIME_Absolute now; - char *str; - - now = GNUNET_TIME_absolute_get (); - for (unsigned int i=0;inum_denom_keys;i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *pk; - - 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++) - { - const struct TALER_EXCHANGE_DenomPublicKey *pk; - - 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; -} - +static char *merchant_url; /** - * Reset the interpreter's state. - * - * @param is interpreter to reset + * Merchant process. */ -static void -cleanup_state (struct InterpreterState *is) -{ - 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_PROPOSAL_LOOKUP: - if (NULL != cmd->details.proposal_lookup.plo) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_MERCHANT_proposal_lookup_cancel (cmd->details.proposal_lookup.plo); - } - 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_PROPOSAL: - if (NULL != cmd->details.proposal.po) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete (proposal put)\n", - i, - cmd->label); - TALER_MERCHANT_proposal_cancel (cmd->details.proposal.po); - cmd->details.proposal.po = NULL; - } - if (NULL != cmd->details.proposal.plo) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete (proposal lookup)\n", - i, - cmd->label); - TALER_MERCHANT_proposal_lookup_cancel (cmd->details.proposal.plo); - } - if (NULL != cmd->details.proposal.contract_terms) - { - json_decref (cmd->details.proposal.contract_terms); - cmd->details.proposal.contract_terms = NULL; - } - break; - case OC_PAY: - if (NULL != cmd->details.pay.ph) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_MERCHANT_pay_cancel (cmd->details.pay.ph); - cmd->details.pay.ph = NULL; - } - break; - case OC_PAY_AGAIN: - if (NULL != cmd->details.pay_again.ph) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_MERCHANT_pay_cancel (cmd->details.pay_again.ph); - cmd->details.pay_again.ph = NULL; - } - break; - case OC_PAY_ABORT: - if (NULL != cmd->details.pay_abort.ph) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_MERCHANT_pay_cancel (cmd->details.pay_abort.ph); - cmd->details.pay_abort.ph = NULL; - } - GNUNET_array_grow (cmd->details.pay_abort.res, - cmd->details.pay_abort.num_refunds, - 0); - break; - case OC_PAY_ABORT_REFUND: - if (NULL != cmd->details.pay_abort_refund.rh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_EXCHANGE_refund_cancel (cmd->details.pay_abort_refund.rh); - cmd->details.pay_abort_refund.rh = 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_TRACK_TRANSFER: - if (NULL != cmd->details.track_transfer.tdo) - { - TALER_MERCHANT_track_transfer_cancel (cmd->details.track_transfer.tdo); - cmd->details.track_transfer.tdo = NULL; - } - break; - case OC_TRACK_TRANSACTION: - if (NULL != cmd->details.track_transaction.tth) - { - TALER_MERCHANT_track_transaction_cancel (cmd->details.track_transaction.tth); - cmd->details.track_transaction.tth = NULL; - } - break; - case OC_HISTORY: - if (NULL != cmd->details.history.ho) - { - TALER_MERCHANT_history_cancel (cmd->details.history.ho); - cmd->details.history.ho = NULL; - } - break; - case OC_REFUND_INCREASE: - if (NULL != cmd->details.refund_increase.rio) - { - TALER_MERCHANT_refund_increase_cancel (cmd->details.refund_increase.rio); - cmd->details.refund_increase.rio = NULL; - } - break; - case OC_REFUND_LOOKUP: - if (NULL != cmd->details.refund_lookup.rlo) - { - TALER_MERCHANT_refund_lookup_cancel (cmd->details.refund_lookup.rlo); - cmd->details.refund_lookup.rlo = NULL; - } - break; - case OC_TIP_AUTHORIZE: - if (NULL != cmd->details.tip_authorize.tao) - { - TALER_MERCHANT_tip_authorize_cancel (cmd->details.tip_authorize.tao); - cmd->details.tip_authorize.tao = NULL; - } - break; - case OC_TIP_PICKUP: - if (NULL != cmd->details.tip_pickup.tpo) - { - TALER_MERCHANT_tip_pickup_cancel (cmd->details.tip_pickup.tpo); - cmd->details.tip_pickup.tpo = NULL; - } - if (NULL != cmd->details.tip_pickup.psa) - { - GNUNET_free (cmd->details.tip_pickup.psa); - cmd->details.tip_pickup.psa = NULL; - } - if (NULL != cmd->details.tip_pickup.dks) - { - GNUNET_free (cmd->details.tip_pickup.dks); - cmd->details.tip_pickup.dks = NULL; - } - if (NULL != cmd->details.tip_pickup.withdraws) - { - for (unsigned int j=0;jdetails.tip_pickup.num_coins;j++) - { - struct WithdrawHandle *wh = &cmd->details.tip_pickup.withdraws[j]; - - if (NULL != wh->wsh) - { - TALER_EXCHANGE_reserve_withdraw_cancel (wh->wsh); - wh->wsh = NULL; - } - } - GNUNET_free (cmd->details.tip_pickup.withdraws); - cmd->details.tip_pickup.withdraws = NULL; - } - if (NULL != cmd->details.tip_pickup.sigs) - { - for (unsigned int j=0;jdetails.tip_pickup.num_coins;j++) - { - if (NULL != cmd->details.tip_pickup.sigs[j].rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (cmd->details.tip_pickup.sigs[j].rsa_signature); - cmd->details.tip_pickup.sigs[j].rsa_signature = NULL; - } - } - GNUNET_free (cmd->details.tip_pickup.sigs); - cmd->details.tip_pickup.sigs = NULL; - } - break; - case OC_CHECK_PAYMENT: - if (NULL != cmd->details.check_payment.cpo) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_MERCHANT_check_payment_cancel (cmd->details.check_payment.cpo); - cmd->details.check_payment.cpo = NULL; - } - break; - case OC_TIP_QUERY: - if (NULL != cmd->details.tip_query.tqo) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - i, - cmd->label); - TALER_MERCHANT_tip_query_cancel (cmd->details.tip_query.tqo); - cmd->details.tip_query.tqo = NULL; - } - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Shutdown: unknown instruction %d at %u (%s)\n", - cmd->oc, - i, - cmd->label); - break; - } - } -} - +static struct GNUNET_OS_Process *merchantd; /** - * Parse the @a coins specification and grow the @a pc - * array with the coins found, updating @a npc. - * - * @param[in,out] pc pointer to array of coins found - * @param[in,out] npc length of array at @a pc - * @param[in] coins string specifying coins to add to @a pc, - * clobbered in the process - * @param is interpreter state - * @param pref reference to the #OC_PAY command - * @return #GNUNET_OK on success + * Exchange base URL. */ -static int -build_coins (struct TALER_MERCHANT_PayCoin **pc, - unsigned int *npc, - char *coins, - struct InterpreterState *is, - const struct Command *pref) -{ - char *token; - - for (token = strtok (coins, ";"); - NULL != token; - token = strtok (NULL, ";")) - { - const struct Command *coin_ref; - char *ctok; - unsigned int ci; - struct TALER_MERCHANT_PayCoin *icoin; - - /* Token syntax is "LABEL[/NUMBER]" */ - ctok = strchr (token, '/'); - ci = 0; - if (NULL != ctok) - { - *ctok = '\0'; - ctok++; - if (1 != sscanf (ctok, - "%u", - &ci)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - GNUNET_assert (coin_ref = find_command (is, - token)); - GNUNET_array_grow (*pc, - *npc, - (*npc) + 1); - icoin = &(*pc)[(*npc)-1]; - switch (coin_ref->oc) - { - case OC_WITHDRAW_SIGN: - icoin->coin_priv = coin_ref->details.reserve_withdraw.ps.coin_priv; - icoin->denom_pub = coin_ref->details.reserve_withdraw.pk->key; - icoin->denom_sig = coin_ref->details.reserve_withdraw.sig; - icoin->denom_value = coin_ref->details.reserve_withdraw.pk->value; - icoin->exchange_url = EXCHANGE_URL; - break; - case OC_TIP_PICKUP: - icoin->coin_priv = coin_ref->details.tip_pickup.psa[ci].coin_priv; - icoin->denom_pub = coin_ref->details.tip_pickup.dks[ci]->key; - icoin->denom_sig = coin_ref->details.tip_pickup.sigs[ci]; - icoin->denom_value = coin_ref->details.tip_pickup.dks[ci]->value; - icoin->exchange_url = EXCHANGE_URL; - break; - default: - GNUNET_assert (0); - } - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (pref->details.pay.amount_with_fee, - &icoin->amount_with_fee)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (pref->details.pay.amount_without_fee, - &icoin->amount_without_fee)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (pref->details.pay.refund_fee, - &icoin->refund_fee)); - } - return GNUNET_OK; -} - +static char *exchange_url; /** - * Callbacks of this type are used to serve the result of submitting a - * /pay request to a merchant. - * - * @param cls closure - * @param http_status HTTP response code, 200 or 300-level response codes - * can indicate success, depending on whether the interaction - * was with a merchant frontend or backend; - * 0 if the merchant's reply is bogus (fails to follow the protocol) - * @param ec taler-specific error code - * @param merchant_pub public key of the merchant - * @param h_contract hash of the contract - * @param num_refunds size of the @a merchant_sigs array, 0 on errors - * @param res merchant signatures refunding coins, NULL on errors - * @param obj the received JSON reply, with error details if the request failed + * Auditor base URL; only used to fix FTBFS. */ -static void -pay_refund_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct GNUNET_HashCode *h_contract, - unsigned int num_refunds, - const struct TALER_MERCHANT_RefundEntry *res, - const json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.pay_abort.ph = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - if ( (MHD_HTTP_OK == http_status) && - (TALER_EC_NONE == ec) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %u refunds\n", - num_refunds); - cmd->details.pay_abort.num_refunds = num_refunds; - cmd->details.pay_abort.res - = GNUNET_new_array (num_refunds, - struct TALER_MERCHANT_RefundEntry); - memcpy (cmd->details.pay_abort.res, - res, - num_refunds * sizeof (struct TALER_MERCHANT_RefundEntry)); - cmd->details.pay_abort.h_contract = *h_contract; - cmd->details.pay_abort.merchant_pub = *merchant_pub; - } - next_command (is); -} - +static char *auditor_url; /** - * Callbacks of this type are used to serve the result of submitting a - * refund request to an exchange. - * - * @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 sign_key exchange key used to sign @a obj, or NULL - * @param obj the received JSON reply, should be kept as proof (and, in particular, - * be forwarded to the customer) + * Account number of the exchange at the bank. */ -static void -abort_refund_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_ExchangePublicKeyP *sign_key, - const json_t *obj) -{ - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - - cmd->details.pay_abort_refund.rh = NULL; - if (cmd->expected_response_code != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - cmd->label); - fail (is); - return; - } - next_command (is); -} +#define EXCHANGE_ACCOUNT_NO 2 +/** + * Account number of some user. + */ +#define USER_ACCOUNT_NO 62 /** - * Run the main interpreter loop that performs exchange operations. - * - * @param cls contains the `struct InterpreterState` + * Account number used by the merchant */ -static void -interpreter_run (void *cls) -{ - const struct GNUNET_SCHEDULER_TaskContext *tc; - struct InterpreterState *is = cls; - struct Command *cmd = &is->commands[is->ip]; - const struct Command *ref; - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount amount; - - is->task = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Test aborted by shutdown request\n"); - fail (is); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Interpreter runs command %u/%s(%u)\n", - is->ip, - cmd->label, - cmd->oc); - switch (cmd->oc) - { - case OC_END: - result = GNUNET_OK; - if (instance_idx + 1 == ninstances) - { - GNUNET_SCHEDULER_shutdown (); - return; - } - cleanup_state (is); - is->ip = 0; - instance_idx++; - instance = instances[instance_idx]; - GNUNET_free_non_null (instance_priv); - instance_priv = get_instance_priv (cfg, instance); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Switching instance: `%s'\n", - instance); - is->task = GNUNET_SCHEDULER_add_now (interpreter_run, - is); - - break; - case OC_PROPOSAL_LOOKUP: - { - const char *order_id; - - GNUNET_assert (NULL != cmd->details.proposal_lookup.proposal_reference); - GNUNET_assert (NULL != (ref = - find_command (is, - cmd->details.proposal_lookup.proposal_reference))); - - order_id = json_string_value (json_object_get (ref->details.proposal.contract_terms, - "order_id")); - - if (NULL == (cmd->details.proposal_lookup.plo - = TALER_MERCHANT_proposal_lookup (ctx, - MERCHANT_URL, - order_id, - instance, - &ref->details.proposal.nonce, - proposal_lookup_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - } - break; - case OC_CHECK_PAYMENT: - { - const char *order_id; - - GNUNET_assert (NULL != cmd->details.check_payment.contract_ref); - GNUNET_assert (NULL != (ref = - find_command (is, - cmd->details.check_payment.contract_ref))); - - order_id = json_string_value (json_object_get (ref->details.proposal.contract_terms, - "order_id")); - - GNUNET_assert (NULL != order_id); - - if (NULL == (cmd->details.check_payment.cpo - = TALER_MERCHANT_check_payment (ctx, - MERCHANT_URL, - instance, - order_id, - NULL, - NULL, - NULL, - check_payment_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - } - break; - case OC_TIP_QUERY: - { - if (instance_idx != 0) - { - // We check /tip-query only for the first instance, - // since for tipping we use a dedicated instance. - // On repeated runs, the expected authorized amounts wouldn't - // match up (they would all accumulate!) - next_command (is); - break; - } - if (NULL == (cmd->details.tip_query.tqo - = TALER_MERCHANT_tip_query (ctx, - MERCHANT_URL, - cmd->details.tip_query.instance, - tip_query_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - } - break; - 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) - { - GNUNET_assert (NULL != (ref - = find_command (is, - cmd->details.admin_add_incoming.reserve_reference))); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - cmd->details.admin_add_incoming.reserve_priv - = ref->details.admin_add_incoming.reserve_priv; - } - else if (NULL != - cmd->details.admin_add_incoming.instance) - { - char *section; - char *keys; - struct GNUNET_CRYPTO_EddsaPrivateKey *pk; - - GNUNET_asprintf (§ion, - "instance-%s", - cmd->details.admin_add_incoming.instance); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - section, - "TIP_RESERVE_PRIV_FILENAME", - &keys)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Configuration fails to specify reserve private key filename in section %s\n", - section); - GNUNET_free (section); - fail (is); - return; - } - pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keys); - if (NULL == pk) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "TIP_RESERVE_PRIV_FILENAME", - "Failed to read private key"); - GNUNET_free (keys); - GNUNET_free (section); - fail (is); - return; - } - cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *pk; - GNUNET_free (pk); - GNUNET_free (keys); - GNUNET_free (section); - } - 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)); - } - - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv, - &reserve_pub.eddsa_pub); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.admin_add_incoming.amount, - &amount)); - 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, - BANK_URL, - &auth, - 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); - } - break; - } - case OC_WITHDRAW_STATUS: - GNUNET_assert (NULL != - cmd->details.reserve_status.reserve_reference); - GNUNET_assert (NULL != (ref = find_command - (is, - cmd->details.reserve_status.reserve_reference))); - 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); - if (NULL == (cmd->details.reserve_status.wsh - = TALER_EXCHANGE_reserve_status (exchange, - &reserve_pub, - &reserve_status_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - break; - case OC_WITHDRAW_SIGN: - GNUNET_assert (NULL != cmd->details.reserve_withdraw.reserve_reference); - GNUNET_assert (NULL != (ref = find_command - (is, - cmd->details.reserve_withdraw.reserve_reference))); - GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - GNUNET_assert (NULL != cmd->details.reserve_withdraw.amount); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.reserve_withdraw.amount, - &amount)); - GNUNET_assert (NULL != (cmd->details.reserve_withdraw.pk - = find_pk (is->keys, - &amount))); - - 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); - } - break; - case OC_PROPOSAL: - { - json_t *order; - json_error_t error; - - GNUNET_assert (NULL != (order = json_loads (cmd->details.proposal.order, - JSON_REJECT_DUPLICATES, - &error))); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &cmd->details.proposal.nonce, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); - - if (NULL != instance) - { - json_t *merchant; - - TALER_LOG_DEBUG ("/proposal: explicit set of merchant:instance to '%s'\n", - instance); - - merchant = json_object (); - - json_object_set_new (merchant, - "instance", - json_string (instance)); - - json_object_set_new (order, - "merchant", - merchant); - - /* When a instance other than the default is used, we're - forced to specify the outer 'instance' field too, otherwise - we'll hit a 2001 "inconsistent instance" error. */ - if (0 != strcmp ("default", - instance)) - { - json_object_set_new (order, - "instance", - json_string (instance)); - - } - } - - TALER_LOG_DEBUG ("PUTting order: %s\n", - json_dumps (order, - JSON_INDENT (1))); - - cmd->details.proposal.po = TALER_MERCHANT_order_put (ctx, - MERCHANT_URL, - order, - &proposal_cb, - is); - json_decref (order); - if (NULL == cmd->details.proposal.po) - { - GNUNET_break (0); - fail (is); - } - break; - } - case OC_PAY: - { - struct TALER_MERCHANT_PayCoin *pc; - unsigned int npc; - char *coins; - const char *order_id; - struct GNUNET_TIME_Absolute refund_deadline; - struct GNUNET_TIME_Absolute pay_deadline; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_HashCode h_wire; - struct TALER_MerchantPublicKeyP merchant_pub; - struct TALER_MerchantSignatureP merchant_sig; - struct TALER_Amount total_amount; - struct TALER_Amount max_fee; - const char *error_name; - unsigned int error_line; - - /* get proposal */ - GNUNET_assert (NULL != (ref = find_command - (is, - cmd->details.pay.contract_ref))); - merchant_sig = ref->details.proposal.merchant_sig; - GNUNET_assert (NULL != ref->details.proposal.contract_terms); - { - /* Get information that needs to be replied in the deposit permission */ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &order_id), - GNUNET_JSON_spec_absolute_time ("refund_deadline", - &refund_deadline), - GNUNET_JSON_spec_absolute_time ("pay_deadline", - &pay_deadline), - GNUNET_JSON_spec_absolute_time ("timestamp", - ×tamp), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_fixed_auto ("H_wire", - &h_wire), - TALER_JSON_spec_amount ("amount", - &total_amount), - TALER_JSON_spec_amount ("max_fee", - &max_fee), - GNUNET_JSON_spec_end() - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ref->details.proposal.contract_terms, - spec, - &error_name, - &error_line)) - { - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Parser failed on %s:%u\n", - error_name, - error_line); - /** - * Let's use fail() here, as the proposal might be broken - * because of backend's fault. - */ - fail (is); - return; - } - cmd->details.pay.merchant_pub = merchant_pub; - } - /* strtok loop here */ - coins = GNUNET_strdup (cmd->details.pay.coin_ref); - pc = NULL; - npc = 0; - if (GNUNET_OK != - build_coins (&pc, - &npc, - coins, - is, - cmd)) - { - fail (is); - GNUNET_array_grow (pc, - npc, - 0); - GNUNET_free (coins); - return; - } - GNUNET_free (coins); - - cmd->details.pay.ph = TALER_MERCHANT_pay_wallet - (ctx, - MERCHANT_URL, - instance, - &ref->details.proposal.hash, - &total_amount, - &max_fee, - &merchant_pub, - &merchant_sig, - timestamp, - refund_deadline, - pay_deadline, - &h_wire, - order_id, - npc /* num_coins */, - pc /* coins */, - &pay_cb, - is); - GNUNET_array_grow (pc, - npc, - 0); - } - if (NULL == cmd->details.pay.ph) - { - GNUNET_break (0); - fail (is); - } - break; +#define MERCHANT_ACCOUNT_NO 3 - case OC_PAY_AGAIN: - { - struct TALER_MERCHANT_PayCoin *pc; - const struct Command *pref; - unsigned int npc; - char *coins; - const char *order_id; - struct GNUNET_TIME_Absolute refund_deadline; - struct GNUNET_TIME_Absolute pay_deadline; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_HashCode h_wire; - struct TALER_MerchantPublicKeyP merchant_pub; - struct TALER_MerchantSignatureP merchant_sig; - struct TALER_Amount total_amount; - struct TALER_Amount max_fee; - const char *error_name; - unsigned int error_line; - - /* Get original /pay command */ - GNUNET_assert (NULL != (pref = find_command - (is, - cmd->details.pay_again.pay_ref))); - /* get proposal */ - GNUNET_assert (NULL != (ref = find_command - (is, - pref->details.pay.contract_ref))); - merchant_sig = ref->details.proposal.merchant_sig; - GNUNET_assert (NULL != ref->details.proposal.contract_terms); - { - /* Get information that needs to be replied in the deposit permission */ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &order_id), - GNUNET_JSON_spec_absolute_time ("refund_deadline", - &refund_deadline), - GNUNET_JSON_spec_absolute_time ("pay_deadline", - &pay_deadline), - GNUNET_JSON_spec_absolute_time ("timestamp", - ×tamp), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_fixed_auto ("H_wire", - &h_wire), - TALER_JSON_spec_amount ("amount", - &total_amount), - TALER_JSON_spec_amount ("max_fee", - &max_fee), - GNUNET_JSON_spec_end() - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ref->details.proposal.contract_terms, - spec, - &error_name, - &error_line)) - { - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Parser failed on %s:%u\n", - error_name, - error_line); - /** - * Let's use fail() here, as the proposal might be broken - * because of backend's fault. - */ - fail (is); - return; - } - } - pc = NULL; - npc = 0; - /* Loop over coins from pay again */ - coins = GNUNET_strdup (cmd->details.pay_again.coin_ref); - if (GNUNET_OK != - build_coins (&pc, - &npc, - coins, - is, - pref)) - { - fail (is); - GNUNET_array_grow (pc, - npc, - 0); - GNUNET_free (coins); - return; - } - GNUNET_free (coins); - /* then repeat payment attempt */ - cmd->details.pay_again.ph = TALER_MERCHANT_pay_wallet - (ctx, - MERCHANT_URL, - instance, - &ref->details.proposal.hash, - &total_amount, - &max_fee, - &merchant_pub, - &merchant_sig, - timestamp, - refund_deadline, - pay_deadline, - &h_wire, - order_id, - npc /* num_coins */, - pc /* coins */, - &pay_again_cb, - is); - GNUNET_array_grow (pc, - npc, - 0); - } - if (NULL == cmd->details.pay_again.ph) - { - GNUNET_break (0); - fail (is); - } - break; - case OC_PAY_ABORT: - { - struct TALER_MERCHANT_PayCoin *pc; - const struct Command *pref; - unsigned int npc; - char *coins; - const char *order_id; - struct GNUNET_TIME_Absolute refund_deadline; - struct GNUNET_TIME_Absolute pay_deadline; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_HashCode h_wire; - struct TALER_MerchantPublicKeyP merchant_pub; - struct TALER_MerchantSignatureP merchant_sig; - struct TALER_Amount total_amount; - struct TALER_Amount max_fee; - const char *error_name; - unsigned int error_line; - - /* Get original /pay command */ - GNUNET_assert (NULL != (pref = find_command - (is, - cmd->details.pay_abort.pay_ref))); - /* get proposal */ - GNUNET_assert (NULL != (ref = find_command - (is, - pref->details.pay.contract_ref))); - merchant_sig = ref->details.proposal.merchant_sig; - GNUNET_assert (NULL != ref->details.proposal.contract_terms); - { - /* Get information that needs to be replied in the deposit permission */ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &order_id), - GNUNET_JSON_spec_absolute_time ("refund_deadline", - &refund_deadline), - GNUNET_JSON_spec_absolute_time ("pay_deadline", - &pay_deadline), - GNUNET_JSON_spec_absolute_time ("timestamp", - ×tamp), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_fixed_auto ("H_wire", - &h_wire), - TALER_JSON_spec_amount ("amount", - &total_amount), - TALER_JSON_spec_amount ("max_fee", - &max_fee), - GNUNET_JSON_spec_end() - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (ref->details.proposal.contract_terms, - spec, - &error_name, - &error_line)) - { - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Parser failed on %s:%u\n", - error_name, - error_line); - /** - * Let's use fail() here, as the proposal might be broken - * because of backend's fault. - */ - fail (is); - return; - } - } - /* strtok loop over original coins here */ - pc = NULL; - npc = 0; - coins = GNUNET_strdup (pref->details.pay.coin_ref); - if (GNUNET_OK != - build_coins (&pc, - &npc, - coins, - is, - pref)) - { - fail (is); - GNUNET_array_grow (pc, - npc, - 0); - GNUNET_free (coins); - return; - } - GNUNET_free (coins); - /* then trigger abort-refund operation */ - cmd->details.pay_abort.ph = TALER_MERCHANT_pay_abort - (ctx, - MERCHANT_URL, - instance, - &ref->details.proposal.hash, - &total_amount, - &max_fee, - &merchant_pub, - &merchant_sig, - timestamp, - refund_deadline, - pay_deadline, - &h_wire, - order_id, - npc /* num_coins */, - pc /* coins */, - &pay_refund_cb, - is); - GNUNET_array_grow (pc, - npc, - 0); - } - if (NULL == cmd->details.pay_abort.ph) - { - GNUNET_break (0); - fail (is); - } - break; - case OC_PAY_ABORT_REFUND: - { - struct TALER_Amount refund_fee; - const struct TALER_MERCHANT_RefundEntry *re; - - /* Get original /pay command */ - GNUNET_assert (NULL != (ref = find_command - (is, - cmd->details.pay_abort_refund.abort_ref))); - GNUNET_assert (OC_PAY_ABORT == ref->oc); - - GNUNET_assert (ref->details.pay_abort.num_refunds > - /* 'num_coin' is actually the coin _index_ */ - cmd->details.pay_abort_refund.num_coin); - - re = &ref->details.pay_abort.res[cmd->details.pay_abort_refund.num_coin]; - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount - (cmd->details.pay_abort_refund.refund_amount, - &amount)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount - (cmd->details.pay_abort_refund.refund_fee, - &refund_fee)); - cmd->details.pay_abort_refund.rh - = TALER_EXCHANGE_refund2 (exchange, - &amount, - &refund_fee, - &ref->details.pay_abort.h_contract, - &re->coin_pub, - re->rtransaction_id, - &ref->details.pay_abort.merchant_pub, - &re->merchant_sig, - &abort_refund_cb, - is); - if (NULL == cmd->details.pay_abort_refund.rh) - { - GNUNET_break (0); - fail (is); - } - } - break; - case OC_RUN_AGGREGATOR: - { - const struct GNUNET_DISK_FileHandle *pr; - - GNUNET_assert (NULL != (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_merchant_api.conf", - "-t", /* exit when done */ - NULL))); - 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); - } - break; - case OC_RUN_WIREWATCH: - { - const struct GNUNET_DISK_FileHandle *pr; - static int once; - - 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_merchant_api.conf", - "-T", /* exit when done */ - (0 == once ? "-r" : NULL), - NULL); - if (NULL == cmd->details.run_wirewatch.wirewatch_proc) - { - GNUNET_break (0); - fail (is); - return; - } - once = 1; - 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: - { - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount - (cmd->details.check_bank_transfer.amount, - &amount)); - if (GNUNET_OK != TALER_FAKEBANK_check - (fakebank, - &amount, - cmd->details.check_bank_transfer.account_debit, - cmd->details.check_bank_transfer.account_credit, - EXCHANGE_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_TRACK_TRANSFER: - { - struct TALER_WireTransferIdentifierRawP wtid; - const char *subject; - - GNUNET_assert (NULL != ( ref = find_command - (is, - cmd->details.track_transfer.check_bank_ref))); - subject = ref->details.check_bank_transfer.subject; - GNUNET_assert (GNUNET_OK == - GNUNET_STRINGS_string_to_data (subject, - strlen (subject), - &wtid, - sizeof (wtid))); - if (NULL == (cmd->details.track_transfer.tdo - = TALER_MERCHANT_track_transfer (ctx, - MERCHANT_URL, - instance, - "x-taler-bank", - &wtid, - EXCHANGE_URL, - &track_transfer_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - } - return; - case OC_TRACK_TRANSACTION: - { - const struct Command *proposal_ref; - const char *order_id; - - /* get proposal reference, and order_id from it */ - GNUNET_assert(NULL != (ref = find_command - (is, - cmd->details.track_transaction.pay_ref))); - GNUNET_assert (NULL != (proposal_ref = find_command - (is, - ref->details.pay.contract_ref))); - order_id = json_string_value - (json_object_get (proposal_ref->details.proposal.contract_terms, - "order_id")); - - if (NULL == (cmd->details.track_transaction.tth - = TALER_MERCHANT_track_transaction (ctx, - MERCHANT_URL, - instance, - order_id, - &track_transaction_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - return; - } - case OC_HISTORY: - /* If given as zero, convert it as now+1_hour. */ - if (0 == cmd->details.history.date.abs_value_us) - { - cmd->details.history.date = GNUNET_TIME_absolute_add - (GNUNET_TIME_absolute_get (), - GNUNET_TIME_UNIT_HOURS); - GNUNET_TIME_round_abs (&cmd->details.history.date); - } - if (NULL == (cmd->details.history.ho - = TALER_MERCHANT_history (ctx, - MERCHANT_URL, - instance, - cmd->details.history.start, - cmd->details.history.nrows, - cmd->details.history.date, - &history_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - break; - case OC_REFUND_INCREASE: - { - struct TALER_Amount refund_amount; - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount - (cmd->details.refund_increase.refund_amount, - &refund_amount)); - if (NULL == (cmd->details.refund_increase.rio - = TALER_MERCHANT_refund_increase - (ctx, - MERCHANT_URL, - cmd->details.refund_increase.order_id, - &refund_amount, - cmd->details.refund_increase.reason, - instance, - &refund_increase_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - break; - } - case OC_REFUND_LOOKUP: - { - if (NULL == (cmd->details.refund_lookup.rlo - = TALER_MERCHANT_refund_lookup - (ctx, - MERCHANT_URL, - cmd->details.refund_lookup.order_id, - instance, - refund_lookup_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - break; - } - case OC_TIP_AUTHORIZE: - { - GNUNET_assert (NULL != cmd->details.tip_authorize.amount); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.tip_authorize.amount, - &amount)); - if (NULL == (cmd->details.tip_authorize.tao - = TALER_MERCHANT_tip_authorize - (ctx, - MERCHANT_URL, - "http://merchant.com/pickup", - "http://merchant.com/continue", - &amount, - cmd->details.tip_authorize.instance, - cmd->details.tip_authorize.justification, - &tip_authorize_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - break; - } - case OC_TIP_PICKUP: - { - unsigned int num_planchets; - const struct Command *rr; - - if (NULL == cmd->details.tip_pickup.replay_ref) - { - rr = NULL; - for (num_planchets=0; - NULL != cmd->details.tip_pickup.amounts[num_planchets]; - num_planchets++); - ref = find_command (is, - cmd->details.tip_pickup.authorize_ref); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_TIP_AUTHORIZE == ref->oc); - } - else - { - rr = find_command (is, - cmd->details.tip_pickup.replay_ref); - GNUNET_assert (NULL != rr); - GNUNET_assert (OC_TIP_PICKUP == rr->oc); - num_planchets = rr->details.tip_pickup.num_coins; - ref = find_command (is, - rr->details.tip_pickup.authorize_ref); - GNUNET_assert (NULL != ref); - GNUNET_assert (OC_TIP_AUTHORIZE == ref->oc); - } - cmd->details.tip_pickup.num_coins = num_planchets; - { - struct TALER_PlanchetDetail planchets[num_planchets]; - - cmd->details.tip_pickup.psa = GNUNET_new_array (num_planchets, - struct TALER_PlanchetSecretsP); - cmd->details.tip_pickup.dks = GNUNET_new_array (num_planchets, - const struct TALER_EXCHANGE_DenomPublicKey *); - for (unsigned int i=0;idetails.tip_pickup.amounts[i], - &amount)); - cmd->details.tip_pickup.dks[i] - = find_pk (is->keys, - &amount); - if (NULL == cmd->details.tip_pickup.dks[i]) - { - GNUNET_break (0); - fail (is); - return; - } - TALER_planchet_setup_random (&cmd->details.tip_pickup.psa[i]); - } - else - { - cmd->details.tip_pickup.dks[i] - = rr->details.tip_pickup.dks[i]; - cmd->details.tip_pickup.psa[i] - = rr->details.tip_pickup.psa[i]; - } - if (GNUNET_OK != - TALER_planchet_prepare (&cmd->details.tip_pickup.dks[i]->key, - &cmd->details.tip_pickup.psa[i], - &planchets[i])) - { - GNUNET_break (0); - fail (is); - return; - } - } - if (NULL == (cmd->details.tip_pickup.tpo - = TALER_MERCHANT_tip_pickup (ctx, - MERCHANT_URL, - &ref->details.tip_authorize.tip_id, - num_planchets, - planchets, - &pickup_cb, - is))) - { - GNUNET_break (0); - fail (is); - } - for (unsigned int i=0;ioc, - is->ip, - cmd->label); - fail (is); - return; - } -} +/** + * User name. Never checked by fakebank. + */ +#define USER_LOGIN_NAME "user42" +/** + * User password. Never checked by fakebank. + */ +#define USER_LOGIN_PASS "pass42" /** - * Function run when the test times out. + * Execute the taler-exchange-wirewatch command with + * our configuration file. * - * @param cls NULL + * @param label label to use for the command. */ -static void -do_timeout (void *cls) -{ - timeout_task = NULL; - GNUNET_SCHEDULER_shutdown (); -} - +#define CMD_EXEC_WIREWATCH(label) \ + TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE) /** - * Function run when the test terminates (good or bad). - * Cleans up our state. + * Execute the taler-exchange-aggregator command with + * our configuration file. * - * @param cls the interpreter state. + * @param label label to use for the command. */ -static void -do_shutdown (void *cls) -{ - struct InterpreterState *is = cls; - - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutdown executing\n"); - cleanup_state (is); - if (NULL != instance_priv) - { - GNUNET_free (instance_priv); - instance_priv = NULL; - } - if (NULL != is->task) - { - GNUNET_SCHEDULER_cancel (is->task); - is->task = NULL; - } - GNUNET_free (is); - for (unsigned int i=0;idrop_tables (db->cls); - TALER_MERCHANTDB_plugin_unload (db); - GNUNET_CONFIGURATION_destroy (cfg); -} - +#define CMD_EXEC_AGGREGATOR(label) \ + TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE) /** - * 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. + * Run wire transfer of funds from some user's account to the + * exchange. * - * @param cls closure - * @param keys information about keys of the exchange - * @param vc compatibility information + * @param label label to use for the command. + * @param amount amount to transfer, i.e. "EUR:1" + * @param url exchange_url */ -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 */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Certificate callback invoked, starting interpreter\n"); - is->keys = keys; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); -} - +#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ + TALER_TESTING_cmd_fakebank_transfer (label, amount, \ + fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \ + USER_LOGIN_NAME, USER_LOGIN_PASS, EXCHANGE_URL) /** - * Signal handler called for SIGCHLD. Triggers the - * respective handler by writing to the trigger pipe. + * Run wire transfer of funds from some user's account to the + * exchange. + * + * @param label label to use for the command. + * @param amount amount to transfer, i.e. "EUR:1" */ -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 */ -} +#define CMD_TRANSFER_TO_EXCHANGE_SUBJECT(label,amount,subject) \ + TALER_TESTING_cmd_fakebank_transfer_with_subject \ + (label, amount, fakebank_url, USER_ACCOUNT_NO, \ + EXCHANGE_ACCOUNT_NO, USER_LOGIN_NAME, USER_LOGIN_PASS, \ + subject) /** - * Main function that will be run by the scheduler. + * Main function that will tell the interpreter what commands to + * run. * * @param cls closure */ static void -run (void *cls) +run (void *cls, + struct TALER_TESTING_Interpreter *is) { - struct InterpreterState *is; - static const char *pickup_amounts_1[] = { - "EUR:5", - NULL - }; - static struct Command commands[] = - { - /* 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 = 62, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user62", - .details.admin_add_incoming.auth_password = "pass62", - .details.admin_add_incoming.amount = "EUR:10.02" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-1" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-1", - .details.check_bank_transfer.amount = "EUR:10.02", - .details.check_bank_transfer.account_debit = 62, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO - }, - - /* 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" }, - - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-2", - .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" }, - /* Create proposal */ - { .oc = OC_PROPOSAL, - .label = "create-proposal-1", - .expected_response_code = MHD_HTTP_OK, - .details.proposal.order = "{\ - \"max_fee\":\ + struct TALER_TESTING_Command pay[] = { + /** + * Move money to the exchange's bank account. + */ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1", + "EUR:10.02"), + /** + * Make a reserve exist, according to the previous + * transfer. + */ + CMD_EXEC_WIREWATCH ("wirewatch-1"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-2", + EXCHANGE_URL, + "EUR:10.02", + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_withdraw_amount + ("withdraw-coin-1", + "create-reserve-1", + "EUR:5", + MHD_HTTP_OK), + + TALER_TESTING_cmd_withdraw_amount + ("withdraw-coin-2", + "create-reserve-1", + "EUR:5", + MHD_HTTP_OK), + + /** + * Check the reserve is depleted. + */ + TALER_TESTING_cmd_status ("withdraw-status-1", + "create-reserve-1", + "EUR:0", + MHD_HTTP_OK), + + TALER_TESTING_cmd_proposal + ("create-proposal-1", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ {\"currency\":\"EUR\",\ \"value\":0,\ \"fraction\":50000000},\ @@ -4274,435 +209,557 @@ run (void *cls) {\"currency\":\"EUR\",\ \"value\":5,\ \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\":\ - [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }"}, - - /* check payment before we pay */ - { .oc = OC_CHECK_PAYMENT, - .label = "check-payment-1", - .expected_response_code = MHD_HTTP_OK, - .details.check_payment.contract_ref = "create-proposal-1", - .details.check_payment.expect_paid = GNUNET_NO }, - - /* execute simple payment */ - { .oc = OC_PAY, - .label = "deposit-simple", - .expected_response_code = MHD_HTTP_OK, - .details.pay.contract_ref = "create-proposal-1", - .details.pay.coin_ref = "withdraw-coin-1", - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - - /* check payment after we've paid */ - { .oc = OC_CHECK_PAYMENT, - .label = "check-payment-2", - .expected_response_code = MHD_HTTP_OK, - .details.check_payment.contract_ref = "create-proposal-1", - .details.check_payment.expect_paid = GNUNET_YES }, - - /* Test for #5262: abort after full payment */ - { .oc = OC_PAY_ABORT, - .label = "pay-abort-2", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.pay_abort.pay_ref = "deposit-simple", - }, - - /* Try to replay payment reusing coin */ - { .oc = OC_PAY, - .label = "replay-simple", - .expected_response_code = MHD_HTTP_OK, - .details.pay.contract_ref = "create-proposal-1", - .details.pay.coin_ref = "withdraw-coin-1", - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - - /* Create another contract */ - { .oc = OC_PROPOSAL, - .label = "create-proposal-2", - .expected_response_code = MHD_HTTP_OK, - .details.proposal.order = "{\ - \"max_fee\":\ + \"summary\": \"merchant-lib testcase\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:5}\"} ] }", + NULL), + + TALER_TESTING_cmd_check_payment ("check-payment-1", + merchant_url, + MHD_HTTP_OK, + "create-proposal-1", + GNUNET_NO), + TALER_TESTING_cmd_pay ("deposit-simple", + merchant_url, + MHD_HTTP_OK, + "create-proposal-1", + "withdraw-coin-1", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + TALER_TESTING_cmd_check_payment ("check-payment-2", + merchant_url, + MHD_HTTP_OK, + "create-proposal-1", + GNUNET_YES), + + TALER_TESTING_cmd_pay_abort ("pay-abort-2", + merchant_url, + "deposit-simple", + MHD_HTTP_FORBIDDEN), + + TALER_TESTING_cmd_pay ("replay-simple", + merchant_url, + MHD_HTTP_OK, + "create-proposal-1", + "withdraw-coin-1", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + TALER_TESTING_cmd_check_bank_empty + ("check_bank_empty-1"), + + CMD_EXEC_AGGREGATOR ("run-aggregator"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-498c", + EXCHANGE_URL, + "EUR:4.98", + EXCHANGE_ACCOUNT_NO, + MERCHANT_ACCOUNT_NO), + + TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-2"), + + TALER_TESTING_cmd_end () + }; + + + struct TALER_TESTING_Command double_spending[] = { + + TALER_TESTING_cmd_proposal + ("create-proposal-2", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ {\"currency\":\"EUR\",\ \"value\":0,\ \"fraction\":50000000},\ \"order_id\":\"2\",\ \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(9999999999)\\/\",\ + \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ \"amount\":\ {\"currency\":\"EUR\",\ \"value\":5,\ \"fraction\":0},\ - \"summary\":\"useful product\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\":\ - [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }" }, - /** - * Try to double-spend the 5 EUR coin at the same - * merchant (but different transaction ID) - */ - { .oc = OC_PAY, - .label = "deposit-double-2", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.pay.contract_ref = "create-proposal-2", - .details.pay.coin_ref = "withdraw-coin-1", - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - - { .oc = OC_HISTORY, - .label = "history-0", - .expected_response_code = MHD_HTTP_OK, + \"summary\": \"useful product\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:5}\"} ] }", + NULL), + + TALER_TESTING_cmd_proposal_lookup ("fetch-proposal-2", + merchant_url, + MHD_HTTP_OK, + "create-proposal-2", + NULL), + + TALER_TESTING_cmd_pay ("deposit-double-2", + merchant_url, + MHD_HTTP_FORBIDDEN, + "create-proposal-2", + "withdraw-coin-1", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + TALER_TESTING_cmd_history ("history-0", + merchant_url, + MHD_HTTP_OK, /** * all records to be returned; setting date as 0 lets the * interpreter set it as 'now' + one hour delta, just to * make sure it surpasses the proposal's timestamp. */ - .details.history.date.abs_value_us = 0, + GNUNET_TIME_UNIT_ZERO_ABS, /** * We only expect ONE result (create-proposal-1) to be * included in /history response, because create-proposal-3 * did NOT go through because of double spending. */ - .details.history.nresult = 1, - .details.history.start = 10, - .details.history.nrows = -10 - }, - - /* Fill second reserve with EUR:1 */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-2", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = 63, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user63", - .details.admin_add_incoming.auth_password = "pass63", - .details.admin_add_incoming.amount = "EUR:1" }, - - /* Add another 4.01 EUR to reserve #2 */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-2b", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.reserve_reference = "create-reserve-2", - .details.admin_add_incoming.debit_account_no = 63, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user63", - .details.admin_add_incoming.auth_password = "pass63", - .details.admin_add_incoming.amount = "EUR:4.01" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-2" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-2", - .details.check_bank_transfer.amount = "EUR:1", - .details.check_bank_transfer.account_debit = 63, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO - }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-2", - .details.check_bank_transfer.amount = "EUR:4.01", - .details.check_bank_transfer.account_debit = 63, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO - }, - - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-2", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference - = "create-reserve-2", - .details.reserve_withdraw.amount = "EUR:5" }, - - /* Proposal lookup */ - { - .oc = OC_PROPOSAL_LOOKUP, - .label = "fetch-proposal-2", - .expected_response_code = MHD_HTTP_OK, - .details.proposal_lookup.proposal_reference - = "create-proposal-2" }, - - /* Check nothing happened on the bank side so far */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty" }, - - /* Run transfers. */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator" }, - - /* Obtain WTID of the transfer generated by "deposit-simple" */ - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-498c", - .details.check_bank_transfer.amount = "EUR:4.98", - .details.check_bank_transfer.account_debit = EXCHANGE_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = MERCHANT_ACCOUNT_NO - }, - - /* Check that there are no other unusual transfers */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty" }, - - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-1", - .expected_response_code = MHD_HTTP_OK, - .details.track_transaction.expected_transfer_ref - = "check_bank_transfer-498c", - .details.track_transaction.pay_ref = "deposit-simple", - .details.track_transaction.wire_fee = "EUR:0.01" - }, - - /* Trace the WTID back to the original transaction */ - { .oc = OC_TRACK_TRANSFER, - .label = "track-transfer-1", - .expected_response_code = MHD_HTTP_OK, - .details.track_transfer.check_bank_ref - = "check_bank_transfer-498c", - .details.track_transfer.expected_pay_ref = "deposit-simple" - }, - { .oc = OC_TRACK_TRANSFER, - .label = "track-transfer-1-again", - .expected_response_code = MHD_HTTP_OK, - .details.track_transfer.check_bank_ref - = "check_bank_transfer-498c", - .details.track_transfer.expected_pay_ref = "deposit-simple" - }, - - /* Pay again successfully on 2nd contract */ - { .oc = OC_PAY, - .label = "deposit-simple-2", - .expected_response_code = MHD_HTTP_OK, - .details.pay.contract_ref = "create-proposal-2", - .details.pay.coin_ref = "withdraw-coin-2", - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - - /* Run transfers. */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-2" }, - - /* Obtain WTID of the transfer */ - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-498c-2", - .details.check_bank_transfer.amount = "EUR:4.98", - .details.check_bank_transfer.account_debit = EXCHANGE_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = MERCHANT_ACCOUNT_NO - }, - - /* Check that there are no other unusual transfers */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty" }, - - /* Trace the WTID back to the original transaction */ - { .oc = OC_TRACK_TRANSFER, - .label = "track-transfer-2", - .expected_response_code = MHD_HTTP_OK, - .details.track_transfer.check_bank_ref - = "check_bank_transfer-498c-2", - .details.track_transfer.expected_pay_ref - = "deposit-simple-2" - }, - { .oc = OC_TRACK_TRANSFER, - .label = "track-transfer-2-again", - .expected_response_code = MHD_HTTP_OK, - .details.track_transfer.check_bank_ref - = "check_bank_transfer-498c-2", - .details.track_transfer.expected_pay_ref - = "deposit-simple-2" - }, - - { .oc = OC_TRACK_TRANSACTION, - .label = "track-transaction-2", - .expected_response_code = MHD_HTTP_OK, - .details.track_transaction.expected_transfer_ref - = "check_bank_transfer-498c-2", - .details.track_transaction.wire_fee = "EUR:0.01", - .details.track_transaction.pay_ref = "deposit-simple-2" - }, - - { .oc = OC_HISTORY, - .label = "history-1", - .expected_response_code = MHD_HTTP_OK, - .details.history.date.abs_value_us = 0, + 1, // nresult + 10, // start + -10), // nrows + + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command track[] = { + + TALER_TESTING_cmd_merchant_track_transaction + ("track-transaction-1", + merchant_url, + MHD_HTTP_OK, + "deposit-simple"), + + TALER_TESTING_cmd_merchant_track_transfer + ("track-transfer-1", + merchant_url, + MHD_HTTP_OK, + "check_bank_transfer-498c"), + + TALER_TESTING_cmd_merchant_track_transfer + ("track-transfer-again", + merchant_url, + MHD_HTTP_OK, + "check_bank_transfer-498c"), + + TALER_TESTING_cmd_fakebank_transfer + ("create-reserve-2", + "EUR:1", + fakebank_url, + USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, + "user62", + "pass62", + EXCHANGE_URL), + + TALER_TESTING_cmd_fakebank_transfer_with_ref + ("create-reserve-2b", + "EUR:4.01", + fakebank_url, + USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, + "user62", + "pass62", + "create-reserve-2", + EXCHANGE_URL), + + CMD_EXEC_WIREWATCH ("wirewatch-2"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-2a", + EXCHANGE_URL, + "EUR:1", + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-2b", + EXCHANGE_URL, + "EUR:4.01", + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2", + "create-reserve-2", + "EUR:5", + MHD_HTTP_OK), + + TALER_TESTING_cmd_pay ("deposit-simple-2", + merchant_url, + MHD_HTTP_OK, + "create-proposal-2", + "withdraw-coin-2", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + CMD_EXEC_AGGREGATOR ("run-aggregator-2"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-498c-2", + EXCHANGE_URL, + "EUR:4.98", + EXCHANGE_ACCOUNT_NO, + MERCHANT_ACCOUNT_NO), + + TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"), + + TALER_TESTING_cmd_merchant_track_transfer + ("track-transfer-2", + merchant_url, + MHD_HTTP_OK, + "check_bank_transfer-498c-2"), + + TALER_TESTING_cmd_merchant_track_transfer + ("track-transfer-2-again", + merchant_url, + MHD_HTTP_OK, + "check_bank_transfer-498c-2"), + + TALER_TESTING_cmd_merchant_track_transaction + ("track-transaction-2", + merchant_url, + MHD_HTTP_OK, + "deposit-simple-2"), + + TALER_TESTING_cmd_history ("history-1", + merchant_url, + MHD_HTTP_OK, + GNUNET_TIME_UNIT_ZERO_ABS, /** * Now we expect BOTH contracts (create-proposal-{1,2}) * to be included in /history response, because * create-proposal-2 has now been correctly paid. */ - .details.history.nresult = 2, - .details.history.start = 10, - .details.history.nrows = -10 - }, - - { .oc = OC_HISTORY, - .label = "history-2", - .expected_response_code = MHD_HTTP_OK, - /* no records returned, time limit too ancient - * and start's too high. */ - .details.history.date.abs_value_us = 1, - .details.history.nresult = 0, - /* younger than 'start' */ - .details.history.start = 10, - .details.history.nrows = 10 - /* end of "younger than 'start'" */ - }, - - { .oc = OC_REFUND_INCREASE, - .label = "refund-increase-1", - .details.refund_increase.refund_amount = "EUR:0.1", - .details.refund_increase.refund_fee = "EUR:0.01", - .details.refund_increase.reason = "refund test", - .details.refund_increase.order_id = "1" - }, - { .oc = OC_REFUND_LOOKUP, - .label = "refund-lookup-1", - .details.refund_lookup.order_id = "1", - .details.refund_lookup.increase_ref = "refund-increase-1", - .details.refund_lookup.pay_ref = "deposit-simple" - }, - - /* Test tipping */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-tip-1", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.instance = "tip", - .details.admin_add_incoming.debit_account_no = TIP_ACCOUNT_NO, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user62", - .details.admin_add_incoming.auth_password = "pass62", - /* we run *two* __merchant__ instances, but only this first call will - actually fill the reserve, as the second one will be seen as - a duplicate. Hence fill with twice the require amount per - round. */ - .details.admin_add_incoming.amount = "EUR:20.04" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-3" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-tip-1", - .details.check_bank_transfer.amount = "EUR:20.04", - .details.check_bank_transfer.account_debit = TIP_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO - }, - /* Authorize two tips */ - { .oc = OC_TIP_AUTHORIZE, - .label = "authorize-tip-1", - .expected_response_code = MHD_HTTP_OK, - .details.tip_authorize.instance = "tip", - .details.tip_authorize.justification = "tip 1", - .details.tip_authorize.amount = "EUR:5.01" }, - { .oc = OC_TIP_AUTHORIZE, - .label = "authorize-tip-2", - .expected_response_code = MHD_HTTP_OK, - .details.tip_authorize.instance = "tip", - .details.tip_authorize.justification = "tip 2", - .details.tip_authorize.amount = "EUR:5.01" }, - /* Check tip status */ - { .oc = OC_TIP_QUERY, - .label = "query-tip-1", - .expected_response_code = MHD_HTTP_OK, - .details.tip_query.instance = "tip" }, - { .oc = OC_TIP_QUERY, - .label = "query-tip-2", - .expected_response_code = MHD_HTTP_OK, - .details.tip_query.instance = "tip", - .details.tip_query.expected_amount_authorized = "EUR:10.02", - .details.tip_query.expected_amount_picked_up = "EUR:0.0", - .details.tip_query.expected_amount_available = "EUR:20.04" }, - /* Withdraw tip */ - { .oc = OC_TIP_PICKUP, - .label = "pickup-tip-1", - .expected_response_code = MHD_HTTP_OK, - .details.tip_pickup.authorize_ref = "authorize-tip-1", - .details.tip_pickup.amounts = pickup_amounts_1 }, - /* Check tip status again */ - { .oc = OC_TIP_QUERY, - .label = "query-tip-3", - .expected_response_code = MHD_HTTP_OK, - .details.tip_query.instance = "tip", - .details.tip_query.expected_amount_picked_up = "EUR:5.01", - .details.tip_query.expected_amount_available = "EUR:15.03" }, - { .oc = OC_TIP_PICKUP, - .label = "pickup-tip-2", - .expected_response_code = MHD_HTTP_OK, - .details.tip_pickup.authorize_ref = "authorize-tip-2", - .details.tip_pickup.amounts = pickup_amounts_1 }, - { .oc = OC_TIP_QUERY, - .label = "query-tip-4", - .expected_response_code = MHD_HTTP_OK, - .details.tip_query.instance = "tip", - .details.tip_query.expected_amount_picked_up = "EUR:10.02", - .details.tip_query.expected_amount_available = "EUR:10.02", - .details.tip_query.expected_amount_authorized = "EUR:10.02" }, - { .oc = OC_TIP_PICKUP, - .label = "pickup-tip-2b", - .expected_response_code = MHD_HTTP_OK, - .details.tip_pickup.replay_ref = "pickup-tip-2", - .details.tip_pickup.authorize_ref = "authorize-tip-2", - .details.tip_pickup.amounts = pickup_amounts_1 }, - /* Test authorization failure modes */ - - /* Need first to _create_ the reserve that should - * give back the "insufficient funds" error. */ - - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-insufficient-funds-tip-reserve", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.instance = "dtip", - .details.admin_add_incoming.debit_account_no = TIP_ACCOUNT_NO, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user62", - .details.admin_add_incoming.auth_password = "pass62", - /* we run *two* __merchant__ instances, but only this first call will - actually fill the reserve, as the second one will be seen as - a duplicate. Hence fill with twice the require amount per - round. */ - .details.admin_add_incoming.amount = "EUR:1.01" }, - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-insufficient-funds-tip-reserve" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-insufficient-funds-tip-reserve", - .details.check_bank_transfer.amount = "EUR:1.01", - .details.check_bank_transfer.account_debit = TIP_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO }, - { .oc = OC_TIP_AUTHORIZE, - .label = "authorize-tip-3-insufficient-funds", - .expected_response_code = MHD_HTTP_PRECONDITION_FAILED, - .details.tip_authorize.instance = "dtip", - .details.tip_authorize.justification = "tip 3", - .details.tip_authorize.amount = "EUR:5.01", - .details.tip_authorize.expected_ec = TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS }, - { .oc = OC_TIP_AUTHORIZE, - .label = "authorize-tip-4-unknown-instance", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.tip_authorize.instance = "unknown", - .details.tip_authorize.justification = "tip 4", - .details.tip_authorize.amount = "EUR:5.01", - .details.tip_authorize.expected_ec = TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN }, - { .oc = OC_TIP_AUTHORIZE, - .label = "authorize-tip-5-notip-instance", - .expected_response_code = MHD_HTTP_NOT_FOUND, - .details.tip_authorize.instance = "default", - .details.tip_authorize.justification = "tip 5", - .details.tip_authorize.amount = "EUR:5.01", - .details.tip_authorize.expected_ec = TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP }, - - { .oc = OC_TIP_PICKUP, - .label = "pickup-tip-3-too-much", - .expected_response_code = MHD_HTTP_CONFLICT, - .details.tip_pickup.expected_ec = TALER_EC_TIP_PICKUP_NO_FUNDS, - .details.tip_pickup.authorize_ref = "authorize-tip-1", - .details.tip_pickup.amounts = pickup_amounts_1 }, - /* Spend tip (just to be sure...) */ - { .oc = OC_PROPOSAL, - .label = "create-proposal-tip-1", - .expected_response_code = MHD_HTTP_OK, - .details.proposal.order = "{\ - \"max_fee\":\ + 2, + 10, + -10), + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command refund[] = { + TALER_TESTING_cmd_refund_increase + ("refund-increase-1", + merchant_url, + "refund test", + "1", + "EUR:0.1", + "EUR:0.01", + MHD_HTTP_OK), + + /* Ordinary refund. */ + TALER_TESTING_cmd_refund_lookup ("refund-lookup-1", + merchant_url, + "refund-increase-1", + "deposit-simple", + "1", + MHD_HTTP_OK), + /* Trying to pick up refund from non existent proposal. */ + TALER_TESTING_cmd_refund_lookup ("refund-lookup-non-existent", + merchant_url, + "refund-increase-1", + "deposit-simple", + "non-existend-id", + MHD_HTTP_NOT_FOUND), + + /* Test /refund on a contract that was never paid. */ + + TALER_TESTING_cmd_proposal + ("create-proposal-not-to-be-paid", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ + {\"currency\":\"EUR\",\ + \"value\":0,\ + \"fraction\":50000000},\ + \"order_id\":\"1-unpaid\",\ + \"refund_deadline\":\"\\/Date(0)\\/\",\ + \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ + \"amount\":\ + {\"currency\":\"EUR\",\ + \"value\":5,\ + \"fraction\":0},\ + \"summary\": \"useful product\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:5}\"} ] }", + NULL), + + /* Try to increase a non paid proposal. */ + TALER_TESTING_cmd_refund_increase + ("refund-increase-unpaid-proposal", + merchant_url, + "refund test", + "1-unpaid", + "EUR:0.1", + "EUR:0.01", + MHD_HTTP_BAD_REQUEST), + + /* Try to increase a non existent proposal. */ + TALER_TESTING_cmd_refund_increase + ("refund-increase-unpaid-proposal", + merchant_url, + "refund test", + "non-existent-id", + "EUR:0.1", + "EUR:0.01", + MHD_HTTP_NOT_FOUND), + + /** + * The following block will (1) create a new + * reserve, then (2) a proposal, then (3) pay for + * it, and finally (4) attempt to pick up a refund + * from it without any increasing taking place + * in the first place. + **/ + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-unincreased-refund", + "EUR:5.01"), + + CMD_EXEC_WIREWATCH ("wirewatch-unincreased-refund"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-unincreased-refund", + EXCHANGE_URL, + "EUR:5.01", + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_withdraw_amount + ("withdraw-coin-unincreased-refund", + "create-reserve-unincreased-refund", + "EUR:5", + MHD_HTTP_OK), + + TALER_TESTING_cmd_proposal + ("create-proposal-unincreased-refund", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ + {\"currency\":\"EUR\",\ + \"value\":0,\ + \"fraction\":50000000},\ + \"order_id\":\"unincreased-proposal\",\ + \"refund_deadline\":\"\\/Date(0)\\/\",\ + \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ + \"amount\":\ + {\"currency\":\"EUR\",\ + \"value\":5,\ + \"fraction\":0},\ + \"summary\": \"merchant-lib testcase\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:5}\"} ] }", + NULL), + + TALER_TESTING_cmd_pay ("pay-unincreased-proposal", + merchant_url, + MHD_HTTP_OK, + "create-proposal-unincreased-refund", + "withdraw-coin-unincreased-refund", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + CMD_EXEC_AGGREGATOR ("run-aggregator-unincreased-refund"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-unincreased-refund", + EXCHANGE_URL, + "EUR:4.98", + EXCHANGE_ACCOUNT_NO, + MERCHANT_ACCOUNT_NO), + + /* Actually try to pick up the refund from the + * "unincreased proposal". */ + TALER_TESTING_cmd_refund_lookup_with_amount + ("refund-lookup-unincreased", + merchant_url, + NULL, + "pay-unincreased-proposal", + "unincreased-proposal", + MHD_HTTP_OK, + /* If a lookup is attempted on an unincreased proposal, + * the backend will simply respond with a empty refunded + * coin "set", but the HTTP response code is 200 OK. */ + "EUR:0"), + + TALER_TESTING_cmd_end () + }; + + + struct TALER_TESTING_Command tip[] = { + + /* Test tipping. */ + TALER_TESTING_cmd_fakebank_transfer_with_instance + ("create-reserve-tip-1", + "EUR:20.04", + fakebank_url, + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO, + USER_LOGIN_NAME, + USER_LOGIN_PASS, + "tip", + EXCHANGE_URL, + CONFIG_FILE), + + CMD_EXEC_WIREWATCH ("wirewatch-3"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-tip-1", + EXCHANGE_URL, + "EUR:20.04", USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_tip_authorize ("authorize-tip-1", + merchant_url, + exchange_url, + MHD_HTTP_OK, + "tip", + "tip 1", + "EUR:5.01"), + + TALER_TESTING_cmd_tip_authorize ("authorize-tip-2", + merchant_url, + exchange_url, + MHD_HTTP_OK, + "tip", + "tip 2", + "EUR:5.01"), + + /* This command tests the authorization of tip + * against a reserve that does not exist. This is + * implemented by passing a "tip instance" that + * specifies a reserve key that was never used to + * actually create a reserve. */ + TALER_TESTING_cmd_tip_authorize_with_ec + ("authorize-tip-null", + merchant_url, + exchange_url, + MHD_HTTP_NOT_FOUND, + "nulltip", + "tip 2", + "EUR:5.01", + TALER_EC_RESERVE_STATUS_UNKNOWN), + + TALER_TESTING_cmd_tip_query ("query-tip-1", + merchant_url, + MHD_HTTP_OK, + "tip"), + + TALER_TESTING_cmd_tip_query_with_amounts ("query-tip-2", + merchant_url, + MHD_HTTP_OK, + "tip", + "EUR:0.0", // picked + "EUR:10.02", // auth + "EUR:20.04"),// ava + + TALER_TESTING_cmd_tip_pickup ("pickup-tip-1", + merchant_url, + MHD_HTTP_OK, + "authorize-tip-1", + pickup_amounts_1), + + TALER_TESTING_cmd_tip_query_with_amounts ("query-tip-3", + merchant_url, + MHD_HTTP_OK, + "tip", + "EUR:5.01", // picked + NULL, // auth + "EUR:15.03"),// ava + + TALER_TESTING_cmd_tip_pickup ("pickup-tip-2", + merchant_url, + MHD_HTTP_OK, + "authorize-tip-2", + pickup_amounts_1), + + TALER_TESTING_cmd_tip_query_with_amounts ("query-tip-4", + merchant_url, + MHD_HTTP_OK, + "tip", + "EUR:10.02", // pick + "EUR:10.02", // auth + "EUR:10.02"), // ava + + TALER_TESTING_cmd_fakebank_transfer_with_instance + ("create-reserve-insufficient-funds", + "EUR:1.01", + fakebank_url, + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO, + USER_LOGIN_NAME, + USER_LOGIN_PASS, + "dtip", + EXCHANGE_URL, + CONFIG_FILE), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-insufficient-tip-funds", + EXCHANGE_URL, + "EUR:1.01", + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO), + + CMD_EXEC_WIREWATCH + ("wirewatch-insufficient-tip-funds"), + + TALER_TESTING_cmd_tip_authorize_with_ec + ("authorize-tip-3-insufficient-funds", + merchant_url, + exchange_url, + MHD_HTTP_PRECONDITION_FAILED, + "dtip", + "tip 3", + "EUR:2.02", + TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS), + + TALER_TESTING_cmd_tip_authorize_with_ec + ("authorize-tip-4-unknown-instance", + merchant_url, + exchange_url, + MHD_HTTP_NOT_FOUND, + "unknown", + "tip 4", + "EUR:5.01", + TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN), + + TALER_TESTING_cmd_tip_authorize_with_ec + ("authorize-tip-5-notip-instance", + merchant_url, + exchange_url, + MHD_HTTP_NOT_FOUND, + "default", + "tip 5", + "EUR:5.01", + TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP), + + TALER_TESTING_cmd_tip_pickup_with_ec + ("pickup-tip-3-too-much", + merchant_url, + MHD_HTTP_CONFLICT, + "authorize-tip-1", + pickup_amounts_1, + TALER_EC_TIP_PICKUP_NO_FUNDS), + + TALER_TESTING_cmd_tip_authorize_fake + ("fake-tip-authorization"), + + TALER_TESTING_cmd_tip_pickup_with_ec + ("pickup-non-existent-id", + merchant_url, + MHD_HTTP_NOT_FOUND, + "fake-tip-authorization", + pickup_amounts_1, + TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN), + + TALER_TESTING_cmd_proposal + ("create-proposal-tip-1", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ {\"currency\":\"EUR\",\ \"value\":0,\ \"fraction\":50000000},\ @@ -4713,87 +770,72 @@ run (void *cls) {\"currency\":\"EUR\",\ \"value\":5,\ \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\":\ - [ {\"description\":\"ice cream tip\",\ - \"value\":\"{EUR:5}\"} ] }"}, - { .oc = OC_PAY, - .label = "deposit-tip-simple", - .expected_response_code = MHD_HTTP_OK, - .details.pay.contract_ref = "create-proposal-tip-1", - .details.pay.coin_ref = "pickup-tip-1", - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - /* Run transfers. */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-tip-1" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-tip-498c", - .details.check_bank_transfer.amount = "EUR:4.98", - .details.check_bank_transfer.account_debit = EXCHANGE_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = MERCHANT_ACCOUNT_NO - }, - - /* Check that there are no other unusual transfers */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty" }, - - - /* ****************** /pay again logic ************* */ - - /* Fill reserve with EUR:10.02, as withdraw fee is 1 ct per - config */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-10", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = USER_ACCOUNT_NO, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user62", - .details.admin_add_incoming.auth_password = "pass62", - .details.admin_add_incoming.amount = "EUR:10.02" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-10" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-10", - .details.check_bank_transfer.amount = "EUR:10.02", - .details.check_bank_transfer.account_debit = USER_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO - }, - - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-10a", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference - = "create-reserve-10", - .details.reserve_withdraw.amount = "EUR:5" }, - - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-10b", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference - = "create-reserve-10", - .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-10", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_status.reserve_reference - = "create-reserve-10", - .details.reserve_status.expected_balance = "EUR:0" }, - - /* Create proposal */ - { .oc = OC_PROPOSAL, - .label = "create-proposal-10", - .expected_response_code = MHD_HTTP_OK, - .details.proposal.order = "{\ - \"max_fee\":\ + \"summary\": \"useful product\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:5}\"} ] }", + NULL), + + TALER_TESTING_cmd_pay ("deposit-tip-simple", + merchant_url, + MHD_HTTP_OK, + "create-proposal-tip-1", + "pickup-tip-1", + "EUR:5", // amount + fee + "EUR:4.99", // amount - fee + "EUR:0.01"), // refund fee + + CMD_EXEC_AGGREGATOR ("aggregator-tip-1"), + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-tip-498c", + EXCHANGE_URL, + "EUR:4.98", + EXCHANGE_ACCOUNT_NO, + MERCHANT_ACCOUNT_NO), + TALER_TESTING_cmd_check_bank_empty + ("check_bank_empty-at-tips"), + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command pay_again[] = { + + TALER_TESTING_cmd_fakebank_transfer + ("create-reserve-10", + "EUR:10.02", + fakebank_url, + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO, + USER_LOGIN_NAME, + USER_LOGIN_PASS, + EXCHANGE_URL), + + CMD_EXEC_WIREWATCH ("wirewatch-10"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-10", + EXCHANGE_URL, + "EUR:10.02", USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10a", + "create-reserve-10", + "EUR:5", + MHD_HTTP_OK), + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10b", + "create-reserve-10", + "EUR:5", + MHD_HTTP_OK), + TALER_TESTING_cmd_status ("withdraw-status-10", + "create-reserve-10", + "EUR:0", + MHD_HTTP_OK), + + + TALER_TESTING_cmd_proposal + ("create-proposal-10", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ {\"currency\":\"EUR\",\ \"value\":0,\ \"fraction\":50000000},\ @@ -4804,102 +846,76 @@ run (void *cls) {\"currency\":\"EUR\",\ \"value\":10,\ \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\":\ - [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:10}\"} ] }"}, - - /* execute simple payment, re-using one ancient coin */ - { .oc = OC_PAY, - .label = "pay-fail-partial-double-10", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.pay.contract_ref = "create-proposal-10", - .details.pay.coin_ref = "withdraw-coin-10a;withdraw-coin-1", - /* These amounts are given per coin! */ - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - - /* Try to replay payment reusing coin */ - { .oc = OC_PAY_AGAIN, - .label = "pay-again-10", - .expected_response_code = MHD_HTTP_OK, - .details.pay.refund_fee = "EUR:0.01", - .details.pay_again.pay_ref = "pay-fail-partial-double-10", - .details.pay_again.coin_ref = "withdraw-coin-10a;withdraw-coin-10b" }, - - /* Run transfers. */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-10" }, - - /* Obtain WTID of the transfer */ - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-9.97-10", - .details.check_bank_transfer.amount = "EUR:9.97", - .details.check_bank_transfer.account_debit = EXCHANGE_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = MERCHANT_ACCOUNT_NO - }, - - /* Check that there are no other unusual transfers */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty-10" }, - - - /* ****************** /pay abort logic ************* */ - - /* Fill reserve with EUR:10.02, as withdraw fee is 1 ct per - config */ - { .oc = OC_ADMIN_ADD_INCOMING, - .label = "create-reserve-11", - .expected_response_code = MHD_HTTP_OK, - .details.admin_add_incoming.debit_account_no = USER_ACCOUNT_NO, - .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO, - .details.admin_add_incoming.auth_username = "user62", - .details.admin_add_incoming.auth_password = "pass62", - .details.admin_add_incoming.amount = "EUR:10.02" }, - /* Run wirewatch to observe /admin/add/incoming */ - { .oc = OC_RUN_WIREWATCH, - .label = "wirewatch-11" }, - { .oc = OC_CHECK_BANK_TRANSFER, - .label = "check_bank_transfer-11", - .details.check_bank_transfer.amount = "EUR:10.02", - .details.check_bank_transfer.account_debit = USER_ACCOUNT_NO, - .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO - }, - - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-11a", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference - = "create-reserve-11", - .details.reserve_withdraw.amount = "EUR:5" }, - - /* Withdraw a 5 EUR coin, at fee of 1 ct */ - { .oc = OC_WITHDRAW_SIGN, - .label = "withdraw-coin-11b", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_withdraw.reserve_reference - = "create-reserve-11", - .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-11", - .expected_response_code = MHD_HTTP_OK, - .details.reserve_status.reserve_reference - = "create-reserve-11", - .details.reserve_status.expected_balance = "EUR:0" }, - - /* Create proposal, to be partially paid, then - * abort-refunded, and then paid again. */ - { .oc = OC_PROPOSAL, - .label = "create-proposal-11", - .expected_response_code = MHD_HTTP_OK, - .details.proposal.order = "{\ - \"max_fee\":\ + \"summary\": \"merchant-lib testcase\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:10}\"} ] }", + NULL), + + TALER_TESTING_cmd_pay ("pay-fail-partial-double-10", + merchant_url, + MHD_HTTP_FORBIDDEN, + "create-proposal-10", + "withdraw-coin-10a;withdraw-coin-1", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + TALER_TESTING_cmd_pay_again + ("pay-again-10", + merchant_url, + "pay-fail-partial-double-10", + "withdraw-coin-10a;withdraw-coin-10b", + "EUR:0.01", + MHD_HTTP_OK), + + CMD_EXEC_AGGREGATOR ("run-aggregator-10"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-9.97-10", + EXCHANGE_URL, + "EUR:9.97", + EXCHANGE_ACCOUNT_NO, + MERCHANT_ACCOUNT_NO), + + TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-10"), + + TALER_TESTING_cmd_end () + }; + + struct TALER_TESTING_Command pay_abort[] = { + CMD_TRANSFER_TO_EXCHANGE ("create-reserve-11", + "EUR:10.02"), + + CMD_EXEC_WIREWATCH ("wirewatch-11"), + + TALER_TESTING_cmd_check_bank_transfer + ("check_bank_transfer-11", + EXCHANGE_URL, + "EUR:10.02", + USER_ACCOUNT_NO, + EXCHANGE_ACCOUNT_NO), + + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-11a", + "create-reserve-11", + "EUR:5", + MHD_HTTP_OK), + + TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-11b", + "create-reserve-11", + "EUR:5", + MHD_HTTP_OK), + + TALER_TESTING_cmd_status ("withdraw-status-11", + "create-reserve-11", + "EUR:0", + MHD_HTTP_OK), + + TALER_TESTING_cmd_proposal + ("create-proposal-11", + merchant_url, + MHD_HTTP_OK, + "{\"max_fee\":\ {\"currency\":\"EUR\",\ \"value\":0,\ \"fraction\":50000000},\ @@ -4910,297 +926,158 @@ run (void *cls) {\"currency\":\"EUR\",\ \"value\":10,\ \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\":\ - [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:10}\"} ] }"}, + \"summary\": \"merchant-lib testcase\",\ + \"fulfillment_url\": \"https://example.com/\",\ + \"products\": [ {\"description\":\"ice cream\",\ + \"value\":\"{EUR:10}\"} ] }", + NULL), + + TALER_TESTING_cmd_pay ("pay-fail-partial-double-11-good", + merchant_url, + MHD_HTTP_NOT_ACCEPTABLE, + "create-proposal-11", + "withdraw-coin-11a", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + TALER_TESTING_cmd_pay ("pay-fail-partial-double-11-bad", + merchant_url, + MHD_HTTP_FORBIDDEN, + "create-proposal-11", + "withdraw-coin-1", + "EUR:5", + "EUR:4.99", + "EUR:0.01"), + + TALER_TESTING_cmd_pay_abort ("pay-abort-11", + merchant_url, + "pay-fail-partial-double-11-good", + MHD_HTTP_OK), + + TALER_TESTING_cmd_pay_abort_refund ("pay-abort-refund-11", + /* abort reference */ + "pay-abort-11", + 0, + "EUR:5", + "EUR:0.01", + MHD_HTTP_OK), + + CMD_EXEC_AGGREGATOR ("run-aggregator-11"), + + TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-11"), + + TALER_TESTING_cmd_end () + }; + struct TALER_TESTING_Command commands[] = { + + TALER_TESTING_cmd_batch ("pay", + pay), + + TALER_TESTING_cmd_batch ("double-spending", + double_spending), + + TALER_TESTING_cmd_batch ("track", + track), + TALER_TESTING_cmd_history + ("history-2", + merchant_url, + MHD_HTTP_OK, + GNUNET_TIME_absolute_add (GNUNET_TIME_UNIT_ZERO_ABS, + GNUNET_TIME_UNIT_MICROSECONDS), + /* zero results expected, there isn't any row with id + * bigger than 10. */ + 0, + 10, + 10), + + TALER_TESTING_cmd_batch ("refund", + refund), + + TALER_TESTING_cmd_batch ("tip", + tip), + + TALER_TESTING_cmd_batch ("pay-again", + pay_again), + + TALER_TESTING_cmd_batch ("pay-abort", + pay_abort), + + TALER_TESTING_cmd_history_default_start + ("history-default-start", + merchant_url, + MHD_HTTP_OK, + GNUNET_TIME_UNIT_ZERO_ABS, + 5, /* Expected number of records */ + -100), /* Delta */ /** - * The following two OC_PAY CMDs used to be one. - * - * However, This caused the refund to be issued _randomly_, - * because the order the good coin and the double-spent - * coin were deposited was random. Therefore, when - * the bad coin was first deposited, the CMD returned - * (403) and the good coin was never deposited. This - * used to make the "abort refund" CMD fail, since it - * expected at least one coin to be refunded. - * - * The workaround is to _split_ the payment, so as to make - * sure that the good coin is always deposited, therefore - * having _always_ one coin to get a refund for. - * - * The test case could now work even without the second - * half of the split, but we keep it for "historical" - * reasons. */ - { .oc = OC_PAY, - .label = "pay-fail-partial-double-11-not_acceptable", - .expected_response_code = MHD_HTTP_NOT_ACCEPTABLE, - .details.pay.contract_ref = "create-proposal-11", - .details.pay.coin_ref = "withdraw-coin-11a", - /* These amounts are given per coin! */ - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - { .oc = OC_PAY, - .label = "pay-fail-partial-double-11-forbidden", - .expected_response_code = MHD_HTTP_FORBIDDEN, - .details.pay.contract_ref = "create-proposal-11", - .details.pay.coin_ref = "withdraw-coin-1", - /* These amounts are given per coin! */ - .details.pay.refund_fee = "EUR:0.01", - .details.pay.amount_with_fee = "EUR:5", - .details.pay.amount_without_fee = "EUR:4.99" }, - - { .oc = OC_PAY_ABORT, - .label = "pay-abort-11", - .expected_response_code = MHD_HTTP_OK, - .details.pay_abort.pay_ref = "pay-fail-partial-double-11-not_acceptable", - }, - - { .oc = OC_PAY_ABORT_REFUND, - .label = "pay-abort-refund-11", - .expected_response_code = MHD_HTTP_OK, - /* Will provide the refund permission. */ - .details.pay_abort_refund.abort_ref = "pay-abort-11", - /* Coin _index_ to be refunded. */ - .details.pay_abort_refund.num_coin = 0, - .details.pay_abort_refund.refund_amount = "EUR:5", - .details.pay_abort_refund.refund_fee = "EUR:0.01" }, - - /* Run transfers. */ - { .oc = OC_RUN_AGGREGATOR, - .label = "run-aggregator-11" }, - /* Check that there are no other unusual transfers */ - { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, - .label = "check_bank_empty-11" }, - /* end of testcase */ - { .oc = OC_END } + * End the suite. Fixme: better to have a label for this + * too, as it shows a "(null)" token on logs. + */ + TALER_TESTING_cmd_end () }; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Interpreter initializing\n"); - fakebank = TALER_FAKEBANK_start (BANK_PORT); - if (NULL == fakebank) - { - fprintf (stderr, - "\nFailed to start fake bank service\n"); - result = 77; - return; - } - is = GNUNET_new (struct InterpreterState); - is->commands = commands; - - GNUNET_assert (ctx = GNUNET_CURL_init - (&GNUNET_CURL_gnunet_scheduler_reschedule, - &rc)); - GNUNET_CURL_enable_async_scope_header (ctx, "Taler-Correlation-Id"); - rc = GNUNET_CURL_gnunet_rc_create (ctx); - GNUNET_assert (NULL != (exchange - = TALER_EXCHANGE_connect (ctx, - EXCHANGE_URL, - &cert_cb, - is, - TALER_EXCHANGE_OPTION_END))); - timeout_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 150), - &do_timeout, NULL); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is); + TALER_TESTING_run_with_fakebank (is, + commands, + fakebank_url); } - -/** - * 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) { - char *_instances; - char *token; - struct GNUNET_OS_Process *proc; - struct GNUNET_OS_Process *exchanged; - struct GNUNET_OS_Process *merchantd; - unsigned int cnt; - struct GNUNET_SIGNAL_Context *shc_chld; - + unsigned int ret; + /* These environment variables get in the way... */ unsetenv ("XDG_DATA_HOME"); unsetenv ("XDG_CONFIG_HOME"); - GNUNET_log_setup ("test-merchant-api", + + GNUNET_log_setup ("test-merchant-api-new", "DEBUG", NULL); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_load (cfg, - "test_merchant_api.conf")); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, - "TEST", - "INSTANCES", - &_instances)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Found instances `%s'\n", - _instances); - GNUNET_break (NULL != (token = strtok (_instances, " "))); - GNUNET_array_append (instances, - ninstances, - GNUNET_strdup (token)); - while (NULL != (token = strtok (NULL, " "))) - GNUNET_array_append (instances, - ninstances, - GNUNET_strdup (token)); - GNUNET_free (_instances); - instance = instances[instance_idx]; - instance_priv = get_instance_priv (cfg, instance); - db = TALER_MERCHANTDB_plugin_load (cfg); - if (NULL == db) - { - GNUNET_CONFIGURATION_destroy (cfg); - return 77; - } - (void) db->drop_tables (db->cls); - if (GNUNET_OK != db->initialize (db->cls)) - { - TALER_MERCHANTDB_plugin_unload (db); - GNUNET_CONFIGURATION_destroy (cfg); - return 77; - } - if (NULL == (proc = GNUNET_OS_start_process - (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-keyup", - "taler-exchange-keyup", - "-c", "test_merchant_api.conf", - NULL))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run taler-exchange-keyup. Check your PATH.\n"); - return 77; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - if (NULL == (proc = GNUNET_OS_start_process - (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-dbinit", - "taler-exchange-dbinit", - "-c", "test_merchant_api.conf", - "-r", - NULL))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run taler-exchange-dbinit. Check your PATH.\n"); + if (NULL == + (fakebank_url = TALER_TESTING_prepare_fakebank + (CONFIG_FILE, + "account-exchange"))) return 77; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - if (NULL == (exchanged = GNUNET_OS_start_process - (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-httpd", - "taler-exchange-httpd", - "-c", "test_merchant_api.conf", - NULL))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run taler-exchange-httpd. Check your PATH.\n"); + if (NULL == + (merchant_url = TALER_TESTING_prepare_merchant (CONFIG_FILE))) return 77; - } - /* give child time to start and bind against the socket */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Waiting for taler-exchange-httpd to be ready\n"); - cnt = 0; - do - { - fprintf (stderr, "."); - sleep (1); - cnt++; - if (cnt > 60) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "\nFailed to start taler-exchange-httpd\n"); - GNUNET_OS_process_kill (exchanged, - SIGKILL); - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); - return 77; - } - } - while (0 != system ("wget -q -t 1 -T 1 " EXCHANGE_URL "keys -o /dev/null -O /dev/null")); - fprintf (stderr, "\n"); - if (NULL == (merchantd = GNUNET_OS_start_process - (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-merchant-httpd", - "taler-merchant-httpd", - "-c", "test_merchant_api.conf", - "-L", "DEBUG", - NULL))) + + TALER_TESTING_cleanup_files (CONFIG_FILE); + + switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, + &auditor_url, + &exchange_url)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run taler-merchant-httpd. Check your PATH.\n"); - GNUNET_OS_process_kill (exchanged, - SIGKILL); - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); + case GNUNET_SYSERR: + GNUNET_break (0); + return 1; + case GNUNET_NO: return 77; + + case GNUNET_OK: + + if (NULL == (merchantd = + TALER_TESTING_run_merchant (CONFIG_FILE, merchant_url))) + return 1; + + ret = TALER_TESTING_setup_with_exchange (&run, + NULL, + CONFIG_FILE); + + GNUNET_OS_process_kill (merchantd, SIGTERM); + GNUNET_OS_process_wait (merchantd); + GNUNET_OS_process_destroy (merchantd); + GNUNET_free (merchant_url); + + if (GNUNET_OK != ret) + return 1; + break; + default: + GNUNET_break (0); + return 1; } - /* give child time to start and bind against the socket */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Waiting for taler-merchant-httpd to be ready\n"); - cnt = 0; - do - { - fprintf (stderr, "."); - sleep (1); - cnt++; - if (cnt > 60) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "\nFailed to start taler-merchant-httpd\n"); - GNUNET_OS_process_kill (merchantd, - SIGKILL); - GNUNET_OS_process_wait (merchantd); - GNUNET_OS_process_destroy (merchantd); - GNUNET_OS_process_kill (exchanged, - SIGKILL); - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); - return 77; - } - } - while (0 != system ("wget -q -t 1 -T 1 " MERCHANT_URL " -o /dev/null -O /dev/null")); - fprintf (stderr, "\n"); - - result = GNUNET_SYSERR; - GNUNET_assert (NULL != (sigpipe = GNUNET_DISK_pipe - (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO))); - 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_OS_process_kill (merchantd, - SIGTERM); - GNUNET_OS_process_wait (merchantd); - GNUNET_OS_process_destroy (merchantd); - GNUNET_OS_process_kill (exchanged, - SIGTERM); - GNUNET_OS_process_wait (exchanged); - GNUNET_OS_process_destroy (exchanged); - if (77 == result) - return 77; - return (GNUNET_OK == result) ? 0 : 1; + return 0; } + +/* end of test_merchant_api_new.c */ diff --git a/src/lib/test_merchant_api_new.c b/src/lib/test_merchant_api_new.c deleted file mode 100644 index 67df2b93..00000000 --- a/src/lib/test_merchant_api_new.c +++ /dev/null @@ -1,1083 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 3, or - (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, see - -*/ - -/** - * @file exchange/test_merchant_api_new.c - * @brief testcase to test exchange's HTTP API interface - * @author Sree Harsha Totakura - * @author Christian Grothoff - * @author Marcello Stanisci - */ - -#include "platform.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "taler_merchant_testing_lib.h" - -/** - * Configuration file we use. One (big) configuration is used - * for the various components for this test. - */ -#define CONFIG_FILE "test_merchant_api.conf" - -/** - * Exchange base URL. Could also be taken from config. - */ -#define EXCHANGE_URL "http://localhost:8081/" - -static const char *pickup_amounts_1[] = {"EUR:5", NULL}; - -/** - * URL of the fakebank. - */ -static char *fakebank_url; - -/** - * Merchant base URL. - */ -static char *merchant_url; - -/** - * Merchant process. - */ -static struct GNUNET_OS_Process *merchantd; - -/** - * Exchange base URL. - */ -static char *exchange_url; - -/** - * Auditor base URL; only used to fix FTBFS. - */ -static char *auditor_url; - -/** - * Account number of the exchange at the bank. - */ -#define EXCHANGE_ACCOUNT_NO 2 - -/** - * Account number of some user. - */ -#define USER_ACCOUNT_NO 62 - -/** - * Account number used by the merchant - */ -#define MERCHANT_ACCOUNT_NO 3 - -/** - * User name. Never checked by fakebank. - */ -#define USER_LOGIN_NAME "user42" - -/** - * User password. Never checked by fakebank. - */ -#define USER_LOGIN_PASS "pass42" - -/** - * Execute the taler-exchange-wirewatch command with - * our configuration file. - * - * @param label label to use for the command. - */ -#define CMD_EXEC_WIREWATCH(label) \ - TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE) - -/** - * Execute the taler-exchange-aggregator command with - * our configuration file. - * - * @param label label to use for the command. - */ -#define CMD_EXEC_AGGREGATOR(label) \ - TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE) - -/** - * Run wire transfer of funds from some user's account to the - * exchange. - * - * @param label label to use for the command. - * @param amount amount to transfer, i.e. "EUR:1" - * @param url exchange_url - */ -#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ - TALER_TESTING_cmd_fakebank_transfer (label, amount, \ - fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \ - USER_LOGIN_NAME, USER_LOGIN_PASS, EXCHANGE_URL) - -/** - * Run wire transfer of funds from some user's account to the - * exchange. - * - * @param label label to use for the command. - * @param amount amount to transfer, i.e. "EUR:1" - */ -#define CMD_TRANSFER_TO_EXCHANGE_SUBJECT(label,amount,subject) \ - TALER_TESTING_cmd_fakebank_transfer_with_subject \ - (label, amount, fakebank_url, USER_ACCOUNT_NO, \ - EXCHANGE_ACCOUNT_NO, USER_LOGIN_NAME, USER_LOGIN_PASS, \ - subject) - -/** - * Main function that will tell the interpreter what commands to - * run. - * - * @param cls closure - */ -static void -run (void *cls, - struct TALER_TESTING_Interpreter *is) -{ - struct TALER_TESTING_Command pay[] = { - /** - * Move money to the exchange's bank account. - */ - CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1", - "EUR:10.02"), - /** - * Make a reserve exist, according to the previous - * transfer. - */ - CMD_EXEC_WIREWATCH ("wirewatch-1"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-2", - EXCHANGE_URL, - "EUR:10.02", - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_withdraw_amount - ("withdraw-coin-1", - "create-reserve-1", - "EUR:5", - MHD_HTTP_OK), - - TALER_TESTING_cmd_withdraw_amount - ("withdraw-coin-2", - "create-reserve-1", - "EUR:5", - MHD_HTTP_OK), - - /** - * Check the reserve is depleted. - */ - TALER_TESTING_cmd_status ("withdraw-status-1", - "create-reserve-1", - "EUR:0", - MHD_HTTP_OK), - - TALER_TESTING_cmd_proposal - ("create-proposal-1", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"1\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":5,\ - \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }", - NULL), - - TALER_TESTING_cmd_check_payment ("check-payment-1", - merchant_url, - MHD_HTTP_OK, - "create-proposal-1", - GNUNET_NO), - TALER_TESTING_cmd_pay ("deposit-simple", - merchant_url, - MHD_HTTP_OK, - "create-proposal-1", - "withdraw-coin-1", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - TALER_TESTING_cmd_check_payment ("check-payment-2", - merchant_url, - MHD_HTTP_OK, - "create-proposal-1", - GNUNET_YES), - - TALER_TESTING_cmd_pay_abort ("pay-abort-2", - merchant_url, - "deposit-simple", - MHD_HTTP_FORBIDDEN), - - TALER_TESTING_cmd_pay ("replay-simple", - merchant_url, - MHD_HTTP_OK, - "create-proposal-1", - "withdraw-coin-1", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - TALER_TESTING_cmd_check_bank_empty - ("check_bank_empty-1"), - - CMD_EXEC_AGGREGATOR ("run-aggregator"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-498c", - EXCHANGE_URL, - "EUR:4.98", - EXCHANGE_ACCOUNT_NO, - MERCHANT_ACCOUNT_NO), - - TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-2"), - - TALER_TESTING_cmd_end () - }; - - - struct TALER_TESTING_Command double_spending[] = { - - TALER_TESTING_cmd_proposal - ("create-proposal-2", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"2\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":5,\ - \"fraction\":0},\ - \"summary\": \"useful product\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }", - NULL), - - TALER_TESTING_cmd_proposal_lookup ("fetch-proposal-2", - merchant_url, - MHD_HTTP_OK, - "create-proposal-2", - NULL), - - TALER_TESTING_cmd_pay ("deposit-double-2", - merchant_url, - MHD_HTTP_FORBIDDEN, - "create-proposal-2", - "withdraw-coin-1", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - TALER_TESTING_cmd_history ("history-0", - merchant_url, - MHD_HTTP_OK, - /** - * all records to be returned; setting date as 0 lets the - * interpreter set it as 'now' + one hour delta, just to - * make sure it surpasses the proposal's timestamp. - */ - GNUNET_TIME_UNIT_ZERO_ABS, - /** - * We only expect ONE result (create-proposal-1) to be - * included in /history response, because create-proposal-3 - * did NOT go through because of double spending. - */ - 1, // nresult - 10, // start - -10), // nrows - - - TALER_TESTING_cmd_end () - }; - - struct TALER_TESTING_Command track[] = { - - TALER_TESTING_cmd_merchant_track_transaction - ("track-transaction-1", - merchant_url, - MHD_HTTP_OK, - "deposit-simple"), - - TALER_TESTING_cmd_merchant_track_transfer - ("track-transfer-1", - merchant_url, - MHD_HTTP_OK, - "check_bank_transfer-498c"), - - TALER_TESTING_cmd_merchant_track_transfer - ("track-transfer-again", - merchant_url, - MHD_HTTP_OK, - "check_bank_transfer-498c"), - - TALER_TESTING_cmd_fakebank_transfer - ("create-reserve-2", - "EUR:1", - fakebank_url, - USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, - "user62", - "pass62", - EXCHANGE_URL), - - TALER_TESTING_cmd_fakebank_transfer_with_ref - ("create-reserve-2b", - "EUR:4.01", - fakebank_url, - USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, - "user62", - "pass62", - "create-reserve-2", - EXCHANGE_URL), - - CMD_EXEC_WIREWATCH ("wirewatch-2"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-2a", - EXCHANGE_URL, - "EUR:1", - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-2b", - EXCHANGE_URL, - "EUR:4.01", - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2", - "create-reserve-2", - "EUR:5", - MHD_HTTP_OK), - - TALER_TESTING_cmd_pay ("deposit-simple-2", - merchant_url, - MHD_HTTP_OK, - "create-proposal-2", - "withdraw-coin-2", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - CMD_EXEC_AGGREGATOR ("run-aggregator-2"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-498c-2", - EXCHANGE_URL, - "EUR:4.98", - EXCHANGE_ACCOUNT_NO, - MERCHANT_ACCOUNT_NO), - - TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"), - - TALER_TESTING_cmd_merchant_track_transfer - ("track-transfer-2", - merchant_url, - MHD_HTTP_OK, - "check_bank_transfer-498c-2"), - - TALER_TESTING_cmd_merchant_track_transfer - ("track-transfer-2-again", - merchant_url, - MHD_HTTP_OK, - "check_bank_transfer-498c-2"), - - TALER_TESTING_cmd_merchant_track_transaction - ("track-transaction-2", - merchant_url, - MHD_HTTP_OK, - "deposit-simple-2"), - - TALER_TESTING_cmd_history ("history-1", - merchant_url, - MHD_HTTP_OK, - GNUNET_TIME_UNIT_ZERO_ABS, - /** - * Now we expect BOTH contracts (create-proposal-{1,2}) - * to be included in /history response, because - * create-proposal-2 has now been correctly paid. - */ - 2, - 10, - -10), - TALER_TESTING_cmd_end () - }; - - struct TALER_TESTING_Command refund[] = { - TALER_TESTING_cmd_refund_increase - ("refund-increase-1", - merchant_url, - "refund test", - "1", - "EUR:0.1", - "EUR:0.01", - MHD_HTTP_OK), - - /* Ordinary refund. */ - TALER_TESTING_cmd_refund_lookup ("refund-lookup-1", - merchant_url, - "refund-increase-1", - "deposit-simple", - "1", - MHD_HTTP_OK), - /* Trying to pick up refund from non existent proposal. */ - TALER_TESTING_cmd_refund_lookup ("refund-lookup-non-existent", - merchant_url, - "refund-increase-1", - "deposit-simple", - "non-existend-id", - MHD_HTTP_NOT_FOUND), - - /* Test /refund on a contract that was never paid. */ - - TALER_TESTING_cmd_proposal - ("create-proposal-not-to-be-paid", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"1-unpaid\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":5,\ - \"fraction\":0},\ - \"summary\": \"useful product\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }", - NULL), - - /* Try to increase a non paid proposal. */ - TALER_TESTING_cmd_refund_increase - ("refund-increase-unpaid-proposal", - merchant_url, - "refund test", - "1-unpaid", - "EUR:0.1", - "EUR:0.01", - MHD_HTTP_BAD_REQUEST), - - /* Try to increase a non existent proposal. */ - TALER_TESTING_cmd_refund_increase - ("refund-increase-unpaid-proposal", - merchant_url, - "refund test", - "non-existent-id", - "EUR:0.1", - "EUR:0.01", - MHD_HTTP_NOT_FOUND), - - /** - * The following block will (1) create a new - * reserve, then (2) a proposal, then (3) pay for - * it, and finally (4) attempt to pick up a refund - * from it without any increasing taking place - * in the first place. - **/ - CMD_TRANSFER_TO_EXCHANGE ("create-reserve-unincreased-refund", - "EUR:5.01"), - - CMD_EXEC_WIREWATCH ("wirewatch-unincreased-refund"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-unincreased-refund", - EXCHANGE_URL, - "EUR:5.01", - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_withdraw_amount - ("withdraw-coin-unincreased-refund", - "create-reserve-unincreased-refund", - "EUR:5", - MHD_HTTP_OK), - - TALER_TESTING_cmd_proposal - ("create-proposal-unincreased-refund", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"unincreased-proposal\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":5,\ - \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }", - NULL), - - TALER_TESTING_cmd_pay ("pay-unincreased-proposal", - merchant_url, - MHD_HTTP_OK, - "create-proposal-unincreased-refund", - "withdraw-coin-unincreased-refund", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - CMD_EXEC_AGGREGATOR ("run-aggregator-unincreased-refund"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-unincreased-refund", - EXCHANGE_URL, - "EUR:4.98", - EXCHANGE_ACCOUNT_NO, - MERCHANT_ACCOUNT_NO), - - /* Actually try to pick up the refund from the - * "unincreased proposal". */ - TALER_TESTING_cmd_refund_lookup_with_amount - ("refund-lookup-unincreased", - merchant_url, - NULL, - "pay-unincreased-proposal", - "unincreased-proposal", - MHD_HTTP_OK, - /* If a lookup is attempted on an unincreased proposal, - * the backend will simply respond with a empty refunded - * coin "set", but the HTTP response code is 200 OK. */ - "EUR:0"), - - TALER_TESTING_cmd_end () - }; - - - struct TALER_TESTING_Command tip[] = { - - /* Test tipping. */ - TALER_TESTING_cmd_fakebank_transfer_with_instance - ("create-reserve-tip-1", - "EUR:20.04", - fakebank_url, - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO, - USER_LOGIN_NAME, - USER_LOGIN_PASS, - "tip", - EXCHANGE_URL, - CONFIG_FILE), - - CMD_EXEC_WIREWATCH ("wirewatch-3"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-tip-1", - EXCHANGE_URL, - "EUR:20.04", USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_tip_authorize ("authorize-tip-1", - merchant_url, - exchange_url, - MHD_HTTP_OK, - "tip", - "tip 1", - "EUR:5.01"), - - TALER_TESTING_cmd_tip_authorize ("authorize-tip-2", - merchant_url, - exchange_url, - MHD_HTTP_OK, - "tip", - "tip 2", - "EUR:5.01"), - - /* This command tests the authorization of tip - * against a reserve that does not exist. This is - * implemented by passing a "tip instance" that - * specifies a reserve key that was never used to - * actually create a reserve. */ - TALER_TESTING_cmd_tip_authorize_with_ec - ("authorize-tip-null", - merchant_url, - exchange_url, - MHD_HTTP_NOT_FOUND, - "nulltip", - "tip 2", - "EUR:5.01", - TALER_EC_RESERVE_STATUS_UNKNOWN), - - TALER_TESTING_cmd_tip_query ("query-tip-1", - merchant_url, - MHD_HTTP_OK, - "tip"), - - TALER_TESTING_cmd_tip_query_with_amounts ("query-tip-2", - merchant_url, - MHD_HTTP_OK, - "tip", - "EUR:0.0", // picked - "EUR:10.02", // auth - "EUR:20.04"),// ava - - TALER_TESTING_cmd_tip_pickup ("pickup-tip-1", - merchant_url, - MHD_HTTP_OK, - "authorize-tip-1", - pickup_amounts_1), - - TALER_TESTING_cmd_tip_query_with_amounts ("query-tip-3", - merchant_url, - MHD_HTTP_OK, - "tip", - "EUR:5.01", // picked - NULL, // auth - "EUR:15.03"),// ava - - TALER_TESTING_cmd_tip_pickup ("pickup-tip-2", - merchant_url, - MHD_HTTP_OK, - "authorize-tip-2", - pickup_amounts_1), - - TALER_TESTING_cmd_tip_query_with_amounts ("query-tip-4", - merchant_url, - MHD_HTTP_OK, - "tip", - "EUR:10.02", // pick - "EUR:10.02", // auth - "EUR:10.02"), // ava - - TALER_TESTING_cmd_fakebank_transfer_with_instance - ("create-reserve-insufficient-funds", - "EUR:1.01", - fakebank_url, - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO, - USER_LOGIN_NAME, - USER_LOGIN_PASS, - "dtip", - EXCHANGE_URL, - CONFIG_FILE), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-insufficient-tip-funds", - EXCHANGE_URL, - "EUR:1.01", - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO), - - CMD_EXEC_WIREWATCH - ("wirewatch-insufficient-tip-funds"), - - TALER_TESTING_cmd_tip_authorize_with_ec - ("authorize-tip-3-insufficient-funds", - merchant_url, - exchange_url, - MHD_HTTP_PRECONDITION_FAILED, - "dtip", - "tip 3", - "EUR:2.02", - TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS), - - TALER_TESTING_cmd_tip_authorize_with_ec - ("authorize-tip-4-unknown-instance", - merchant_url, - exchange_url, - MHD_HTTP_NOT_FOUND, - "unknown", - "tip 4", - "EUR:5.01", - TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN), - - TALER_TESTING_cmd_tip_authorize_with_ec - ("authorize-tip-5-notip-instance", - merchant_url, - exchange_url, - MHD_HTTP_NOT_FOUND, - "default", - "tip 5", - "EUR:5.01", - TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP), - - TALER_TESTING_cmd_tip_pickup_with_ec - ("pickup-tip-3-too-much", - merchant_url, - MHD_HTTP_CONFLICT, - "authorize-tip-1", - pickup_amounts_1, - TALER_EC_TIP_PICKUP_NO_FUNDS), - - TALER_TESTING_cmd_tip_authorize_fake - ("fake-tip-authorization"), - - TALER_TESTING_cmd_tip_pickup_with_ec - ("pickup-non-existent-id", - merchant_url, - MHD_HTTP_NOT_FOUND, - "fake-tip-authorization", - pickup_amounts_1, - TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN), - - TALER_TESTING_cmd_proposal - ("create-proposal-tip-1", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"1-tip\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":5,\ - \"fraction\":0},\ - \"summary\": \"useful product\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:5}\"} ] }", - NULL), - - TALER_TESTING_cmd_pay ("deposit-tip-simple", - merchant_url, - MHD_HTTP_OK, - "create-proposal-tip-1", - "pickup-tip-1", - "EUR:5", // amount + fee - "EUR:4.99", // amount - fee - "EUR:0.01"), // refund fee - - CMD_EXEC_AGGREGATOR ("aggregator-tip-1"), - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-tip-498c", - EXCHANGE_URL, - "EUR:4.98", - EXCHANGE_ACCOUNT_NO, - MERCHANT_ACCOUNT_NO), - TALER_TESTING_cmd_check_bank_empty - ("check_bank_empty-at-tips"), - - TALER_TESTING_cmd_end () - }; - - struct TALER_TESTING_Command pay_again[] = { - - TALER_TESTING_cmd_fakebank_transfer - ("create-reserve-10", - "EUR:10.02", - fakebank_url, - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO, - USER_LOGIN_NAME, - USER_LOGIN_PASS, - EXCHANGE_URL), - - CMD_EXEC_WIREWATCH ("wirewatch-10"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-10", - EXCHANGE_URL, - "EUR:10.02", USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10a", - "create-reserve-10", - "EUR:5", - MHD_HTTP_OK), - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-10b", - "create-reserve-10", - "EUR:5", - MHD_HTTP_OK), - TALER_TESTING_cmd_status ("withdraw-status-10", - "create-reserve-10", - "EUR:0", - MHD_HTTP_OK), - - - TALER_TESTING_cmd_proposal - ("create-proposal-10", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"10\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":10,\ - \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:10}\"} ] }", - NULL), - - TALER_TESTING_cmd_pay ("pay-fail-partial-double-10", - merchant_url, - MHD_HTTP_FORBIDDEN, - "create-proposal-10", - "withdraw-coin-10a;withdraw-coin-1", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - TALER_TESTING_cmd_pay_again - ("pay-again-10", - merchant_url, - "pay-fail-partial-double-10", - "withdraw-coin-10a;withdraw-coin-10b", - "EUR:0.01", - MHD_HTTP_OK), - - CMD_EXEC_AGGREGATOR ("run-aggregator-10"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-9.97-10", - EXCHANGE_URL, - "EUR:9.97", - EXCHANGE_ACCOUNT_NO, - MERCHANT_ACCOUNT_NO), - - TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-10"), - - TALER_TESTING_cmd_end () - }; - - struct TALER_TESTING_Command pay_abort[] = { - CMD_TRANSFER_TO_EXCHANGE ("create-reserve-11", - "EUR:10.02"), - - CMD_EXEC_WIREWATCH ("wirewatch-11"), - - TALER_TESTING_cmd_check_bank_transfer - ("check_bank_transfer-11", - EXCHANGE_URL, - "EUR:10.02", - USER_ACCOUNT_NO, - EXCHANGE_ACCOUNT_NO), - - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-11a", - "create-reserve-11", - "EUR:5", - MHD_HTTP_OK), - - TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-11b", - "create-reserve-11", - "EUR:5", - MHD_HTTP_OK), - - TALER_TESTING_cmd_status ("withdraw-status-11", - "create-reserve-11", - "EUR:0", - MHD_HTTP_OK), - - TALER_TESTING_cmd_proposal - ("create-proposal-11", - merchant_url, - MHD_HTTP_OK, - "{\"max_fee\":\ - {\"currency\":\"EUR\",\ - \"value\":0,\ - \"fraction\":50000000},\ - \"order_id\":\"11\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"pay_deadline\":\"\\/Date(99999999999)\\/\",\ - \"amount\":\ - {\"currency\":\"EUR\",\ - \"value\":10,\ - \"fraction\":0},\ - \"summary\": \"merchant-lib testcase\",\ - \"fulfillment_url\": \"https://example.com/\",\ - \"products\": [ {\"description\":\"ice cream\",\ - \"value\":\"{EUR:10}\"} ] }", - NULL), - - TALER_TESTING_cmd_pay ("pay-fail-partial-double-11-good", - merchant_url, - MHD_HTTP_NOT_ACCEPTABLE, - "create-proposal-11", - "withdraw-coin-11a", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - TALER_TESTING_cmd_pay ("pay-fail-partial-double-11-bad", - merchant_url, - MHD_HTTP_FORBIDDEN, - "create-proposal-11", - "withdraw-coin-1", - "EUR:5", - "EUR:4.99", - "EUR:0.01"), - - TALER_TESTING_cmd_pay_abort ("pay-abort-11", - merchant_url, - "pay-fail-partial-double-11-good", - MHD_HTTP_OK), - - TALER_TESTING_cmd_pay_abort_refund ("pay-abort-refund-11", - /* abort reference */ - "pay-abort-11", - 0, - "EUR:5", - "EUR:0.01", - MHD_HTTP_OK), - - CMD_EXEC_AGGREGATOR ("run-aggregator-11"), - - TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-11"), - - TALER_TESTING_cmd_end () - }; - - struct TALER_TESTING_Command commands[] = { - - TALER_TESTING_cmd_batch ("pay", - pay), - - TALER_TESTING_cmd_batch ("double-spending", - double_spending), - - TALER_TESTING_cmd_batch ("track", - track), - TALER_TESTING_cmd_history - ("history-2", - merchant_url, - MHD_HTTP_OK, - GNUNET_TIME_absolute_add (GNUNET_TIME_UNIT_ZERO_ABS, - GNUNET_TIME_UNIT_MICROSECONDS), - /* zero results expected, there isn't any row with id - * bigger than 10. */ - 0, - 10, - 10), - - TALER_TESTING_cmd_batch ("refund", - refund), - - TALER_TESTING_cmd_batch ("tip", - tip), - - TALER_TESTING_cmd_batch ("pay-again", - pay_again), - - TALER_TESTING_cmd_batch ("pay-abort", - pay_abort), - - TALER_TESTING_cmd_history_default_start - ("history-default-start", - merchant_url, - MHD_HTTP_OK, - GNUNET_TIME_UNIT_ZERO_ABS, - 5, /* Expected number of records */ - -100), /* Delta */ - /** - * End the suite. Fixme: better to have a label for this - * too, as it shows a "(null)" token on logs. - */ - TALER_TESTING_cmd_end () - }; - - TALER_TESTING_run_with_fakebank (is, - commands, - fakebank_url); -} - -int -main (int argc, - char * const *argv) -{ - unsigned int ret; - /* These environment variables get in the way... */ - unsetenv ("XDG_DATA_HOME"); - unsetenv ("XDG_CONFIG_HOME"); - - GNUNET_log_setup ("test-merchant-api-new", - "DEBUG", - NULL); - if (NULL == - (fakebank_url = TALER_TESTING_prepare_fakebank - (CONFIG_FILE, - "account-exchange"))) - return 77; - if (NULL == - (merchant_url = TALER_TESTING_prepare_merchant (CONFIG_FILE))) - return 77; - - TALER_TESTING_cleanup_files (CONFIG_FILE); - - switch (TALER_TESTING_prepare_exchange (CONFIG_FILE, - &auditor_url, - &exchange_url)) - { - case GNUNET_SYSERR: - GNUNET_break (0); - return 1; - case GNUNET_NO: - return 77; - - case GNUNET_OK: - - if (NULL == (merchantd = - TALER_TESTING_run_merchant (CONFIG_FILE, merchant_url))) - return 1; - - ret = TALER_TESTING_setup_with_exchange (&run, - NULL, - CONFIG_FILE); - - GNUNET_OS_process_kill (merchantd, SIGTERM); - GNUNET_OS_process_wait (merchantd); - GNUNET_OS_process_destroy (merchantd); - GNUNET_free (merchant_url); - - if (GNUNET_OK != ret) - return 1; - break; - default: - GNUNET_break (0); - return 1; - } - return 0; -} - -/* end of test_merchant_api_new.c */ -- cgit v1.2.3