diff options
author | Florian Dold <florian.dold@gmail.com> | 2020-01-19 15:23:19 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2020-01-19 15:23:33 +0100 |
commit | 72a629a8be1ed2adc7402285e0f19f8f06816a26 (patch) | |
tree | 499468c12eaf06e8ad514db7b1ff684462c99ac2 /src/lib/testing_api_cmd_refresh.c | |
parent | dbf85bbc0d23b044aa47be67ceae4e6ecd709298 (diff) | |
download | exchange-72a629a8be1ed2adc7402285e0f19f8f06816a26.tar.xz |
move testing logic and integration tests into separate directory
Diffstat (limited to 'src/lib/testing_api_cmd_refresh.c')
-rw-r--r-- | src/lib/testing_api_cmd_refresh.c | 1411 |
1 files changed, 0 insertions, 1411 deletions
diff --git a/src/lib/testing_api_cmd_refresh.c b/src/lib/testing_api_cmd_refresh.c deleted file mode 100644 index 575f1a0c6..000000000 --- a/src/lib/testing_api_cmd_refresh.c +++ /dev/null @@ -1,1411 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3, or (at your - option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file lib/testing_api_cmd_refresh.c - * @brief commands for testing all "refresh" features. - * @author Marcello Stanisci - */ -#include "platform.h" -#include "taler_json_lib.h" -#include <gnunet/gnunet_curl_lib.h> -#include "exchange_api_handle.h" -#include "taler_testing_lib.h" -#include "taler_signatures.h" -#include "backoff.h" - - -/** - * Information about a fresh coin generated by the refresh - * operation. - */ -struct TALER_TESTING_FreshCoinData -{ - - /** - * If @e amount is NULL, this specifies the denomination key to - * use. Otherwise, this will be set (by the interpreter) to the - * denomination PK matching @e amount. - */ - const struct TALER_EXCHANGE_DenomPublicKey *pk; - - /** - * Set (by the interpreter) to the exchange's signature over the - * coin's public key. - */ - struct TALER_DenominationSignature sig; - - /** - * Set (by the interpreter) to the coin's private key. - */ - struct TALER_CoinSpendPrivateKeyP coin_priv; - - /** - * The blinding key (needed for recoup operations). - */ - struct TALER_DenominationBlindingKeyP blinding_key; - -}; - - -/** - * State for a "refresh melt" command. - */ -struct RefreshMeltState -{ - - /** - * Reference to reserve_withdraw operations for coin to - * be used for the /refresh/melt operation. - */ - const char *coin_reference; - - /** - * "Crypto data" used in the refresh operation. - */ - char *refresh_data; - - /** - * Reference to a previous melt command. - */ - const char *melt_reference; - - /** - * Melt handle while operation is running. - */ - struct TALER_EXCHANGE_RefreshMeltHandle *rmh; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Array of the denomination public keys - * corresponding to the @e num_fresh_coins; - */ - struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; - - /** - * Private key of the dirty coin being melted. - */ - const struct TALER_CoinSpendPrivateKeyP *melt_priv; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Number of bytes in @e refresh_data. - */ - size_t refresh_data_length; - - /** - * Amounts to be generated during melt. - */ - const char **melt_fresh_amounts; - - /** - * Number of fresh coins generated by the melt. - */ - unsigned int num_fresh_coins; - - /** - * Expected HTTP response code. - */ - unsigned int expected_response_code; - - /** - * if set to #GNUNET_YES, then two /refresh/melt operations - * will be performed. This is needed to trigger the logic - * that manages those already-made requests. Note: it - * is not possible to just copy-and-paste a test refresh melt - * CMD to have the same effect, because every data preparation - * generates new planchets that (in turn) make the whole "hash" - * different from any previous one, therefore NOT allowing the - * exchange to pick any previous /rerfesh/melt operation from - * the database. - */ - unsigned int double_melt; - - /** - * Should we retry on (transient) failures? - */ - int do_retry; - - /** - * Set by the melt callback as it comes from the exchange. - */ - uint16_t noreveal_index; -}; - - -/** - * State for a "refresh reveal" CMD. - */ -struct RefreshRevealState -{ - /** - * Link to a "refresh melt" command. - */ - const char *melt_reference; - - /** - * Reveal handle while operation is running. - */ - struct TALER_EXCHANGE_RefreshRevealHandle *rrh; - - /** - * Convenience struct to keep in one place all the - * data related to one fresh coin, set by the reveal callback - * as it comes from the exchange. - */ - struct TALER_TESTING_FreshCoinData *fresh_coins; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Number of fresh coins withdrawn, set by the - * reveal callback as it comes from the exchange, - * it is the length of the @e fresh_coins array. - */ - unsigned int num_fresh_coins; - - /** - * Expected HTTP response code. - */ - unsigned int expected_response_code; - - /** - * Should we retry on (transient) failures? - */ - int do_retry; - -}; - - -/** - * State for a "refresh link" CMD. - */ -struct RefreshLinkState -{ - /** - * Link to a "refresh reveal" command. - */ - const char *reveal_reference; - - /** - * Handle to the ongoing operation. - */ - struct TALER_EXCHANGE_RefreshLinkHandle *rlh; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Task scheduled to try later. - */ - struct GNUNET_SCHEDULER_Task *retry_task; - - /** - * How long do we wait until we retry? - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Expected HTTP response code. - */ - unsigned int expected_response_code; - - /** - * Should we retry on (transient) failures? - */ - int do_retry; - -}; - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -refresh_reveal_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #refresh_reveal_run. - * - * @param cls a `struct RefreshRevealState` - */ -static void -do_reveal_retry (void *cls) -{ - struct RefreshRevealState *rrs = cls; - - rrs->retry_task = NULL; - refresh_reveal_run (rrs, - NULL, - rrs->is); -} - - -/** - * "refresh reveal" request callback; it checks that the response - * code is expected and copies into its command's state the data - * coming from the exchange, namely the fresh coins. - * - * @param cls closure. - * @param http_status HTTP response code. - * @param ec taler-specific error code. - * @param num_coins number of fresh coins created, length of the - * @a sigs and @a coin_privs arrays, 0 if the operation - * failed. - * @param coin_privs array of @a num_coins private keys for the - * coins that were created, NULL on error. - * @param sigs array of signature over @a num_coins coins, - * NULL on error. - * @param full_response raw exchange response. - */ -static void -reveal_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - unsigned int num_coins, - const struct TALER_PlanchetSecretsP *coin_privs, - const struct TALER_DenominationSignature *sigs, - const json_t *full_response) -{ - struct RefreshRevealState *rrs = cls; - const struct TALER_TESTING_Command *melt_cmd; - - rrs->rrh = NULL; - if (rrs->expected_response_code != http_status) - { - if (GNUNET_YES == rrs->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying refresh reveal failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - rrs->backoff = GNUNET_TIME_UNIT_ZERO; - else - rrs->backoff = EXCHANGE_LIB_BACKOFF (rrs->backoff); - rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff, - &do_reveal_retry, - rrs); - return; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - http_status, - (int) ec, - rrs->is->commands[rrs->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (full_response, stderr, 0); - TALER_TESTING_interpreter_fail (rrs->is); - return; - } - melt_cmd = TALER_TESTING_interpreter_lookup_command - (rrs->is, rrs->melt_reference); - if (NULL == melt_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rrs->is); - return; - } - rrs->num_fresh_coins = num_coins; - switch (http_status) - { - case MHD_HTTP_OK: - rrs->fresh_coins = GNUNET_new_array (num_coins, - struct TALER_TESTING_FreshCoinData); - for (unsigned int i = 0; i<num_coins; i++) - { - struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i]; - - if (GNUNET_OK != - TALER_TESTING_get_trait_denom_pub (melt_cmd, - i, - &fc->pk)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rrs->is); - return; - } - fc->coin_priv = coin_privs[i].coin_priv; - fc->blinding_key = coin_privs[i].blinding_key; - fc->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup - (sigs[i].rsa_signature); - } - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unknown HTTP status %d\n", - http_status); - } - TALER_TESTING_interpreter_next (rrs->is); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -refresh_reveal_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct RefreshRevealState *rrs = cls; - struct RefreshMeltState *rms; - const struct TALER_TESTING_Command *melt_cmd; - - rrs->is = is; - melt_cmd = TALER_TESTING_interpreter_lookup_command - (is, rrs->melt_reference); - - if (NULL == melt_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rrs->is); - return; - } - rms = melt_cmd->cls; - rrs->rrh = TALER_EXCHANGE_refresh_reveal - (is->exchange, - rms->refresh_data_length, - rms->refresh_data, - rms->noreveal_index, - &reveal_cb, rrs); - - if (NULL == rrs->rrh) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } -} - - -/** - * Free the state from a "refresh reveal" CMD, and possibly - * cancel a pending operation thereof. - * - * @param cls closure. - * @param cmd the command which is being cleaned up. - */ -static void -refresh_reveal_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct RefreshRevealState *rrs = cls; - - if (NULL != rrs->rrh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rrs->is->ip, - cmd->label); - - TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh); - rrs->rrh = NULL; - } - if (NULL != rrs->retry_task) - { - GNUNET_SCHEDULER_cancel (rrs->retry_task); - rrs->retry_task = NULL; - } - - for (unsigned int j = 0; j < rrs->num_fresh_coins; j++) - GNUNET_CRYPTO_rsa_signature_free (rrs->fresh_coins[j].sig.rsa_signature); - - GNUNET_free_non_null (rrs->fresh_coins); - rrs->fresh_coins = NULL; - rrs->num_fresh_coins = 0; - GNUNET_free (rrs); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -refresh_link_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #refresh_link_run. - * - * @param cls a `struct RefreshLinkState` - */ -static void -do_link_retry (void *cls) -{ - struct RefreshLinkState *rls = cls; - - rls->retry_task = NULL; - refresh_link_run (rls, - NULL, - rls->is); -} - - -/** - * "refresh link" operation callback, checks that HTTP response - * code is expected _and_ that all the linked coins were actually - * withdrawn by the "refresh reveal" CMD. - * - * @param cls closure. - * @param http_status HTTP response code. - * @param ec taler-specific error code - * @param num_coins number of fresh coins created, length of the - * @a sigs and @a coin_privs arrays, 0 if the operation - * failed. - * @param coin_privs array of @a num_coins private keys for the - * coins that were created, NULL on error. - * @param sigs array of signature over @a num_coins coins, NULL on - * error. - * @param pubs array of public keys for the @a sigs, - * NULL on error. - * @param full_response raw response from the exchange. - */ -static void -link_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - unsigned int num_coins, - const struct TALER_CoinSpendPrivateKeyP *coin_privs, - const struct TALER_DenominationSignature *sigs, - const struct TALER_DenominationPublicKey *pubs, - const json_t *full_response) -{ - - struct RefreshLinkState *rls = cls; - const struct TALER_TESTING_Command *reveal_cmd; - struct TALER_TESTING_Command *link_cmd - = &rls->is->commands[rls->is->ip]; - unsigned int found; - const unsigned int *num_fresh_coins; - - rls->rlh = NULL; - if (rls->expected_response_code != http_status) - { - if (GNUNET_YES == rls->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying refresh link failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - rls->backoff = GNUNET_TIME_UNIT_ZERO; - else - rls->backoff = EXCHANGE_LIB_BACKOFF (rls->backoff); - rls->retry_task = GNUNET_SCHEDULER_add_delayed (rls->backoff, - &do_link_retry, - rls); - return; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - http_status, - (int) ec, - link_cmd->label, - __FILE__, - __LINE__); - json_dumpf (full_response, stderr, 0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - reveal_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, rls->reveal_reference); - - if (NULL == reveal_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - - switch (http_status) - { - case MHD_HTTP_OK: - /* check that number of coins returned matches */ - if (GNUNET_OK != TALER_TESTING_get_trait_uint - (reveal_cmd, 0, &num_fresh_coins)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - if (num_coins != *num_fresh_coins) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected number of fresh coins: %d vs %d in %s:%u\n", - num_coins, - *num_fresh_coins, - __FILE__, - __LINE__); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - /* check that the coins match */ - for (unsigned int i = 0; i<num_coins; i++) - for (unsigned int j = i + 1; j<num_coins; j++) - if (0 == GNUNET_memcmp - (&coin_privs[i], &coin_privs[j])) - GNUNET_break (0); - /* Note: coins might be legitimately permutated in here... */ - found = 0; - - /* Will point to the pointer inside the cmd state. */ - const struct TALER_TESTING_FreshCoinData *fc = NULL; - - if (GNUNET_OK != TALER_TESTING_get_trait_fresh_coins - (reveal_cmd, 0, &fc)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - - for (unsigned int i = 0; i<num_coins; i++) - for (unsigned int j = 0; j<num_coins; j++) - { - if ( (0 == GNUNET_memcmp - (&coin_privs[i], &fc[j].coin_priv)) && - (0 == GNUNET_CRYPTO_rsa_signature_cmp - (fc[i].sig.rsa_signature, - sigs[j].rsa_signature)) && - (0 == GNUNET_CRYPTO_rsa_public_key_cmp - (fc[i].pk->key.rsa_public_key, - pubs[j].rsa_public_key)) ) - { - found++; - break; - } - } - if (found != num_coins) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Only %u/%u coins match expectations\n", - found, num_coins); - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown HTTP response code %u.\n", - http_status); - } - TALER_TESTING_interpreter_next (rls->is); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -refresh_link_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct RefreshLinkState *rls = cls; - struct RefreshRevealState *rrs; - struct RefreshMeltState *rms; - const struct TALER_TESTING_Command *reveal_cmd; - const struct TALER_TESTING_Command *melt_cmd; - const struct TALER_TESTING_Command *coin_cmd; - - rls->is = is; - reveal_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, rls->reveal_reference); - - if (NULL == reveal_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - rrs = reveal_cmd->cls; - melt_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, rrs->melt_reference); - - if (NULL == melt_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - - /* find reserve_withdraw command */ - { - rms = melt_cmd->cls; - coin_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, rms->coin_reference); - if (NULL == coin_cmd) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - } - - const struct TALER_CoinSpendPrivateKeyP *coin_priv; - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv - (coin_cmd, 0, &coin_priv)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - - /* finally, use private key from withdraw sign command */ - rls->rlh = TALER_EXCHANGE_refresh_link - (is->exchange, coin_priv, &link_cb, rls); - - if (NULL == rls->rlh) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rls->is); - return; - } -} - - -/** - * Free the state of the "refresh link" CMD, and possibly - * cancel a operation thereof. - * - * @param cls closure - * @param cmd the command which is being cleaned up. - */ -static void -refresh_link_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct RefreshLinkState *rls = cls; - - if (NULL != rls->rlh) - { - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rls->is->ip, - cmd->label); - TALER_EXCHANGE_refresh_link_cancel (rls->rlh); - rls->rlh = NULL; - } - if (NULL != rls->retry_task) - { - GNUNET_SCHEDULER_cancel (rls->retry_task); - rls->retry_task = NULL; - } - GNUNET_free (rls); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -refresh_melt_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is); - - -/** - * Task scheduled to re-try #refresh_melt_run. - * - * @param cls a `struct RefreshMeltState` - */ -static void -do_melt_retry (void *cls) -{ - struct RefreshMeltState *rms = cls; - - rms->retry_task = NULL; - refresh_melt_run (rms, - NULL, - rms->is); -} - - -/** - * Callback for a "refresh melt" operation; checks if the HTTP - * response code is okay and re-run the melt operation if the - * CMD was set to do so. - * - * @param cls closure. - * @param http_status HTTP response code. - * @param ec taler-specific error code. - * @param noreveal_index choice by the exchange in the - * cut-and-choose protocol, UINT16_MAX on error. - * @param exchange_pub public key the exchange used for signing. - * @param full_response raw response body from the exchange. - */ -static void -melt_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint32_t noreveal_index, - const struct TALER_ExchangePublicKeyP *exchange_pub, - const json_t *full_response) -{ - struct RefreshMeltState *rms = cls; - - rms->rmh = NULL; - if (rms->expected_response_code != http_status) - { - if (GNUNET_YES == rms->do_retry) - { - if ( (0 == http_status) || - (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || - (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Retrying refresh melt failed with %u/%d\n", - http_status, - (int) ec); - /* on DB conflicts, do not use backoff */ - if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) - rms->backoff = GNUNET_TIME_UNIT_ZERO; - else - rms->backoff = EXCHANGE_LIB_BACKOFF (rms->backoff); - rms->retry_task = GNUNET_SCHEDULER_add_delayed - (rms->backoff, - &do_melt_retry, - rms); - return; - } - } - GNUNET_log - (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d to command %s in %s:%u\n", - http_status, - (int) ec, - rms->is->commands[rms->is->ip].label, - __FILE__, - __LINE__); - json_dumpf (full_response, stderr, 0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - rms->noreveal_index = noreveal_index; - - if (GNUNET_YES == rms->double_melt) - { - TALER_LOG_DEBUG ("Doubling the melt (%s)\n", - rms->is->commands[rms->is->ip].label); - rms->rmh = TALER_EXCHANGE_refresh_melt - (rms->is->exchange, rms->refresh_data_length, - rms->refresh_data, &melt_cb, rms); - rms->double_melt = GNUNET_NO; - return; - } - TALER_TESTING_interpreter_next (rms->is); -} - - -/** - * Run the command. - * - * @param cls closure. - * @param cmd the command to execute. - * @param is the interpreter state. - */ -static void -refresh_melt_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct RefreshMeltState *rms = cls; - unsigned int num_fresh_coins; - const char *default_melt_fresh_amounts[] = { - "EUR:1", "EUR:1", "EUR:1", "EUR:0.1", - NULL - }; - const char **melt_fresh_amounts; - - if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts)) - melt_fresh_amounts = default_melt_fresh_amounts; - rms->is = is; - rms->noreveal_index = UINT16_MAX; - for (num_fresh_coins = 0; - NULL != melt_fresh_amounts[num_fresh_coins]; - num_fresh_coins++) - ; - rms->num_fresh_coins = num_fresh_coins; - rms->fresh_pks = GNUNET_new_array - (num_fresh_coins, - struct TALER_EXCHANGE_DenomPublicKey); - { - struct TALER_Amount melt_amount; - struct TALER_Amount fresh_amount; - const struct TALER_DenominationSignature *melt_sig; - const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; - const struct TALER_TESTING_Command *coin_command; - - if (NULL == (coin_command - = TALER_TESTING_interpreter_lookup_command - (is, rms->coin_reference))) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv - (coin_command, 0, &rms->melt_priv)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - - if (GNUNET_OK != - TALER_TESTING_get_trait_denom_sig (coin_command, - 0, - &melt_sig)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub - (coin_command, 0, &melt_denom_pub)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - /* Melt amount starts with the melt fee of the old coin; we'll add the - values and withdraw fees of the fresh coins next */ - melt_amount = melt_denom_pub->fee_refresh; - for (unsigned int i = 0; i<num_fresh_coins; i++) - { - const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; - - if (GNUNET_OK != TALER_string_to_amount - (melt_fresh_amounts[i], &fresh_amount)) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at index %u\n", - melt_fresh_amounts[i], i); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - fresh_pk = TALER_TESTING_find_pk - (TALER_EXCHANGE_get_keys (is->exchange), &fresh_amount); - if (NULL == fresh_pk) - { - GNUNET_break (0); - /* Subroutine logs specific error */ - TALER_TESTING_interpreter_fail (rms->is); - return; - } - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&melt_amount, - &melt_amount, - &fresh_amount)); - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&melt_amount, - &melt_amount, - &fresh_pk->fee_withdraw)); - rms->fresh_pks[i] = *fresh_pk; - /* Make a deep copy of the RSA key */ - rms->fresh_pks[i].key.rsa_public_key - = GNUNET_CRYPTO_rsa_public_key_dup (fresh_pk->key.rsa_public_key); - } - rms->refresh_data - = TALER_EXCHANGE_refresh_prepare (rms->melt_priv, - &melt_amount, - melt_sig, - melt_denom_pub, - num_fresh_coins, - rms->fresh_pks, - &rms->refresh_data_length); - - if (NULL == rms->refresh_data) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - rms->rmh = TALER_EXCHANGE_refresh_melt (is->exchange, - rms->refresh_data_length, - rms->refresh_data, - &melt_cb, - rms); - - if (NULL == rms->rmh) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (rms->is); - return; - } - } -} - - -/** - * Free the "refresh melt" CMD state, and possibly cancel a - * pending operation thereof. - * - * @param cls closure, must be a `struct RefreshMeltState`. - * @param cmd the command which is being cleaned up. - */ -static void -refresh_melt_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct RefreshMeltState *rms = cls; - - if (NULL != rms->rmh) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", - rms->is->ip, rms->is->commands[rms->is->ip].label); - TALER_EXCHANGE_refresh_melt_cancel (rms->rmh); - rms->rmh = NULL; - } - if (NULL != rms->retry_task) - { - GNUNET_SCHEDULER_cancel (rms->retry_task); - rms->retry_task = NULL; - } - if (NULL != rms->fresh_pks) - { - for (unsigned int i = 0; i < rms->num_fresh_coins; i++) - GNUNET_CRYPTO_rsa_public_key_free (rms->fresh_pks[i].key.rsa_public_key); - } - GNUNET_free_non_null (rms->fresh_pks); - rms->fresh_pks = NULL; - GNUNET_free_non_null (rms->refresh_data); - rms->refresh_data = NULL; - rms->refresh_data_length = 0; - GNUNET_free_non_null (rms->melt_fresh_amounts); - GNUNET_free (rms); -} - - -/** - * Offer internal data to the "refresh melt" CMD. - * - * @param cls closure. - * @param[out] ret result (could be anything). - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -refresh_melt_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct RefreshMeltState *rms = cls; - - if (index >= rms->num_fresh_coins) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_denom_pub (index, &rms->fresh_pks[index]), - TALER_TESTING_make_trait_coin_priv (0, rms->melt_priv), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); - } -} - - -/** - * Parse list of amounts for melt operation. - * - * @param[in,out] rms where to store the list - * @param ap NULL-termianted list of amounts to be melted (one per fresh coin) - * @return #GNUNET_OK on success - */ -static int -parse_amounts (struct RefreshMeltState *rms, - va_list ap) -{ - unsigned int len; - unsigned int off; - const char *amount; - - len = 0; - off = 0; - while (NULL != (amount = va_arg (ap, const char *))) - { - if (len == off) - { - struct TALER_Amount a; - - GNUNET_array_grow (rms->melt_fresh_amounts, - len, - off + 16); - if (GNUNET_OK != - TALER_string_to_amount (amount, &a)) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at index %u\n", - amount, off); - GNUNET_free (rms->melt_fresh_amounts); - rms->melt_fresh_amounts = NULL; - return GNUNET_SYSERR; - } - rms->melt_fresh_amounts[off++] = amount; - } - } - if (0 == off) - return GNUNET_OK; /* no amounts given == use defaults! */ - /* ensure NULL-termination */ - GNUNET_array_grow (rms->melt_fresh_amounts, - len, - off + 1); - return GNUNET_OK; -} - - -/** - * Create a "refresh melt" command. - * - * @param label command label. - * @param coin_reference reference to a command - * that will provide a coin to refresh. - * @param expected_response_code expected HTTP code. - * @param ... NULL-terminated list of amounts to be melted - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_melt (const char *label, - const char *coin_reference, - unsigned int expected_response_code, - ...) -{ - struct RefreshMeltState *rms; - va_list ap; - - rms = GNUNET_new (struct RefreshMeltState); - rms->coin_reference = coin_reference; - rms->expected_response_code = expected_response_code; - va_start (ap, expected_response_code); - GNUNET_assert (GNUNET_OK == - parse_amounts (rms, ap)); - va_end (ap); - { - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = rms, - .run = &refresh_melt_run, - .cleanup = &refresh_melt_cleanup, - .traits = &refresh_melt_traits - }; - - return cmd; - } -} - - -/** - * Create a "refresh melt" CMD that does TWO /refresh/melt - * requests. This was needed to test the replay of a valid melt - * request, see #5312. - * - * @param label command label - * @param coin_reference reference to a command that will provide - * a coin to refresh - * @param expected_response_code expected HTTP code - * @param ... NULL-terminated list of amounts to be melted - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_melt_double (const char *label, - const char *coin_reference, - unsigned int expected_response_code, - ...) -{ - struct RefreshMeltState *rms; - va_list ap; - - rms = GNUNET_new (struct RefreshMeltState); - rms->coin_reference = coin_reference; - rms->expected_response_code = expected_response_code; - rms->double_melt = GNUNET_YES; - va_start (ap, expected_response_code); - GNUNET_assert (GNUNET_OK == - parse_amounts (rms, ap)); - va_end (ap); - { - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = rms, - .run = &refresh_melt_run, - .cleanup = &refresh_melt_cleanup, - .traits = &refresh_melt_traits - }; - - return cmd; - } -} - - -/** - * Modify a "refresh melt" command to enable retries. - * - * @param cmd command - * @return modified command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd) -{ - struct RefreshMeltState *rms; - - GNUNET_assert (&refresh_melt_run == cmd.run); - rms = cmd.cls; - rms->do_retry = GNUNET_YES; - return cmd; -} - - -/** - * Offer internal data from a "refresh reveal" CMD. - * - * @param cls closure. - * @param[out] ret result (could be anything). - * @param trait name of the trait. - * @param index index number of the object to offer. - * @return #GNUNET_OK on success. - */ -static int -refresh_reveal_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct RefreshRevealState *rrs = cls; - unsigned int num_coins = rrs->num_fresh_coins; -#define NUM_TRAITS ((num_coins * 4) + 3) - struct TALER_TESTING_Trait traits[NUM_TRAITS]; - - /* Making coin privs traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[i] = TALER_TESTING_make_trait_coin_priv - (i, &rrs->fresh_coins[i].coin_priv); - - /* Making denom pubs traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[num_coins + i] - = TALER_TESTING_make_trait_denom_pub - (i, rrs->fresh_coins[i].pk); - - /* Making denom sigs traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[(num_coins * 2) + i] - = TALER_TESTING_make_trait_denom_sig - (i, &rrs->fresh_coins[i].sig); - /* blinding key traits */ - for (unsigned int i = 0; i<num_coins; i++) - traits[(num_coins * 3) + i] - = TALER_TESTING_make_trait_blinding_key (i, - &rrs->fresh_coins[i].blinding_key), - - /* number of fresh coins */ - traits[(num_coins * 4)] = TALER_TESTING_make_trait_uint - (0, &rrs->num_fresh_coins); - - /* whole array of fresh coins */ - traits[(num_coins * 4) + 1] - = TALER_TESTING_make_trait_fresh_coins (0, rrs->fresh_coins), - - /* end of traits */ - traits[(num_coins * 4) + 2] = TALER_TESTING_trait_end (); - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Create a "refresh reveal" command. - * - * @param label command label. - * @param melt_reference reference to a "refresh melt" command. - * @param expected_response_code expected HTTP response code. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_reveal (const char *label, - const char *melt_reference, - unsigned int expected_response_code) -{ - struct RefreshRevealState *rrs; - - rrs = GNUNET_new (struct RefreshRevealState); - rrs->melt_reference = melt_reference; - rrs->expected_response_code = expected_response_code; - { - struct TALER_TESTING_Command cmd = { - .cls = rrs, - .label = label, - .run = &refresh_reveal_run, - .cleanup = &refresh_reveal_cleanup, - .traits = &refresh_reveal_traits - }; - - return cmd; - } -} - - -/** - * Modify a "refresh reveal" command to enable retries. - * - * @param cmd command - * @return modified command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_reveal_with_retry (struct TALER_TESTING_Command cmd) -{ - struct RefreshRevealState *rrs; - - GNUNET_assert (&refresh_reveal_run == cmd.run); - rrs = cmd.cls; - rrs->do_retry = GNUNET_YES; - return cmd; -} - - -/** - * Create a "refresh link" command. - * - * @param label command label. - * @param reveal_reference reference to a "refresh reveal" CMD. - * @param expected_response_code expected HTTP response code - * @return the "refresh link" command - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_link (const char *label, - const char *reveal_reference, - unsigned int expected_response_code) -{ - struct RefreshLinkState *rrs; - - rrs = GNUNET_new (struct RefreshLinkState); - rrs->reveal_reference = reveal_reference; - rrs->expected_response_code = expected_response_code; - { - struct TALER_TESTING_Command cmd = { - .cls = rrs, - .label = label, - .run = &refresh_link_run, - .cleanup = &refresh_link_cleanup - }; - - return cmd; - } -} - - -/** - * Modify a "refresh link" command to enable retries. - * - * @param cmd command - * @return modified command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refresh_link_with_retry (struct TALER_TESTING_Command cmd) -{ - struct RefreshLinkState *rls; - - GNUNET_assert (&refresh_link_run == cmd.run); - rls = cmd.cls; - rls->do_retry = GNUNET_YES; - return cmd; -} |