modify /payback API and test usage of that API to pass/return additional information required when dealing with payback of refreshed coins

This commit is contained in:
Christian Grothoff 2019-07-23 21:56:21 +02:00
parent 7d43ad56d3
commit e8a892c29a
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
10 changed files with 225 additions and 107 deletions

View File

@ -1651,7 +1651,8 @@ struct TALER_EXCHANGE_PaybackHandle;
* @param amount amount the exchange will wire back for this coin,
* on error the total balance remaining, or NULL
* @param timestamp what time did the exchange receive the /payback request
* @param reserve_pub public key of the reserve receiving the payback
* @param reserve_pub public key of the reserve receiving the payback, NULL if refreshed or on error
* @param old_coin_pub public key of the dirty coin, NULL if not refreshed or on error
* @param full_response full response from the exchange (for logging, in case of errors)
*/
typedef void
@ -1661,6 +1662,7 @@ typedef void
const struct TALER_Amount *amount,
struct GNUNET_TIME_Absolute timestamp,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const json_t *full_response);
@ -1673,6 +1675,7 @@ typedef void
* @param pk kind of coin to pay back
* @param denom_sig signature over the coin by the exchange using @a pk
* @param ps secret internals of the original planchet
* @param was_refreshed #GNUNET_YES if the coin in @a ps was refreshed
* @param payback_cb the callback to call when the final result for this request is available
* @param payback_cb_cls closure for @a payback_cb
* @return NULL
@ -1684,6 +1687,7 @@ TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_DenominationSignature *denom_sig,
const struct TALER_PlanchetSecretsP *ps,
int was_refreshed,
TALER_EXCHANGE_PaybackResultCallback payback_cb,
void *payback_cb_cls);

View File

@ -1388,14 +1388,15 @@ TALER_TESTING_cmd_refund (const char *label,
* the index of the coin using "$LABEL#$INDEX" syntax.
* Here, $INDEX must be a non-negative number.
* @param amount denomination to pay back.
*
* @param NULL if coin was not refreshed, otherwise label of the melt operation
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_payback (const char *label,
unsigned int expected_response_code,
const char *coin_reference,
const char *amount);
const char *amount,
const char *melt_reference);
/**
@ -1406,7 +1407,6 @@ TALER_TESTING_cmd_payback (const char *label,
* @param coin_reference reference to a CMD that will offer the
* denomination to revoke.
* @param config_filename configuration file name.
*
* @return the command.
*/
struct TALER_TESTING_Command

View File

@ -172,7 +172,7 @@ auditor_cb (void *cls,
static int
verify_deposit_signature_ok (struct TALER_EXCHANGE_DepositHandle *dh,
const json_t *json,
struct TALER_ExchangeSignatureP *exchange_sig,
struct TALER_ExchangeSignatureP *exchange_sig,
struct TALER_ExchangePublicKeyP *exchange_pub)
{
const struct TALER_EXCHANGE_Keys *key_state;

View File

@ -79,6 +79,11 @@ struct TALER_EXCHANGE_PaybackHandle
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* #GNUNET_YES if the coin was refreshed
*/
int was_refreshed;
};
@ -97,12 +102,13 @@ verify_payback_signature_ok (const struct TALER_EXCHANGE_PaybackHandle *ph,
const json_t *json)
{
struct TALER_PaybackConfirmationPS pc;
struct TALER_PaybackRefreshConfirmationPS pr;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_Amount amount;
struct GNUNET_TIME_Absolute timestamp;
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification spec[] = {
struct GNUNET_JSON_Specification spec_withdraw[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", &exchange_pub),
TALER_JSON_spec_amount ("amount", &amount),
@ -110,10 +116,18 @@ verify_payback_signature_ok (const struct TALER_EXCHANGE_PaybackHandle *ph,
GNUNET_JSON_spec_fixed_auto ("reserve_pub", &pc.reserve_pub),
GNUNET_JSON_spec_end()
};
struct GNUNET_JSON_Specification spec_refresh[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", &exchange_pub),
TALER_JSON_spec_amount ("amount", &amount),
GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
GNUNET_JSON_spec_fixed_auto ("old_coin_pub", &pr.old_coin_pub),
GNUNET_JSON_spec_end()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
ph->was_refreshed ? spec_refresh : spec_withdraw,
NULL, NULL))
{
GNUNET_break_op (0);
@ -122,32 +136,54 @@ verify_payback_signature_ok (const struct TALER_EXCHANGE_PaybackHandle *ph,
key_state = TALER_EXCHANGE_get_keys (ph->exchange);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state,
&exchange_pub))
&exchange_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
pc.purpose.size = htonl (sizeof (pc));
pc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
TALER_amount_hton (&pc.payback_amount,
&amount);
pc.coin_pub = ph->coin_pub;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
&pc.purpose,
&exchange_sig.eddsa_signature,
&exchange_pub.eddsa_pub))
if (ph->was_refreshed)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
pr.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK_REFRESH);
pr.purpose.size = htonl (sizeof (pr));
pr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
TALER_amount_hton (&pr.payback_amount,
&amount);
pr.coin_pub = ph->coin_pub;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK_REFRESH,
&pr.purpose,
&exchange_sig.eddsa_signature,
&exchange_pub.eddsa_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
else
{
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
pc.purpose.size = htonl (sizeof (pc));
pc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
TALER_amount_hton (&pc.payback_amount,
&amount);
pc.coin_pub = ph->coin_pub;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
&pc.purpose,
&exchange_sig.eddsa_signature,
&exchange_pub.eddsa_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
ph->cb (ph->cb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
TALER_EC_NONE,
&amount,
timestamp,
&pc.reserve_pub,
ph->was_refreshed ? NULL : &pc.reserve_pub,
ph->was_refreshed ? &pr.old_coin_pub : NULL,
json);
return GNUNET_OK;
}
@ -204,16 +240,17 @@ handle_payback_finished (void *cls,
history,
&total))
{
GNUNET_break_op (0);
response_code = 0;
GNUNET_break_op (0);
response_code = 0;
}
ph->cb (ph->cb_cls,
response_code,
TALER_JSON_get_error_code (j),
&total,
GNUNET_TIME_UNIT_FOREVER_ABS,
NULL,
j);
response_code,
TALER_JSON_get_error_code (j),
&total,
GNUNET_TIME_UNIT_FOREVER_ABS,
NULL,
NULL,
j);
TALER_EXCHANGE_payback_cancel (ph);
return;
}
@ -245,10 +282,11 @@ handle_payback_finished (void *cls,
}
ph->cb (ph->cb_cls,
response_code,
TALER_JSON_get_error_code (j),
TALER_JSON_get_error_code (j),
NULL,
GNUNET_TIME_UNIT_FOREVER_ABS,
NULL,
NULL,
j);
TALER_EXCHANGE_payback_cancel (ph);
}
@ -263,6 +301,7 @@ handle_payback_finished (void *cls,
* @param pk kind of coin to pay back
* @param denom_sig signature over the coin by the exchange using @a pk
* @param ps secret internals of the original planchet
* @param was_refreshed #GNUNET_YES if the coin in @a ps was refreshed
* @param payback_cb the callback to call when the final result for this request is available
* @param payback_cb_cls closure for @a payback_cb
* @return NULL
@ -274,6 +313,7 @@ TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_DenominationSignature *denom_sig,
const struct TALER_PlanchetSecretsP *ps,
int was_refreshed,
TALER_EXCHANGE_PaybackResultCallback payback_cb,
void *payback_cb_cls)
{
@ -302,12 +342,13 @@ TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange,
payback_obj = json_pack ("{s:o, s:o," /* denom pub/sig */
" s:o, s:o," /* coin pub/sig */
" s:o}", /* coin_bks */
" s:o, s:o}", /* coin_bks */
"denom_pub_hash", GNUNET_JSON_from_data_auto (&h_denom_pub),
"denom_sig", GNUNET_JSON_from_rsa_signature (denom_sig->rsa_signature),
"coin_pub", GNUNET_JSON_from_data_auto (&pr.coin_pub),
"coin_sig", GNUNET_JSON_from_data_auto (&coin_sig),
"coin_blind_key_secret", GNUNET_JSON_from_data_auto (&ps->blinding_key)
"coin_blind_key_secret", GNUNET_JSON_from_data_auto (&ps->blinding_key),
"refreshed", json_boolean (was_refreshed)
);
if (NULL == payback_obj)
{
@ -323,6 +364,7 @@ TALER_EXCHANGE_payback (struct TALER_EXCHANGE_Handle *exchange,
ph->cb = payback_cb;
ph->cb_cls = payback_cb_cls;
ph->url = TEAH_path_to_url (exchange, "/payback");
ph->was_refreshed = was_refreshed;
eh = TEL_curl_easy_get (ph->url);
if (GNUNET_OK !=
TALER_curl_easy_post (&ph->ctx,

View File

@ -474,7 +474,8 @@ run (void *cls,
TALER_TESTING_cmd_payback ("payback-1",
MHD_HTTP_OK,
"payback-withdraw-coin-1",
"EUR:5"),
"EUR:5",
NULL),
/**
* Re-withdraw from this reserve
*/
@ -536,7 +537,8 @@ run (void *cls,
TALER_TESTING_cmd_payback ("payback-2",
MHD_HTTP_OK,
"payback-withdraw-coin-2a",
"EUR:0.5"),
"EUR:0.5",
NULL),
TALER_TESTING_cmd_end ()
};

View File

@ -688,7 +688,8 @@ run (void *cls,
TALER_TESTING_cmd_payback ("payback-1",
MHD_HTTP_OK,
"payback-withdraw-coin-1",
"EUR:5"),
"EUR:5",
NULL),
/* Check the money is back with the reserve */
TALER_TESTING_cmd_status ("payback-reserve-status-1",
@ -799,11 +800,13 @@ run (void *cls,
TALER_TESTING_cmd_payback ("payback-2", MHD_HTTP_OK,
"payback-withdraw-coin-2a",
"EUR:0.5"),
"EUR:0.5",
NULL),
TALER_TESTING_cmd_payback ("payback-2b", MHD_HTTP_FORBIDDEN,
"payback-withdraw-coin-2a",
"EUR:0.5"),
"EUR:0.5",
NULL),
TALER_TESTING_cmd_deposit
("payback-deposit-revoked",

View File

@ -162,7 +162,8 @@ run (void *cls,
TALER_TESTING_cmd_payback ("payback-1",
MHD_HTTP_OK,
"refresh-reveal-1",
"EUR:5"),
"EUR:5",
"refresh-melt-1"),
/**
* Melt original coin AGAIN
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
@ -189,12 +190,14 @@ run (void *cls,
TALER_TESTING_cmd_payback ("payback-2",
MHD_HTTP_OK,
"refresh-melt-2",
"EUR:5"),
"EUR:5",
"refresh-melt-2"),
/* Refund original coin to reserve */
TALER_TESTING_cmd_payback ("payback-3",
MHD_HTTP_OK,
"withdraw-coin-1",
"EUR:5"),
"EUR:5",
NULL),
/* Check the money is back with the reserve */
TALER_TESTING_cmd_status ("payback-reserve-status-1",
"create-reserve-1",

View File

@ -449,8 +449,8 @@ deposit_traits (void *cls,
if (GNUNET_OK !=
TALER_TESTING_get_trait_coin_priv (coin_cmd,
ds->coin_index,
&coin_spent_priv))
ds->coin_index,
&coin_spent_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
@ -461,26 +461,26 @@ deposit_traits (void *cls,
/* First two traits are only available if
ds->traits is #GNUNET_YES */
TALER_TESTING_make_trait_exchange_pub (0,
&ds->exchange_pub),
&ds->exchange_pub),
TALER_TESTING_make_trait_exchange_sig (0,
&ds->exchange_sig),
&ds->exchange_sig),
/* These traits are always available */
TALER_TESTING_make_trait_coin_priv (0,
coin_spent_priv),
coin_spent_priv),
TALER_TESTING_make_trait_wire_details (0,
ds->wire_details),
ds->wire_details),
TALER_TESTING_make_trait_contract_terms (0,
ds->contract_terms),
ds->contract_terms),
TALER_TESTING_make_trait_peer_key (0,
&ds->merchant_priv.eddsa_priv),
&ds->merchant_priv.eddsa_priv),
TALER_TESTING_make_trait_amount (0,
ds->amount),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait ((ds->traits_ready)
? traits
: &traits[2],
? traits
: &traits[2],
ret,
trait,
index);

View File

@ -98,6 +98,13 @@ struct PaybackState
* Handle to the ongoing operation.
*/
struct TALER_EXCHANGE_PaybackHandle *ph;
/**
* NULL if coin was not refreshed, otherwise reference
* to the melt operation underlying @a coin_reference.
*/
const char *melt_reference;
};
/**
@ -111,8 +118,8 @@ struct PaybackState
* @param amount amount the exchange will wire back for this coin.
* @param timestamp what time did the exchange receive the
* /payback request
* @param reserve_pub public key of the reserve affected by the
* payback.
* @param reserve_pub public key of the reserve receiving the payback, NULL if refreshed or on error
* @param old_coin_pub public key of the dirty coin, NULL if not refreshed or on error
* @param full_response raw response from the exchange.
*/
static void
@ -122,15 +129,13 @@ payback_cb (void *cls,
const struct TALER_Amount *amount,
struct GNUNET_TIME_Absolute timestamp,
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const json_t *full_response)
{
struct PaybackState *ps = cls;
struct TALER_TESTING_Interpreter *is = ps->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
const struct TALER_TESTING_Command *reserve_cmd;
const struct TALER_ReservePrivateKeyP *reserve_priv;
struct TALER_ReservePublicKeyP rp;
struct TALER_Amount expected_amount;
char *cref;
const char *index;
unsigned int idx;
@ -150,7 +155,7 @@ payback_cb (void *cls,
return;
}
/* We allow command referneces of the form "$LABEL#$INDEX" or
/* We allow command references of the form "$LABEL#$INDEX" or
just "$LABEL", which implies the index is 0. Figure out
which one it is. */
index = strchr (ps->coin_reference, '#');
@ -189,41 +194,90 @@ payback_cb (void *cls,
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
(reserve_cmd, idx, &reserve_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&rp.eddsa_pub);
switch (http_status)
{
case MHD_HTTP_OK:
if (GNUNET_OK != TALER_string_to_amount
(ps->amount, &expected_amount))
/* first, check amount */
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
struct TALER_Amount expected_amount;
if (GNUNET_OK !=
TALER_string_to_amount (ps->amount, &expected_amount))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != TALER_amount_cmp (amount, &expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
cmd->label);
json_dumpf (full_response, stderr, 0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
if (0 != TALER_amount_cmp (amount, &expected_amount))
/* now, check old_coin_pub or reserve_pub, respectively */
if (NULL != ps->melt_reference)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
cmd->label);
json_dumpf (full_response, stderr, 0);
TALER_TESTING_interpreter_fail (is);
return;
const struct TALER_TESTING_Command *melt_cmd;
const struct TALER_CoinSpendPrivateKeyP *dirty_priv;
struct TALER_CoinSpendPublicKeyP oc;
melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
ps->melt_reference);
if (NULL == melt_cmd)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_coin_priv (melt_cmd,
0,
&dirty_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv,
&oc.eddsa_pub);
if (0 != GNUNET_memcmp (&oc,
old_coin_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
if (0 != GNUNET_memcmp (reserve_pub, &rp))
else
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
const struct TALER_ReservePrivateKeyP *reserve_priv;
struct TALER_ReservePublicKeyP rp;
if (NULL == reserve_priv)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
(reserve_cmd, idx, &reserve_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&rp.eddsa_pub);
if (0 != GNUNET_memcmp (reserve_pub, &rp))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
break;
default:
@ -309,6 +363,7 @@ payback_run (void *cls,
denom_pub,
coin_sig,
&planchet,
NULL != ps->melt_reference,
payback_cb,
ps);
GNUNET_assert (NULL != ps->ph);
@ -465,14 +520,15 @@ revoke_run (void *cls,
* @param coin_reference reference to any command which
* offers a coin & reserve private key.
* @param amount denomination to pay back.
*
* @param melt_reference NULL if coin was not refreshed
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_payback (const char *label,
unsigned int expected_response_code,
const char *coin_reference,
const char *amount)
const char *amount,
const char *melt_reference)
{
struct PaybackState *ps;
@ -480,15 +536,17 @@ TALER_TESTING_cmd_payback (const char *label,
ps->expected_response_code = expected_response_code;
ps->coin_reference = coin_reference;
ps->amount = amount;
ps->melt_reference = melt_reference;
{
struct TALER_TESTING_Command cmd = {
.cls = ps,
.label = label,
.run = &payback_run,
.cleanup = &payback_cleanup
};
struct TALER_TESTING_Command cmd = {
.cls = ps,
.label = label,
.run = &payback_run,
.cleanup = &payback_cleanup
};
return cmd;
return cmd;
}
}
@ -516,14 +574,15 @@ TALER_TESTING_cmd_revoke (const char *label,
rs->expected_response_code = expected_response_code;
rs->coin_reference = coin_reference;
rs->config_filename = config_filename;
{
struct TALER_TESTING_Command cmd = {
.cls = rs,
.label = label,
.run = &revoke_run,
.cleanup = &revoke_cleanup,
.traits = &revoke_traits
};
struct TALER_TESTING_Command cmd = {
.cls = rs,
.label = label,
.run = &revoke_run,
.cleanup = &revoke_cleanup,
.traits = &revoke_traits
};
return cmd;
return cmd;
}
}

View File

@ -87,6 +87,11 @@ struct RefreshMeltState
*/
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.
*/
@ -889,7 +894,6 @@ refresh_melt_run (void *cls,
(num_fresh_coins,
struct TALER_EXCHANGE_DenomPublicKey);
{
const struct TALER_CoinSpendPrivateKeyP *melt_priv;
struct TALER_Amount melt_amount;
struct TALER_Amount fresh_amount;
const struct TALER_DenominationSignature *melt_sig;
@ -907,7 +911,7 @@ refresh_melt_run (void *cls,
}
if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
(coin_command, 0, &melt_priv))
(coin_command, 0, &rms->melt_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (rms->is);
@ -969,7 +973,7 @@ refresh_melt_run (void *cls,
rms->fresh_pks[i] = *fresh_pk;
}
rms->refresh_data = TALER_EXCHANGE_refresh_prepare
(melt_priv, &melt_amount, melt_sig, melt_denom_pub,
(rms->melt_priv, &melt_amount, melt_sig, melt_denom_pub,
GNUNET_YES, num_fresh_coins, rms->fresh_pks,
&rms->refresh_data_length);
@ -1053,6 +1057,7 @@ refresh_melt_traits (void *cls,
{
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 ()
};