-fix recoup ugliness

This commit is contained in:
Christian Grothoff 2021-12-16 20:18:44 +01:00
parent 3b6a0dd599
commit 1acc851deb
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
13 changed files with 248 additions and 160 deletions

View File

@ -1983,38 +1983,29 @@ check_recoup (struct CoinContext *cc,
cc->qs = qs;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_wallet_recoup_verify (&coin->denom_pub_hash,
coin_blind,
amount,
&coin->coin_pub,
coin_sig))
{
struct TALER_RecoupRequestPS pr = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.purpose.size = htonl (sizeof (pr)),
.coin_pub = coin->coin_pub,
.coin_blind = *coin_blind,
.h_denom_pub = coin->denom_pub_hash
};
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&pr,
&coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
operation),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount),
GNUNET_JSON_pack_data_auto ("coin_pub",
&coin->coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
operation),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount),
GNUNET_JSON_pack_data_auto ("coin_pub",
&coin->coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
ds = get_denomination_summary (cc,
issue,

View File

@ -684,7 +684,7 @@ handle_reserve_out (void *cls,
* @param coin_blind blinding factor used to blind the coin
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
static enum GNUNET_GenericReturnValue
handle_recoup_by_reserve (
void *cls,
uint64_t rowid,
@ -711,35 +711,26 @@ handle_recoup_by_reserve (
ppr.last_reserve_recoup_serial_id = rowid + 1;
/* We know that denom_pub matches denom_pub_hash because this
is how the SQL statement joined the tables. */
if (GNUNET_OK !=
TALER_wallet_recoup_verify (&coin->denom_pub_hash,
coin_blind,
amount,
&coin->coin_pub,
coin_sig))
{
struct TALER_RecoupRequestPS pr = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.purpose.size = htonl (sizeof (pr)),
.h_denom_pub = coin->denom_pub_hash,
.coin_pub = coin->coin_pub,
.coin_blind = *coin_blind
};
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&pr,
&coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"recoup"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount),
GNUNET_JSON_pack_data_auto ("key_pub",
&coin->coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount);
}
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"recoup"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount),
GNUNET_JSON_pack_data_auto ("key_pub",
&coin->coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount);
}
/* check that the coin was eligible for recoup!*/

View File

@ -87,6 +87,7 @@ struct RecoupContext
* Set by #recoup_transaction() to the amount that will be paid back
*/
struct TALER_Amount amount;
const struct TALER_Amount *requested_amount;
/**
* Set by #recoup_transaction to the timestamp when the recoup
@ -234,6 +235,15 @@ recoup_transaction (void *cls,
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
pc->now = GNUNET_TIME_timestamp_get ();
if (0 != TALER_amount_cmp (&pc->amount,
pc->requested_amount))
{
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_CONFLICT,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
TALER_amount2s (&pc->amount));
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* add coin to list of wire transfers for recoup */
if (pc->refreshed)
@ -284,6 +294,7 @@ recoup_transaction (void *cls,
* @param coin information about the coin
* @param coin_bks blinding data of the coin (to be checked)
* @param coin_sig signature of the coin
* @param requested_amount requested amount to be recouped
* @param refreshed true if the coin was refreshed
* @return MHD result code
*/
@ -293,6 +304,7 @@ verify_and_execute_recoup (
const struct TALER_CoinPublicInfo *coin,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *requested_amount,
bool refreshed)
{
struct RecoupContext pc;
@ -352,27 +364,18 @@ verify_and_execute_recoup (
}
/* check recoup request signature */
if (GNUNET_OK !=
TALER_wallet_recoup_verify (&coin->denom_pub_hash,
coin_bks,
requested_amount,
&coin->coin_pub,
coin_sig))
{
struct TALER_RecoupRequestPS pr = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.purpose.size = htonl (sizeof (pr)),
.coin_pub = coin->coin_pub,
.h_denom_pub = coin->denom_pub_hash,
.coin_blind = *coin_bks
};
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&pr,
&coin_sig->eddsa_signature,
&coin->coin_pub.eddsa_pub))
{
TALER_LOG_WARNING ("Invalid signature on recoup request\n");
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID,
NULL);
}
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID,
NULL);
}
{
@ -404,6 +407,7 @@ verify_and_execute_recoup (
pc.coin_bks = coin_bks;
pc.coin = coin;
pc.refreshed = refreshed;
pc.requested_amount = requested_amount;
{
MHD_RESULT mhd_ret = MHD_NO;
@ -552,6 +556,7 @@ TEH_handler_recoup (struct MHD_Connection *connection,
&coin,
&coin_bks,
&coin_sig,
&amount,
refreshed);
GNUNET_JSON_parse_free (spec);
return res;

View File

@ -697,8 +697,6 @@ CREATE INDEX IF NOT EXISTS revolving_work_shards_index
-- Stored procedures
DROP FUNCTION IF EXISTS exchange_do_withdraw(bigint,integer,bytea,bytea,bytea,bytea,bytea,bigint,bigint) ;
CREATE OR REPLACE FUNCTION exchange_do_withdraw(
IN amount_val INT8,
IN amount_frac INT4,
@ -857,9 +855,6 @@ COMMENT ON FUNCTION exchange_do_withdraw(INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA,
DROP FUNCTION IF EXISTS exchange_do_withdraw_limit_check(bigint,bigint,bigint,int) ;
CREATE OR REPLACE FUNCTION exchange_do_withdraw_limit_check(
IN ruuid INT8,
IN start_time INT8,

View File

@ -3246,8 +3246,8 @@ postgres_get_denomination_info (
rs);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
return qs;
issue->properties.purpose.size = htonl (sizeof (struct
TALER_DenominationKeyValidityPS));
issue->properties.purpose.size
= htonl (sizeof (struct TALER_DenominationKeyValidityPS));
issue->properties.purpose.purpose = htonl (
TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
issue->properties.denom_hash = *denom_pub_hash;

View File

@ -238,23 +238,26 @@ create_denom_key_pair (unsigned int size,
sizeof (struct TALER_EXCHANGEDB_DenominationKey));
dki.denom_pub = dkp->pub;
dki.issue.properties.start = GNUNET_TIME_timestamp_hton (now);
dki.issue.properties.expire_withdraw = GNUNET_TIME_timestamp_hton
(GNUNET_TIME_absolute_to_timestamp
(GNUNET_TIME_absolute_add (
now.abs_time,
GNUNET_TIME_UNIT_HOURS)));
dki.issue.properties.expire_deposit = GNUNET_TIME_timestamp_hton (
GNUNET_TIME_absolute_to_timestamp
(GNUNET_TIME_absolute_add
(now.abs_time,
GNUNET_TIME_relative_multiply (
GNUNET_TIME_UNIT_HOURS, 2))));
dki.issue.properties.expire_legal = GNUNET_TIME_timestamp_hton (
GNUNET_TIME_absolute_to_timestamp
(GNUNET_TIME_absolute_add
(now.abs_time,
GNUNET_TIME_relative_multiply (
GNUNET_TIME_UNIT_HOURS, 3))));
dki.issue.properties.expire_withdraw
= GNUNET_TIME_timestamp_hton
(GNUNET_TIME_absolute_to_timestamp
(GNUNET_TIME_absolute_add (
now.abs_time,
GNUNET_TIME_UNIT_HOURS)));
dki.issue.properties.expire_deposit
= GNUNET_TIME_timestamp_hton (
GNUNET_TIME_absolute_to_timestamp
(GNUNET_TIME_absolute_add
(now.abs_time,
GNUNET_TIME_relative_multiply (
GNUNET_TIME_UNIT_HOURS, 2))));
dki.issue.properties.expire_legal
= GNUNET_TIME_timestamp_hton (
GNUNET_TIME_absolute_to_timestamp
(GNUNET_TIME_absolute_add
(now.abs_time,
GNUNET_TIME_relative_multiply (
GNUNET_TIME_UNIT_HOURS, 3))));
TALER_amount_hton (&dki.issue.properties.value, value);
TALER_amount_hton (&dki.issue.properties.fee_withdraw, fee_withdraw);
TALER_amount_hton (&dki.issue.properties.fee_deposit, fee_deposit);
@ -276,6 +279,8 @@ create_denom_key_pair (unsigned int size,
destroy_denom_key_pair (dkp);
return NULL;
}
memset (&issue2, 0, sizeof (issue2));
plugin->commit (plugin->cls);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_denomination_info (plugin->cls,
&dki.issue.properties.denom_hash,

View File

@ -1738,6 +1738,63 @@ TALER_wallet_link_verify (
const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig);
/**
* Sign link data.
*
* @param h_denom_pub hash of the denomiantion public key of the new coin
* @param transfer_pub transfer public key
* @param coin_ev coin envelope
* @param coin_ev_size number of bytes in @a coin_ev
* @param old_coin_priv private key to sign with
* @param[out] coin_sig resulting signature
*/
void
TALER_wallet_link_sign (const struct TALER_DenominationHash *h_denom_pub,
const struct TALER_TransferPublicKeyP *transfer_pub,
const void *coin_ev,
size_t coin_ev_size,
const struct TALER_CoinSpendPrivateKeyP *old_coin_priv,
struct TALER_CoinSpendSignatureP *coin_sig);
/**
* Verify recoup signature.
*
* @param h_denom_pub hash of the denomiantion public key of the coin
* @param coin_bks blinding factor used when withdrawing the coin
* @param requested_amount amount that is left to be recouped
* @param coin_pub coin key of the coin to be recouped
* @param coin_sig resulting signature
* @return #GNUNET_OK if the signature is valid
*/
enum GNUNET_GenericReturnValue
TALER_wallet_recoup_verify (
const struct TALER_DenominationHash *h_denom_pub,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_Amount *requested_amount,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig);
/**
* Create recoup signature.
*
* @param h_denom_pub hash of the denomiantion public key of the coin
* @param coin_bks blinding factor used when withdrawing the coin
* @param requested_amount amount that is left to be recouped
* @param coin_priv coin key of the coin to be recouped
* @param coin_sig resulting signature
*/
void
TALER_wallet_recoup_sign (
const struct TALER_DenominationHash *h_denom_pub,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_Amount *requested_amount,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_CoinSpendSignatureP *coin_sig);
/* ********************* offline signing ************************** */

View File

@ -1496,11 +1496,6 @@ struct TALER_RecoupRequestPS
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Public key of the coin to be refunded.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Hash of the (revoked) denomination public key of the coin.
*/
@ -1510,6 +1505,12 @@ struct TALER_RecoupRequestPS
* Blinding factor that was used to withdraw the coin.
*/
union TALER_DenominationBlindingKeyP coin_blind;
/**
* How much of the coin's value will be recouped?
*/
struct TALER_AmountNBO recoup_amount;
};

View File

@ -688,17 +688,16 @@ TALER_EXCHANGE_verify_coin_history (
.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
.coin_pub = *coin_pub
};
struct TALER_RecoupRequestPS rr = {
.purpose.size = htonl (sizeof (pc)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.coin_pub = *coin_pub
};
union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_Amount recoup_amount;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_CoinSpendSignatureP coin_sig;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any_nbo ("amount",
&pc.recoup_amount),
TALER_JSON_spec_amount_any ("amount",
&recoup_amount),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
@ -708,9 +707,9 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin_sig),
GNUNET_JSON_spec_fixed_auto ("coin_blind",
&rr.coin_blind),
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&rr.h_denom_pub),
h_denom_pub),
GNUNET_JSON_spec_timestamp_nbo ("timestamp",
&pc.timestamp),
GNUNET_JSON_spec_end ()
@ -736,15 +735,15 @@ TALER_EXCHANGE_verify_coin_history (
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&rr,
&coin_sig.eddsa_signature,
&coin_pub->eddsa_pub))
TALER_wallet_recoup_verify (h_denom_pub,
&coin_bks,
&recoup_amount,
coin_pub,
&coin_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
*h_denom_pub = rr.h_denom_pub;
add = GNUNET_YES;
}
else if (0 == strcasecmp (type,
@ -758,17 +757,16 @@ TALER_EXCHANGE_verify_coin_history (
TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
.coin_pub = *coin_pub
};
struct TALER_RecoupRequestPS rr = {
.purpose.size = htonl (sizeof (pc)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.coin_pub = *coin_pub
};
union TALER_DenominationBlindingKeyP coin_bks;
struct TALER_Amount recoup_amount;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_CoinSpendSignatureP coin_sig;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any_nbo ("amount",
&pc.recoup_amount),
TALER_JSON_spec_amount_any ("amount",
&recoup_amount),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
@ -778,9 +776,9 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
&pc.old_coin_pub),
GNUNET_JSON_spec_fixed_auto ("coin_blind",
&rr.coin_blind),
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&rr.h_denom_pub),
h_denom_pub),
GNUNET_JSON_spec_timestamp_nbo ("timestamp",
&pc.timestamp),
GNUNET_JSON_spec_end ()
@ -807,15 +805,15 @@ TALER_EXCHANGE_verify_coin_history (
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&rr,
&coin_sig.eddsa_signature,
&coin_pub->eddsa_pub))
TALER_wallet_recoup_verify (h_denom_pub,
&coin_bks,
&recoup_amount,
coin_pub,
&coin_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
*h_denom_pub = rr.h_denom_pub;
add = GNUNET_YES;
}
else if (0 == strcasecmp (type,

View File

@ -95,7 +95,7 @@ struct TALER_EXCHANGE_RecoupHandle
* @return #GNUNET_OK if the signature is valid and we called the callback;
* #GNUNET_SYSERR if not (callback must still be called)
*/
static int
static enum GNUNET_GenericReturnValue
process_recoup_response (const struct TALER_EXCHANGE_RecoupHandle *ph,
const json_t *json)
{
@ -312,8 +312,8 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
{
struct TALER_EXCHANGE_RecoupHandle *ph;
struct GNUNET_CURL_Context *ctx;
struct TALER_RecoupRequestPS pr;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_DenominationHash h_denom_pub;
json_t *recoup_obj;
CURL *eh;
@ -321,17 +321,15 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange));
pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP);
pr.purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS));
GNUNET_CRYPTO_eddsa_key_get_public (&ps->coin_priv.eddsa_priv,
&pr.coin_pub.eddsa_pub);
&coin_pub.eddsa_pub);
TALER_denom_pub_hash (&pk->key,
&h_denom_pub);
pr.h_denom_pub = pk->h_key;
pr.coin_blind = ps->blinding_key;
GNUNET_CRYPTO_eddsa_sign (&ps->coin_priv.eddsa_priv,
&pr,
&coin_sig.eddsa_signature);
TALER_wallet_recoup_sign (&h_denom_pub,
&ps->blinding_key,
amount,
&ps->coin_priv,
&coin_sig);
recoup_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("denom_pub_hash",
&h_denom_pub),
@ -349,11 +347,11 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
char *end;
end = GNUNET_STRINGS_data_to_string (&pr.coin_pub,
sizeof (struct
TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
end = GNUNET_STRINGS_data_to_string (
&coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
*end = '\0';
GNUNET_snprintf (arg_str,
sizeof (arg_str),
@ -362,7 +360,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
}
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
ph->coin_pub = pr.coin_pub;
ph->coin_pub = coin_pub;
ph->exchange = exchange;
ph->pk = *pk;
memset (&ph->pk.key,

View File

@ -855,7 +855,7 @@ run (void *cls,
MHD_HTTP_OK,
"recoup-withdraw-coin-2a",
NULL,
NULL),
"EUR:0.5"),
TALER_TESTING_cmd_deposit ("recoup-deposit-revoked",
"recoup-withdraw-coin-2b",
0,

View File

@ -139,7 +139,7 @@ run (void *cls,
MHD_HTTP_GONE,
"refresh-reveal-1#0",
"refresh-melt-1",
NULL),
"EUR:0.1"),
/* Make refreshed coin invalid */
TALER_TESTING_cmd_revoke ("revoke-2-EUR:5",
MHD_HTTP_OK,
@ -155,44 +155,44 @@ run (void *cls,
MHD_HTTP_CONFLICT,
"withdraw-revocation-coin-2",
NULL,
NULL),
"EUR:0.1"),
/* Refund coin to original coin */
TALER_TESTING_cmd_recoup ("recoup-1a",
MHD_HTTP_OK,
"refresh-reveal-1#0",
"refresh-melt-1",
NULL),
"EUR:1"),
TALER_TESTING_cmd_recoup ("recoup-1b",
MHD_HTTP_OK,
"refresh-reveal-1#1",
"refresh-melt-1",
NULL),
"EUR:1"),
TALER_TESTING_cmd_recoup ("recoup-1c",
MHD_HTTP_OK,
"refresh-reveal-1#2",
"refresh-melt-1",
NULL),
"EUR:1"),
/* Repeat recoup to test idempotency */
TALER_TESTING_cmd_recoup ("recoup-1c",
MHD_HTTP_OK,
"refresh-reveal-1#2",
"refresh-melt-1",
NULL),
"EUR:1"),
TALER_TESTING_cmd_recoup ("recoup-1c",
MHD_HTTP_OK,
"refresh-reveal-1#2",
"refresh-melt-1",
NULL),
"EUR:1"),
TALER_TESTING_cmd_recoup ("recoup-1c",
MHD_HTTP_OK,
"refresh-reveal-1#2",
"refresh-melt-1",
NULL),
"EUR:1"),
TALER_TESTING_cmd_recoup ("recoup-1c",
MHD_HTTP_OK,
"refresh-reveal-1#2",
"refresh-melt-1",
NULL),
"EUR:1"),
/* Now we have EUR:3.83 EUR back after 3x EUR:1 in recoups */
/* 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,
@ -223,7 +223,7 @@ run (void *cls,
MHD_HTTP_OK,
"refresh-reveal-2",
"refresh-melt-2",
NULL),
"EUR:0.1"),
/* Due to recoup, original coin is now at EUR:3.79 */
/* Refund original (now zombie) coin to reserve */
TALER_TESTING_cmd_recoup ("recoup-3",

View File

@ -155,4 +155,51 @@ TALER_wallet_link_verify (
}
enum GNUNET_GenericReturnValue
TALER_wallet_recoup_verify (
const struct TALER_DenominationHash *h_denom_pub,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_Amount *requested_amount,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_RecoupRequestPS pr = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.purpose.size = htonl (sizeof (pr)),
.h_denom_pub = *h_denom_pub,
.coin_blind = *coin_bks
};
TALER_amount_hton (&pr.recoup_amount,
requested_amount);
return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
&pr,
&coin_sig->eddsa_signature,
&coin_pub->eddsa_pub);
}
void
TALER_wallet_recoup_sign (
const struct TALER_DenominationHash *h_denom_pub,
const union TALER_DenominationBlindingKeyP *coin_bks,
const struct TALER_Amount *requested_amount,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_RecoupRequestPS pr = {
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
.purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
.h_denom_pub = *h_denom_pub,
.coin_blind = *coin_bks
};
TALER_amount_hton (&pr.recoup_amount,
requested_amount);
GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv,
&pr,
&coin_sig->eddsa_signature);
}
/* end of wallet_signatures.c */