getting revocation/payback test with refreshed coins to pass

This commit is contained in:
Christian Grothoff 2019-07-24 11:57:03 +02:00
parent 80ff186648
commit 9e3f4bdd79
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
13 changed files with 186 additions and 120 deletions

View File

@ -305,6 +305,7 @@ payback_transaction (void *cls,
}
return qs;
}
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (pc->value.currency,
&spent));

View File

@ -556,7 +556,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
GNUNET_break_op (0);
return TEH_RESPONSE_reply_arg_invalid (connection,
TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
"new_denoms");
"new_denoms_h");
}
if (json_array_size (new_denoms_h_json) !=
@ -640,7 +640,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
TEH_KS_release (key_state);
return TEH_RESPONSE_reply_arg_invalid (connection,
TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND,
"new_denoms");
"new_denoms_h");
}
GNUNET_assert (NULL != dkis[i]->denom_priv.rsa_private_key);
}
@ -674,7 +674,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
/* lookup old_coin_pub in database */
{
enum GNUNET_DB_QueryStatus qs;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
(qs = TEH_plugin->get_melt (TEH_plugin->cls,
NULL,
@ -725,7 +725,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
ldp.purpose.size = htonl (sizeof (ldp));
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
ldp.h_denom_pub = dki_h[i];
ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
ldp.transfer_pub = rctx->gamma_tp;
GNUNET_CRYPTO_hash (rcds[i].coin_ev,
rcds[i].coin_ev_size,
@ -744,7 +744,7 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,
}
}
}
rctx->num_fresh_coins = num_fresh_coins;
rctx->rcds = rcds;
rctx->dkis = dkis;

View File

@ -286,7 +286,7 @@ withdraw_transaction (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
*mhd_ret = reply_reserve_withdraw_insufficient_funds (connection,
rh);
rh);
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rh);
return GNUNET_DB_STATUS_HARD_ERROR;
@ -319,8 +319,8 @@ withdraw_transaction (void *cls,
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
wc->collectable.reserve_sig = wc->signature;
qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
session,
&wc->collectable);
session,
&wc->collectable);
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);

View File

@ -2593,8 +2593,8 @@ postgres_insert_withdraw_info (void *cls,
now = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&now);
qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_withdraw_info",
params);
"insert_withdraw_info",
params);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
@ -2627,6 +2627,9 @@ postgres_insert_withdraw_info (void *cls,
TALER_B2S (&collectable->reserve_pub));
return GNUNET_DB_STATUS_SOFT_ERROR;
}
/* FIXME: idle_reserve_expiration_time is not a good value here,
we should base this on the LEGAL expiration time of coins
as we need reserve data for payback! */
expiry = GNUNET_TIME_absolute_add (now,
pg->idle_reserve_expiration_time);
reserve.expiry = GNUNET_TIME_absolute_max (expiry,
@ -7721,11 +7724,12 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
return NULL;
}
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (cfg,
"exchangedb",
"IDLE_RESERVE_EXPIRATION_TIME",
&pg->idle_reserve_expiration_time))
"exchangedb",
"IDLE_RESERVE_EXPIRATION_TIME",
&pg->idle_reserve_expiration_time))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchangedb",

View File

@ -511,6 +511,7 @@ TALER_EXCHANGE_get_signing_key_details (const struct TALER_EXCHANGE_Keys *keys,
const char *
TALER_EXCHANGE_get_base_url (const struct TALER_EXCHANGE_Handle *exchange);
/**
* Obtain the denomination key details from the exchange.
*

View File

@ -131,6 +131,7 @@ TALER_TESTING_cert_cb
const struct TALER_EXCHANGE_Keys *keys,
enum TALER_EXCHANGE_VersionCompatibility compat);
/**
* Wait for the exchange to have started. Waits for at
* most 10s, after that returns 77 to indicate an error.
@ -1108,19 +1109,18 @@ TALER_TESTING_cmd_deposit_with_retry
* Create a "refresh melt" command.
*
* @param label command label.
* @param amount amount to be melted.
* @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 *amount,
const char *coin_reference,
unsigned int expected_response_code);
unsigned int expected_response_code,
...);
/**
@ -1129,19 +1129,18 @@ TALER_TESTING_cmd_refresh_melt
* request, see #5312.
*
* @param label command label
* @param amount FIXME not used.
* @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 *amount,
const char *coin_reference,
unsigned int expected_response_code);
unsigned int expected_response_code,
...);
/**

View File

@ -192,8 +192,8 @@ TESTS = \
$(check_PROGRAMS)
# expected to fail for now: test incomplete, feature not implemented!
XFAIL_TESTS = \
test_exchange_api_revocation
#XFAIL_TESTS = \
# test_exchange_api_revocation
test_exchange_api_SOURCES = \
test_exchange_api.c

View File

@ -1518,11 +1518,11 @@ handle_refresh_reveal_finished (void *cls,
*/
struct TALER_EXCHANGE_RefreshRevealHandle *
TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
size_t refresh_data_length,
const char *refresh_data,
uint32_t noreveal_index,
TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
void *reveal_cb_cls)
size_t refresh_data_length,
const char *refresh_data,
uint32_t noreveal_index,
TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
void *reveal_cb_cls)
{
struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
json_t *transfer_privs;
@ -1535,15 +1535,6 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
struct MeltData *md;
struct TALER_TransferPublicKeyP transfer_pub;
GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange));
md = deserialize_melt_data (refresh_data,
refresh_data_length);
if (NULL == md)
{
GNUNET_break (0);
return NULL;
}
if (noreveal_index >= TALER_CNC_KAPPA)
{
/* We check this here, as it would be really bad to below just
@ -1553,6 +1544,19 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_break (0);
return NULL;
}
if (GNUNET_YES !=
TEAH_handle_is_ready (exchange))
{
GNUNET_break (0);
return NULL;
}
md = deserialize_melt_data (refresh_data,
refresh_data_length);
if (NULL == md)
{
GNUNET_break (0);
return NULL;
}
/* now transfer_pub */
GNUNET_CRYPTO_ecdhe_key_get_public (&md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv,

View File

@ -231,9 +231,9 @@ run (void *cls,
* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x
* EUR:0.13) */
TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
"EUR:4",
"refresh-withdraw-coin-1",
MHD_HTTP_OK),
MHD_HTTP_OK,
NULL),
/**
* Complete (successful) melt operation, and withdraw the coins
*/

View File

@ -309,8 +309,10 @@ run (void *cls,
* Melt the rest of the coin's value
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt_double
("refresh-melt-1", "EUR:4",
"refresh-withdraw-coin-1", MHD_HTTP_OK),
("refresh-melt-1",
"refresh-withdraw-coin-1",
MHD_HTTP_OK,
NULL),
/**
* Complete (successful) melt operation, and
* withdraw the coins
@ -360,8 +362,10 @@ run (void *cls,
/* Test running a failing melt operation (same operation
* again must fail) */
TALER_TESTING_cmd_refresh_melt
("refresh-melt-failing", "EUR:4",
"refresh-withdraw-coin-1", MHD_HTTP_FORBIDDEN),
("refresh-melt-failing",
"refresh-withdraw-coin-1",
MHD_HTTP_FORBIDDEN,
NULL),
/* FIXME: also test with coin that was already melted
* (signature differs from coin that was deposited...) */

View File

@ -142,11 +142,13 @@ run (void *cls,
\"value\":\"EUR:1\"}]}",
GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
/**
* Melt the rest of the coin's value
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt_double
("refresh-melt-1", "EUR:4",
"withdraw-coin-1", MHD_HTTP_OK),
* Melt SOME of the rest of the coin's value
* (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt
("refresh-melt-1",
"withdraw-coin-1",
MHD_HTTP_OK,
NULL),
/**
* Complete (successful) melt operation, and withdraw the coins
*/
@ -174,45 +176,49 @@ run (void *cls,
"refresh-reveal-1#2",
"EUR:1",
"refresh-melt-1"),
/* Melt original coin AGAIN (FIXME: this command
is simply WRONG as it neither matches
the EUR:3 that were paid back NOR is melt_double
precisely right here!) -- it always tries to MELT EUR:4, which is too much! */
TALER_TESTING_cmd_refresh_melt_double
("refresh-melt-2", "EUR:3",
"withdraw-coin-1", MHD_HTTP_OK),
/* Now we have EUR:3.83 EUR back after 3x EUR:1 in paybacks */
/* Melt original coin AGAIN, but only create one 0.1 EUR coin;
This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
leaving EUR:3.69. */
TALER_TESTING_cmd_refresh_melt
("refresh-melt-2",
"withdraw-coin-1",
MHD_HTTP_OK,
"EUR:0.1",
NULL),
/**
* Complete (successful) melt operation, and withdraw the coins
*/
TALER_TESTING_cmd_refresh_reveal
("refresh-reveal-2",
"refresh-melt-2", MHD_HTTP_OK),
/* Make refreshed coin invalid */
/* Revokes refreshed EUR:0.1 coin */
TALER_TESTING_cmd_revoke ("revoke-2",
MHD_HTTP_OK,
"refresh-reveal-2",
CONFIG_FILE),
/* Make also original coin invalid */
/* Revoke also original coin denomination */
TALER_TESTING_cmd_revoke ("revoke-3",
MHD_HTTP_OK,
"withdraw-coin-1",
CONFIG_FILE),
/* Refund coin to original coin */
/* Refund coin EUR:0.1 to original coin, creating zombie! */
TALER_TESTING_cmd_payback ("payback-2",
MHD_HTTP_OK,
"refresh-melt-2",
"EUR:1",
"refresh-reveal-2",
"EUR:0.1",
"refresh-melt-2"),
/* Refund original coin to reserve */
/* Due to payback, original coin is now at EUR:3.79 */
/* Refund original (now zombie) coin to reserve */
TALER_TESTING_cmd_payback ("payback-3",
MHD_HTTP_OK,
"withdraw-coin-1",
"EUR:1",
"EUR:3.79",
NULL),
/* Check the money is back with the reserve */
TALER_TESTING_cmd_status ("payback-reserve-status-1",
"create-reserve-1",
"EUR:1.0",
"EUR:3.79",
MHD_HTTP_OK),
TALER_TESTING_cmd_end ()
};

View File

@ -187,9 +187,9 @@ run (void *cls,
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt
("refresh-melt",
"EUR:4",
"refresh-withdraw-coin",
MHD_HTTP_OK),
MHD_HTTP_OK,
NULL),
/* Trigger 409 Conflict. */
TALER_TESTING_cmd_flip_upload

View File

@ -37,16 +37,6 @@
struct MeltDetails
{
/**
* Amount to melt (including fee).
*/
const char *amount;
/**
* Reference to reserve_withdraw operations for coin to
* be used for the /refresh/melt operation.
*/
const char *coin_reference;
};
@ -57,9 +47,10 @@ struct RefreshMeltState
{
/**
* Information about coins to be melted.
* Reference to reserve_withdraw operations for coin to
* be used for the /refresh/melt operation.
*/
struct MeltDetails melted_coin;
const char *coin_reference;
/**
* "Crypto data" used in the refresh operation.
@ -107,6 +98,11 @@ struct RefreshMeltState
*/
size_t refresh_data_length;
/**
* Amounts to be generated during melt.
*/
const char **melt_fresh_amounts;
/**
* Number of fresh coins generated by the melt.
*/
@ -314,12 +310,12 @@ reveal_cb (void *cls,
"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,
/* 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;
@ -691,12 +687,9 @@ refresh_link_run (void *cls,
/* find reserve_withdraw command */
{
const struct MeltDetails *md;
rms = melt_cmd->cls;
md = &rms->melted_coin;
coin_cmd = TALER_TESTING_interpreter_lookup_command
(rls->is, md->coin_reference);
(rls->is, rms->coin_reference);
if (NULL == coin_cmd)
{
GNUNET_break (0);
@ -879,11 +872,14 @@ refresh_melt_run (void *cls,
{
struct RefreshMeltState *rms = cls;
unsigned int num_fresh_coins;
/* FIXME: this should be dynamic */
const char *melt_fresh_amounts[] = {
const char *default_melt_fresh_amounts[] = {
"EUR:1", "EUR:1", "EUR:1", "EUR:0.1",
NULL};
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;
@ -899,11 +895,10 @@ refresh_melt_run (void *cls,
const struct TALER_DenominationSignature *melt_sig;
const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
const struct TALER_TESTING_Command *coin_command;
const struct MeltDetails *md = &rms->melted_coin;
if (NULL == (coin_command
= TALER_TESTING_interpreter_lookup_command
(is, md->coin_reference)))
(is, rms->coin_reference)))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
@ -918,18 +913,6 @@ refresh_melt_run (void *cls,
return;
}
if (GNUNET_OK !=
TALER_string_to_amount (md->amount,
&melt_amount))
{
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u\n",
md->amount,
is->ip);
TALER_TESTING_interpreter_fail (rms->is);
return;
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_denom_sig (coin_command,
0,
@ -946,6 +929,9 @@ refresh_melt_run (void *cls,
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;
@ -969,7 +955,14 @@ refresh_melt_run (void *cls,
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;
}
rms->refresh_data = TALER_EXCHANGE_refresh_prepare
@ -1028,6 +1021,7 @@ refresh_melt_cleanup (void *cls,
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);
}
@ -1069,32 +1063,83 @@ refresh_melt_traits (void *cls,
}
/**
* Parse list of amounts for melt operation.
*
* @param rms[in,out] 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 amount amount to be melted.
* @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 *amount,
const char *coin_reference,
unsigned int expected_response_code)
unsigned int expected_response_code,
...)
{
struct RefreshMeltState *rms;
struct MeltDetails md;
va_list ap;
md.coin_reference = coin_reference;
md.amount = amount;
rms = GNUNET_new (struct RefreshMeltState);
rms->melted_coin = md;
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,
@ -1116,28 +1161,30 @@ TALER_TESTING_cmd_refresh_melt
*
* @param label command label
* @param exchange connection to the exchange
* @param amount amount to be melted.
* @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 *amount,
const char *coin_reference,
unsigned int expected_response_code)
unsigned int expected_response_code,
...)
{
struct RefreshMeltState *rms;
struct MeltDetails md;
va_list ap;
md.coin_reference = coin_reference;
md.amount = amount;
rms = GNUNET_new (struct RefreshMeltState);
rms->melted_coin = md;
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,