complete P2P/W2W conflict handling, deduplicate code across handlers

This commit is contained in:
Christian Grothoff 2022-06-05 14:07:23 +02:00
parent 6c81796d6f
commit b9963f7525
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
26 changed files with 904 additions and 666 deletions

View File

@ -456,6 +456,21 @@ parse_planchets (const struct TEH_RequestContext *rc,
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
} }
pc->collectable.reserve_pub = *wc->reserve_pub; pc->collectable.reserve_pub = *wc->reserve_pub;
for (unsigned int k = 0; k<i; k++)
{
const struct PlanchetContext *kpc = &wc->planchets[k];
if (0 ==
TALER_blinded_planchet_cmp (&kpc->blinded_planchet,
&pc->blinded_planchet))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"duplicate planchet");
}
}
} }
ksh = TEH_keys_get_state (); ksh = TEH_keys_get_state ();

View File

@ -61,15 +61,19 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
NULL); NULL);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
case TALER_EXCHANGEDB_CKS_DENOM_CONFLICT: case TALER_EXCHANGEDB_CKS_DENOM_CONFLICT:
/* FIXME-Oec: insufficient_funds != denom conflict! */
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
&h_denom_pub,
&coin->coin_pub); &coin->coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
case TALER_EXCHANGEDB_CKS_AGE_CONFLICT: case TALER_EXCHANGEDB_CKS_AGE_CONFLICT:
/* FIXME-Oec: insufficient_funds != Age conflict! */
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH, TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH,
&h_denom_pub,
&coin->coin_pub); &coin->coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA Copyright (C) 2014-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -178,10 +178,12 @@ deposit_transaction (void *cls,
} }
if (in_conflict) if (in_conflict)
{ {
/* FIXME: conficting contract != insufficient funds */
*mhd_ret *mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT, TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
&dc->deposit->coin.denom_pub_hash,
&dc->deposit->coin.coin_pub); &dc->deposit->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
@ -191,6 +193,7 @@ deposit_transaction (void *cls,
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&dc->deposit->coin.denom_pub_hash,
&dc->deposit->coin.coin_pub); &dc->deposit->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -196,6 +196,7 @@ melt_transaction (void *cls,
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&rmc->refresh_session.coin.denom_pub_hash,
&rmc->refresh_session.coin.coin_pub); &rmc->refresh_session.coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -319,6 +319,7 @@ create_transaction (void *cls,
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&coin->cpi.denom_pub_hash,
&coin->cpi.coin_pub); &coin->cpi.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -229,6 +229,7 @@ deposit_transaction (void *cls,
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&coin->cpi.denom_pub_hash,
&coin->cpi.coin_pub); &coin->cpi.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -325,14 +325,13 @@ merge_transaction (void *cls,
GNUNET_JSON_pack_data_auto ("merge_sig", GNUNET_JSON_pack_data_auto ("merge_sig",
&merge_sig), &merge_sig),
GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("partner_base_url", GNUNET_JSON_pack_string ("partner_url",
partner_url)), partner_url)),
GNUNET_JSON_pack_data_auto ("reserve_pub", GNUNET_JSON_pack_data_auto ("reserve_pub",
&reserve_pub)); &reserve_pub));
GNUNET_free (partner_url); GNUNET_free (partner_url);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
// FIXME: if ! kyc check, return 451!
return qs; return qs;
} }

View File

@ -146,6 +146,7 @@ recoup_refresh_transaction (void *cls,
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&pc->coin->denom_pub_hash,
&pc->coin->coin_pub); &pc->coin->coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -149,6 +149,7 @@ recoup_transaction (void *cls,
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&pc->coin->denom_pub_hash,
&pc->coin->coin_pub); &pc->coin->coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }

View File

@ -158,10 +158,10 @@ refund_transaction (void *cls,
} }
if (conflict) if (conflict)
{ {
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_REFUND_INCONSISTENT_AMOUNT, TALER_EC_EXCHANGE_REFUND_INCONSISTENT_AMOUNT,
&refund->coin.denom_pub_hash,
&refund->coin.coin_pub); &refund->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
@ -175,10 +175,10 @@ refund_transaction (void *cls,
} }
if (! refund_ok) if (! refund_ok)
{ {
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
TALER_EC_EXCHANGE_REFUND_CONFLICT_DEPOSIT_INSUFFICIENT, TALER_EC_EXCHANGE_REFUND_CONFLICT_DEPOSIT_INSUFFICIENT,
&refund->coin.denom_pub_hash,
&refund->coin.coin_pub); &refund->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
@ -200,7 +200,6 @@ static MHD_RESULT
verify_and_execute_refund (struct MHD_Connection *connection, verify_and_execute_refund (struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Refund *refund) struct TALER_EXCHANGEDB_Refund *refund)
{ {
struct TALER_DenominationHashP denom_hash;
struct RefundContext rctx = { struct RefundContext rctx = {
.refund = refund .refund = refund
}; };
@ -228,15 +227,16 @@ verify_and_execute_refund (struct MHD_Connection *connection,
qs = TEH_plugin->get_coin_denomination (TEH_plugin->cls, qs = TEH_plugin->get_coin_denomination (TEH_plugin->cls,
&refund->coin.coin_pub, &refund->coin.coin_pub,
&rctx.known_coin_id, &rctx.known_coin_id,
&denom_hash); &refund->coin.denom_pub_hash);
if (0 > qs) if (0 > qs)
{ {
MHD_RESULT res; MHD_RESULT res;
char *dhs; char *dhs;
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
dhs = GNUNET_STRINGS_data_to_string_alloc (&denom_hash, dhs = GNUNET_STRINGS_data_to_string_alloc (
sizeof (denom_hash)); &refund->coin.denom_pub_hash,
sizeof (refund->coin.denom_pub_hash));
res = TALER_MHD_reply_with_error (connection, res = TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_REFUND_COIN_NOT_FOUND, TALER_EC_EXCHANGE_REFUND_COIN_NOT_FOUND,
@ -251,7 +251,7 @@ verify_and_execute_refund (struct MHD_Connection *connection,
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
MHD_RESULT mret; MHD_RESULT mret;
dk = TEH_keys_denomination_by_hash (&denom_hash, dk = TEH_keys_denomination_by_hash (&refund->coin.denom_pub_hash,
connection, connection,
&mret); &mret);
if (NULL == dk) if (NULL == dk)

View File

@ -70,18 +70,19 @@ TEH_RESPONSE_compile_transaction_history (
/* internal sanity check before we hand out a bogus sig... */ /* internal sanity check before we hand out a bogus sig... */
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_deposit_verify (&deposit->amount_with_fee, TALER_wallet_deposit_verify (
&deposit->deposit_fee, &deposit->amount_with_fee,
&h_wire, &deposit->deposit_fee,
&deposit->h_contract_terms, &h_wire,
&deposit->h_age_commitment, &deposit->h_contract_terms,
NULL /* h_extensions! */, &deposit->h_age_commitment,
&deposit->h_denom_pub, NULL /* h_extensions! */,
deposit->timestamp, &deposit->h_denom_pub,
&deposit->merchant_pub, deposit->timestamp,
deposit->refund_deadline, &deposit->merchant_pub,
coin_pub, deposit->refund_deadline,
&deposit->csig)) coin_pub,
&deposit->csig))
{ {
GNUNET_break (0); GNUNET_break (0);
json_decref (history); json_decref (history);
@ -109,8 +110,6 @@ TEH_RESPONSE_compile_transaction_history (
&deposit->h_contract_terms), &deposit->h_contract_terms),
GNUNET_JSON_pack_data_auto ("h_wire", GNUNET_JSON_pack_data_auto ("h_wire",
&h_wire), &h_wire),
GNUNET_JSON_pack_data_auto ("h_denom_pub",
&deposit->h_denom_pub),
GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_allow_null (
deposit->no_age_commitment ? deposit->no_age_commitment ?
GNUNET_JSON_pack_string ( GNUNET_JSON_pack_string (
@ -135,13 +134,14 @@ TEH_RESPONSE_compile_transaction_history (
#if ENABLE_SANITY_CHECKS #if ENABLE_SANITY_CHECKS
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_melt_verify (&melt->amount_with_fee, TALER_wallet_melt_verify (
&melt->melt_fee, &melt->amount_with_fee,
&melt->rc, &melt->melt_fee,
&melt->h_denom_pub, &melt->rc,
&melt->h_age_commitment, &melt->h_denom_pub,
coin_pub, &melt->h_age_commitment,
&melt->coin_sig)) coin_pub,
&melt->coin_sig))
{ {
GNUNET_break (0); GNUNET_break (0);
json_decref (history); json_decref (history);
@ -166,8 +166,6 @@ TEH_RESPONSE_compile_transaction_history (
&melt->melt_fee), &melt->melt_fee),
GNUNET_JSON_pack_data_auto ("rc", GNUNET_JSON_pack_data_auto ("rc",
&melt->rc), &melt->rc),
GNUNET_JSON_pack_data_auto ("h_denom_pub",
&melt->h_denom_pub),
GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_data_auto ("h_age_commitment", GNUNET_JSON_pack_data_auto ("h_age_commitment",
phac)), phac)),
@ -189,12 +187,13 @@ TEH_RESPONSE_compile_transaction_history (
#if ENABLE_SANITY_CHECKS #if ENABLE_SANITY_CHECKS
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_merchant_refund_verify (coin_pub, TALER_merchant_refund_verify (
&refund->h_contract_terms, coin_pub,
refund->rtransaction_id, &refund->h_contract_terms,
&refund->refund_amount, refund->rtransaction_id,
&refund->merchant_pub, &refund->refund_amount,
&refund->merchant_sig)) &refund->merchant_pub,
&refund->merchant_sig))
{ {
GNUNET_break (0); GNUNET_break (0);
json_decref (history); json_decref (history);
@ -319,8 +318,6 @@ TEH_RESPONSE_compile_transaction_history (
&epub), &epub),
GNUNET_JSON_pack_data_auto ("reserve_pub", GNUNET_JSON_pack_data_auto ("reserve_pub",
&recoup->reserve_pub), &recoup->reserve_pub),
GNUNET_JSON_pack_data_auto ("h_denom_pub",
&recoup->h_denom_pub),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&recoup->coin_sig), &recoup->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind", GNUNET_JSON_pack_data_auto ("coin_blind",
@ -376,8 +373,6 @@ TEH_RESPONSE_compile_transaction_history (
&epub), &epub),
GNUNET_JSON_pack_data_auto ("old_coin_pub", GNUNET_JSON_pack_data_auto ("old_coin_pub",
&pr->old_coin_pub), &pr->old_coin_pub),
GNUNET_JSON_pack_data_auto ("h_denom_pub",
&pr->coin.denom_pub_hash),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&pr->coin_sig), &pr->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind", GNUNET_JSON_pack_data_auto ("coin_blind",
@ -558,18 +553,31 @@ MHD_RESULT
TEH_RESPONSE_reply_coin_insufficient_funds ( TEH_RESPONSE_reply_coin_insufficient_funds (
struct MHD_Connection *connection, struct MHD_Connection *connection,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
const struct TALER_DenominationHashP *h_denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub) const struct TALER_CoinSpendPublicKeyP *coin_pub)
{ {
struct TALER_EXCHANGEDB_TransactionList *tl; struct TALER_EXCHANGEDB_TransactionList *tl;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
json_t *history; json_t *history;
// FIXME: maybe start read-committed transaction here? TEH_plugin->rollback (TEH_plugin->cls);
// => check all callers (that they aborted already!) // FIXME: maybe start read-only transaction here?
if (GNUNET_OK !=
TEH_plugin->start_read_committed (TEH_plugin->cls,
"get_coin_transactions"))
{
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_START_FAILED,
NULL);
}
// FIXME: simplify, 3rd arg is always 'true' now?
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls, qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
coin_pub, coin_pub,
GNUNET_NO, true,
&tl); &tl);
TEH_plugin->rollback (TEH_plugin->cls);
if (0 > qs) if (0 > qs)
{ {
return TALER_MHD_reply_with_error ( return TALER_MHD_reply_with_error (
@ -597,6 +605,8 @@ TEH_RESPONSE_reply_coin_insufficient_funds (
TALER_JSON_pack_ec (ec), TALER_JSON_pack_ec (ec),
GNUNET_JSON_pack_data_auto ("coin_pub", GNUNET_JSON_pack_data_auto ("coin_pub",
coin_pub), coin_pub),
GNUNET_JSON_pack_data_auto ("h_denom_pub",
h_denom_pub),
GNUNET_JSON_pack_array_steal ("history", GNUNET_JSON_pack_array_steal ("history",
history)); history));
} }

View File

@ -113,6 +113,7 @@ TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
* *
* @param connection connection to the client * @param connection connection to the client
* @param ec error code to return * @param ec error code to return
* @param h_denom_pub hash of the denomination of the coin
* @param coin_pub public key of the coin * @param coin_pub public key of the coin
* @return MHD result code * @return MHD result code
*/ */
@ -120,6 +121,7 @@ MHD_RESULT
TEH_RESPONSE_reply_coin_insufficient_funds ( TEH_RESPONSE_reply_coin_insufficient_funds (
struct MHD_Connection *connection, struct MHD_Connection *connection,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
const struct TALER_DenominationHashP *h_denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub); const struct TALER_CoinSpendPublicKeyP *coin_pub);

View File

@ -8403,7 +8403,6 @@ add_coin_melt (void *cls,
chc->failed = true; chc->failed = true;
return; return;
} }
} }
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList); tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
tl->next = chc->head; tl->next = chc->head;
@ -8704,7 +8703,7 @@ static enum GNUNET_DB_QueryStatus
postgres_get_coin_transactions ( postgres_get_coin_transactions (
void *cls, void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
int include_recoup, bool include_recoup,
struct TALER_EXCHANGEDB_TransactionList **tlp) struct TALER_EXCHANGEDB_TransactionList **tlp)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;

View File

@ -2978,21 +2978,17 @@ TALER_EXCHANGE_deposits_get_cancel (
* Convenience function. Verifies a coin's transaction history as * Convenience function. Verifies a coin's transaction history as
* returned by the exchange. * returned by the exchange.
* *
* @param dk fee structure for the coin, NULL to skip verifying fees * @param dk fee structure for the coin
* @param currency expected currency for the coin
* @param coin_pub public key of the coin * @param coin_pub public key of the coin
* @param history history of the coin in json encoding * @param history history of the coin in json encoding
* @param[out] h_denom_pub set to the hash of the coin's denomination (if available)
* @param[out] total how much of the coin has been spent according to @a history * @param[out] total how much of the coin has been spent according to @a history
* @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_coin_history ( TALER_EXCHANGE_verify_coin_history (
const struct TALER_EXCHANGE_DenomPublicKey *dk, const struct TALER_EXCHANGE_DenomPublicKey *dk,
const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history, json_t *history,
struct TALER_DenominationHashP *h_denom_pub,
struct TALER_Amount *total); struct TALER_Amount *total);

View File

@ -3620,7 +3620,7 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*get_coin_transactions)(void *cls, (*get_coin_transactions)(void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
int include_recoup, bool include_recoup,
struct TALER_EXCHANGEDB_TransactionList **tlp); struct TALER_EXCHANGEDB_TransactionList **tlp);

View File

@ -22,6 +22,7 @@
#include "platform.h" #include "platform.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_common.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "taler_signatures.h" #include "taler_signatures.h"
@ -684,11 +685,6 @@ struct CoinHistoryParseContext
*/ */
const struct TALER_CoinSpendPublicKeyP *coin_pub; const struct TALER_CoinSpendPublicKeyP *coin_pub;
/**
* Hash of @e dk, set from parsing.
*/
struct TALER_DenominationHashP *h_denom_pub;
/** /**
* Where to sum up total refunds. * Where to sum up total refunds.
*/ */
@ -749,8 +745,6 @@ help_deposit (struct CoinHistoryParseContext *pc,
&h_contract_terms), &h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_wire", GNUNET_JSON_spec_fixed_auto ("h_wire",
&h_wire), &h_wire),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment", GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&hac), &hac),
@ -784,7 +778,7 @@ help_deposit (struct CoinHistoryParseContext *pc,
&h_contract_terms, &h_contract_terms,
no_hac ? NULL : &hac, no_hac ? NULL : &hac,
NULL /* h_extensions! */, NULL /* h_extensions! */,
pc->h_denom_pub, &pc->dk->h_key,
wallet_timestamp, wallet_timestamp,
&merchant_pub, &merchant_pub,
refund_deadline, refund_deadline,
@ -836,8 +830,6 @@ help_melt (struct CoinHistoryParseContext *pc,
&sig), &sig),
GNUNET_JSON_spec_fixed_auto ("rc", GNUNET_JSON_spec_fixed_auto ("rc",
&rc), &rc),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment", GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&h_age_commitment), &h_age_commitment),
@ -876,7 +868,7 @@ help_melt (struct CoinHistoryParseContext *pc,
amount, amount,
&melt_fee, &melt_fee,
&rc, &rc,
pc->h_denom_pub, &pc->dk->h_key,
no_hac no_hac
? NULL ? NULL
: &h_age_commitment, : &h_age_commitment,
@ -1008,8 +1000,6 @@ help_recoup (struct CoinHistoryParseContext *pc,
&coin_sig), &coin_sig),
GNUNET_JSON_spec_fixed_auto ("coin_blind", GNUNET_JSON_spec_fixed_auto ("coin_blind",
&coin_bks), &coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_timestamp ("timestamp", GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp), &timestamp),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
@ -1036,7 +1026,7 @@ help_recoup (struct CoinHistoryParseContext *pc,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_recoup_verify (pc->h_denom_pub, TALER_wallet_recoup_verify (&pc->dk->h_key,
&coin_bks, &coin_bks,
pc->coin_pub, pc->coin_pub,
&coin_sig)) &coin_sig))
@ -1081,8 +1071,6 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
&old_coin_pub), &old_coin_pub),
GNUNET_JSON_spec_fixed_auto ("coin_blind", GNUNET_JSON_spec_fixed_auto ("coin_blind",
&coin_bks), &coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_timestamp ("timestamp", GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp), &timestamp),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
@ -1109,7 +1097,7 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_recoup_verify (pc->h_denom_pub, TALER_wallet_recoup_verify (&pc->dk->h_key,
&coin_bks, &coin_bks,
pc->coin_pub, pc->coin_pub,
&coin_sig)) &coin_sig))
@ -1250,12 +1238,11 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_coin_history ( TALER_EXCHANGE_verify_coin_history (
const struct TALER_EXCHANGE_DenomPublicKey *dk, const struct TALER_EXCHANGE_DenomPublicKey *dk,
const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history, json_t *history,
struct TALER_DenominationHashP *h_denom_pub,
struct TALER_Amount *total) struct TALER_Amount *total)
{ {
const char *currency = dk->value.currency;
const struct const struct
{ {
const char *type; const char *type;
@ -1273,8 +1260,7 @@ TALER_EXCHANGE_verify_coin_history (
struct CoinHistoryParseContext pc = { struct CoinHistoryParseContext pc = {
.dk = dk, .dk = dk,
.coin_pub = coin_pub, .coin_pub = coin_pub,
.total = total, .total = total
.h_denom_pub = h_denom_pub
}; };
size_t len; size_t len;
@ -1528,11 +1514,10 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
{ {
struct TALER_PurseMergeSignatureP merge_sig; struct TALER_PurseMergeSignatureP merge_sig;
struct GNUNET_TIME_Timestamp merge_timestamp; struct GNUNET_TIME_Timestamp merge_timestamp;
const char *partner_url = exchange_url; const char *partner_url = NULL;
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
// FIXME: partner_url or partner_base_url?
GNUNET_JSON_spec_string ("partner_url", GNUNET_JSON_spec_string ("partner_url",
&partner_url), &partner_url),
NULL), NULL),
@ -1554,6 +1539,8 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (NULL == partner_url)
partner_url = exchange_url;
payto_uri = make_payto (partner_url, payto_uri = make_payto (partner_url,
&reserve_pub); &reserve_pub);
if (GNUNET_OK != if (GNUNET_OK !=
@ -1581,18 +1568,55 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
} }
/** enum GNUNET_GenericReturnValue
* Check proof of a contract conflict. TALER_EXCHANGE_check_purse_coin_conflict_ (
* const struct TALER_PurseContractPublicKeyP *purse_pub,
* DESIGN-FIXME: this 'proof' doesn't really proof a conflict! const char *exchange_url,
* const json_t *proof,
* @param ccontract_sig conflicting signature (must struct TALER_CoinSpendPublicKeyP *coin_pub,
* not match the signature from the proof) struct TALER_CoinSpendSignatureP *coin_sig)
* @param purse_pub public key of the purse {
* @param exchange_url the base URL of this exchange const char *partner_url = NULL;
* @param proof the proof to check struct TALER_Amount amount;
* @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig struct GNUNET_JSON_Specification spec[] = {
*/ GNUNET_JSON_spec_fixed_auto ("coin_sig",
coin_sig),
GNUNET_JSON_spec_fixed_auto ("coin_pub",
coin_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("partner_url",
&partner_url),
NULL),
TALER_JSON_spec_amount_any ("amount",
&amount),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (proof,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (NULL == partner_url)
partner_url = exchange_url;
if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify (
partner_url,
purse_pub,
&amount,
coin_pub,
coin_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_purse_econtract_conflict_ ( TALER_EXCHANGE_check_purse_econtract_conflict_ (
const struct TALER_PurseContractSignatureP *ccontract_sig, const struct TALER_PurseContractSignatureP *ccontract_sig,
@ -1642,4 +1666,278 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (
} }
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_amount_conflict_ (
const struct TALER_EXCHANGE_Keys *keys,
const json_t *proof,
struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_Amount *remaining)
{
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_pub",
coin_pub),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&h_denom_pub),
GNUNET_JSON_spec_json ("history",
&history),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (proof,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
dki = TALER_EXCHANGE_get_denomination_key_by_hash (
keys,
&h_denom_pub);
if (NULL == dki)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki,
coin_pub,
history,
&total))
{
GNUNET_break_op (0);
json_decref (history);
return GNUNET_SYSERR;
}
json_decref (history);
if (0 >
TALER_amount_subtract (remaining,
&dki->value,
&total))
{
/* Strange 'proof': coin was double-spent
before our transaction?! */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Verify that @a coin_sig does NOT appear in
* the history of @a proof and thus whatever transaction
* is authorized by @a coin_sig is a conflict with
* @a proof.
*
* @param proof a proof to check
* @param coin_sig signature that must not be in @a proof
* @return #GNUNET_OK if @a coin_sig is not in @a proof
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_signature_conflict_ (
const json_t *proof,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
json_t *history;
size_t off;
json_t *entry;
history = json_object_get (proof,
"history");
if (NULL == history)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
json_array_foreach (history, off, entry)
{
struct TALER_CoinSpendSignatureP cs;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&cs),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (entry,
spec,
NULL, NULL))
continue; /* entry without coin signature */
if (0 ==
GNUNET_memcmp (&cs,
coin_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_denomination_conflict_ (
const json_t *proof,
const struct TALER_DenominationHashP *ch_denom_pub)
{
struct TALER_DenominationHashP h_denom_pub;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&h_denom_pub),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (proof,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (0 ==
GNUNET_memcmp (ch_denom_pub,
&h_denom_pub))
{
GNUNET_break_op (0);
return GNUNET_OK;
}
/* indeed, proof with different denomination key provided */
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_conflict_ (
const struct TALER_EXCHANGE_Keys *keys,
const json_t *proof,
const struct TALER_EXCHANGE_DenomPublicKey *dk,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *required)
{
enum TALER_ErrorCode ec;
ec = TALER_JSON_get_error_code (proof);
switch (ec)
{
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
{
struct TALER_Amount left;
struct TALER_CoinSpendPublicKeyP pcoin_pub;
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_amount_conflict_ (
keys,
proof,
&pcoin_pub,
&left))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (0 !=
GNUNET_memcmp (&pcoin_pub,
coin_pub))
{
/* conflict is for a different coin! */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (-1 !=
TALER_amount_cmp (&left,
required))
{
/* Balance was sufficient after all; recoup MAY have still been possible */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_signature_conflict_ (
proof,
coin_sig))
{
/* Not a conflicting transaction: ours is included! */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
break;
}
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
{
struct TALER_Amount left;
struct TALER_CoinSpendPublicKeyP pcoin_pub;
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_amount_conflict_ (
keys,
proof,
&pcoin_pub,
&left))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (0 !=
GNUNET_memcmp (&pcoin_pub,
coin_pub))
{
/* conflict is for a different coin! */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_denomination_conflict_ (
proof,
&dk->h_key))
{
/* Eh, same denomination, hence no conflict */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
break;
}
default:
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_get_min_denomination_ (
const struct TALER_EXCHANGE_Keys *keys,
struct TALER_Amount *min)
{
bool have_min = false;
for (unsigned int i = 0; i<keys->num_denom_keys; i++)
{
const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i];
if (! have_min)
{
*min = dk->value;
have_min = true;
continue;
}
if (1 != TALER_amount_cmp (min,
&dk->value))
continue;
*min = dk->value;
}
if (! have_min)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/* end of exchange_api_common.c */ /* end of exchange_api_common.c */

View File

@ -23,6 +23,7 @@
#define EXCHANGE_API_COMMON_H #define EXCHANGE_API_COMMON_H
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchange_service.h"
/** /**
@ -33,7 +34,7 @@
* @param purse_pub the public key (must match * @param purse_pub the public key (must match
* the signature from the proof) * the signature from the proof)
* @param proof the proof to check * @param proof the proof to check
* @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a cpurse_sig
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_purse_create_conflict_ ( TALER_EXCHANGE_check_purse_create_conflict_ (
@ -51,7 +52,7 @@ TALER_EXCHANGE_check_purse_create_conflict_ (
* the signature from the proof) * the signature from the proof)
* @param exchange_url the base URL of this exchange * @param exchange_url the base URL of this exchange
* @param proof the proof to check * @param proof the proof to check
* @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and @a merge_pub and conflicts with @a cmerge_sig
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_purse_merge_conflict_ ( TALER_EXCHANGE_check_purse_merge_conflict_ (
@ -63,16 +64,38 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
/** /**
* Check proof of a contract conflict. * Check @a proof that claims this coin was spend
* differently on the same purse already. Note that
* the caller must still check that @a coin_pub is
* in the list of coins that were used, and that
* @a coin_sig is different from the signature the
* caller used.
* *
* DESIGN-FIXME: this 'proof' doesn't really proof a conflict! * @param purse_pub the public key of the purse
* @param exchange_url base URL of our exchange
* @param proof the proof to check
* @param[out] coin_pub set to the conflicting coin
* @param[out] coin_sig set to the conflicting signature
* @return #GNUNET_OK if the @a proof is OK for @a purse_pub and showing that @a coin_pub was spent using @a coin_sig.
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_purse_coin_conflict_ (
const struct TALER_PurseContractPublicKeyP *purse_pub,
const char *exchange_url,
const json_t *proof,
struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_CoinSpendSignatureP *coin_sig);
/**
* Check proof of a contract conflict.
* *
* @param ccontract_sig conflicting signature (must * @param ccontract_sig conflicting signature (must
* not match the signature from the proof) * not match the signature from the proof)
* @param purse_pub public key of the purse * @param purse_pub public key of the purse
* @param exchange_url the base URL of this exchange * @param exchange_url the base URL of this exchange
* @param proof the proof to check * @param proof the proof to check
* @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a ccontract_sig
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_purse_econtract_conflict_ ( TALER_EXCHANGE_check_purse_econtract_conflict_ (
@ -81,4 +104,94 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (
const json_t *proof); const json_t *proof);
/**
* Check proof of a coin spend value conflict.
*
* @param keys exchange /keys structure
* @param proof the proof to check
* @param[out] coin_pub set to the public key of the
* coin that is claimed to have an insufficient
* balance
* @param[out] remaining set to the remaining balance
* of the coin as provided by the proof
* @return #GNUNET_OK if the @a proof is OK for @a purse_pub demonstrating that @a coin_pub has only @a remaining balance.
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_amount_conflict_ (
const struct TALER_EXCHANGE_Keys *keys,
const json_t *proof,
struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_Amount *remaining);
/**
* Verify that @a proof contains a coin history
* that demonstrates that @a coin_pub was previously
* used with a denomination key that is different
* from @a ch_denom_pub. Note that the coin history
* MUST have been checked before using
* #TALER_EXCHANGE_check_coin_amount_conflict_().
*
* @param proof a proof to check
* @param ch_denom_pub hash of the conflicting denomination
* @return #GNUNET_OK if @a ch_denom_pub differs from the
* denomination hash given by the history of the coin
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_denomination_conflict_ (
const json_t *proof,
const struct TALER_DenominationHashP *ch_denom_pub);
/**
* Verify that @a coin_sig does NOT appear in
* the history of @a proof and thus whatever transaction
* is authorized by @a coin_sig is a conflict with
* @a proof.
*
* @param proof a proof to check
* @param coin_sig signature that must not be in @a proof
* @return #GNUNET_OK if @a coin_sig is not in @a proof
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_signature_conflict_ (
const json_t *proof,
const struct TALER_CoinSpendSignatureP *coin_sig);
/**
* Check that the provided @a proof indeeds indicates
* a conflict for @a coin_pub.
*
* @param keys exchange keys
* @param proof provided conflict proof
* @param dk denomination of @a coin_pub that the client
* used
* @param coin_pub public key of the coin
* @param coin_sig signature over operation that conflicted
* @param required balance required on the coin for the operation
* @return #GNUNET_OK if @a proof holds
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_conflict_ (
const struct TALER_EXCHANGE_Keys *keys,
const json_t *proof,
const struct TALER_EXCHANGE_DenomPublicKey *dk,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *required);
/**
* Find the smallest denomination amount in @e keys.
*
* @param keys keys to search
* @param[out] min set to the smallest amount
* @return #GNUNET_SYSERR if there are no denominations in @a keys
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_get_min_denomination_ (
const struct TALER_EXCHANGE_Keys *keys,
struct TALER_Amount *min);
#endif #endif

View File

@ -29,6 +29,7 @@
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_auditor_service.h" #include "taler_auditor_service.h"
#include "taler_exchange_service.h" #include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
@ -128,6 +129,11 @@ struct TALER_EXCHANGE_DepositHandle
*/ */
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Our signature for the deposit operation.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/** /**
* The Merchant's public key. Allows the merchant to later refund * The Merchant's public key. Allows the merchant to later refund
* the transaction or to inquire about the wire transfer identifier. * the transaction or to inquire about the wire transfer identifier.
@ -226,77 +232,6 @@ auditor_cb (void *cls,
} }
/**
* Verify that the signatures on the "403 FORBIDDEN" response from the
* exchange demonstrating customer double-spending are valid.
*
* @param dh deposit handle
* @param json json reply with the signature(s) and transaction history
* @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
*/
static enum GNUNET_GenericReturnValue
verify_deposit_signature_conflict (
const struct TALER_EXCHANGE_DepositHandle *dh,
const json_t *json)
{
json_t *history;
struct TALER_Amount total;
enum TALER_ErrorCode ec;
struct TALER_DenominationHashP h_denom_pub;
memset (&h_denom_pub,
0,
sizeof (h_denom_pub));
history = json_object_get (json,
"history");
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (&dh->dki,
dh->dki.value.currency,
&dh->coin_pub,
history,
&h_denom_pub,
&total))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
ec = TALER_JSON_get_error_code (json);
switch (ec)
{
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
if (0 >
TALER_amount_add (&total,
&total,
&dh->amount_with_fee))
{
/* clearly not OK if our transaction would have caused
the overflow... */
return GNUNET_OK;
}
if (0 >= TALER_amount_cmp (&total,
&dh->dki.value))
{
/* transaction should have still fit */
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* everything OK, proof of double-spending was provided */
return GNUNET_OK;
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
if (0 != GNUNET_memcmp (&dh->dki.h_key,
&h_denom_pub))
return GNUNET_OK; /* indeed, proof with different denomination key provided */
/* invalid proof provided */
return GNUNET_SYSERR;
default:
/* unexpected error code */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
/** /**
* Function called when we're done processing the * Function called when we're done processing the
* HTTP /deposit request. * HTTP /deposit request.
@ -316,8 +251,10 @@ handle_deposit_finished (void *cls,
.hr.reply = j, .hr.reply = j,
.hr.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
const struct TALER_EXCHANGE_Keys *keys;
dh->job = NULL; dh->job = NULL;
keys = TALER_EXCHANGE_get_keys (dh->exchange);
switch (response_code) switch (response_code)
{ {
case 0: case 0:
@ -409,19 +346,21 @@ handle_deposit_finished (void *cls,
happen, we should pass the JSON reply to the application */ happen, we should pass the JSON reply to the application */
break; break;
case MHD_HTTP_CONFLICT: case MHD_HTTP_CONFLICT:
/* Double spending; check signatures on transaction history */ dr.hr.ec = TALER_JSON_get_error_code (j);
dr.hr.hint = TALER_JSON_get_error_hint (j);
if (GNUNET_OK != if (GNUNET_OK !=
verify_deposit_signature_conflict (dh, TALER_EXCHANGE_check_coin_conflict_ (
j)) keys,
j,
&dh->dki,
&dh->coin_pub,
&dh->coin_sig,
&dh->amount_with_fee))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
} break;
else
{
dr.hr.ec = TALER_JSON_get_error_code (j);
dr.hr.hint = TALER_JSON_get_error_hint (j);
} }
break; break;
case MHD_HTTP_GONE: case MHD_HTTP_GONE:
@ -692,6 +631,8 @@ TALER_EXCHANGE_deposit (
dh->exchange = exchange; dh->exchange = exchange;
dh->cb = cb; dh->cb = cb;
dh->cb_cls = cb_cls; dh->cb_cls = cb_cls;
dh->coin_sig = *coin_sig;
dh->coin_pub = *coin_pub;
dh->url = TEAH_path_to_url (exchange, dh->url = TEAH_path_to_url (exchange,
arg_str); arg_str);
if (NULL == dh->url) if (NULL == dh->url)

View File

@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchange_service.h" #include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
@ -101,6 +102,11 @@ struct TALER_EXCHANGE_MeltHandle
*/ */
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Signature affirming the melt.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/** /**
* @brief Public information about the coin's denomination key * @brief Public information about the coin's denomination key
*/ */
@ -183,143 +189,6 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
} }
/**
* Verify that the signatures on the "409 CONFLICT" response from the
* exchange demonstrating customer denomination key differences
* resulting from coin private key reuse are valid.
*
* @param mh melt handle
* @param json json reply with the signature(s) and transaction history
* @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
*/
static enum GNUNET_GenericReturnValue
verify_melt_signature_denom_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
const json_t *json)
{
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
memset (&h_denom_pub,
0,
sizeof (h_denom_pub));
history = json_object_get (json,
"history");
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (mh->dki,
mh->dki->value.currency,
&mh->coin_pub,
history,
&h_denom_pub,
&total))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (0 != GNUNET_memcmp (&mh->dki->h_key,
&h_denom_pub))
return GNUNET_OK; /* indeed, proof with different denomination key provided */
/* invalid proof provided */
return GNUNET_SYSERR;
}
/**
* Verify that the signatures on the "409 CONFLICT" response from the
* exchange demonstrating customer double-spending are valid.
*
* @param mh melt handle
* @param json json reply with the signature(s) and transaction history
* @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
*/
static enum GNUNET_GenericReturnValue
verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
const json_t *json)
{
json_t *history;
struct TALER_Amount total;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("history",
&history),
GNUNET_JSON_spec_end ()
};
const struct MeltedCoin *mc;
enum TALER_ErrorCode ec;
struct TALER_DenominationHashP h_denom_pub;
/* parse JSON reply */
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
/* Find out which coin was deemed problematic by the exchange */
mc = &mh->md.melted_coin;
/* verify coin history */
memset (&h_denom_pub,
0,
sizeof (h_denom_pub));
history = json_object_get (json,
"history");
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (mh->dki,
mc->original_value.currency,
&mh->coin_pub,
history,
&h_denom_pub,
&total))
{
GNUNET_break_op (0);
json_decref (history);
return GNUNET_SYSERR;
}
json_decref (history);
ec = TALER_JSON_get_error_code (json);
switch (ec)
{
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
/* check if melt operation was really too expensive given history */
if (0 >
TALER_amount_add (&total,
&total,
&mc->melt_amount_with_fee))
{
/* clearly not OK if our transaction would have caused
the overflow... */
return GNUNET_OK;
}
if (0 >= TALER_amount_cmp (&total,
&mc->original_value))
{
/* transaction should have still fit */
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* everything OK, valid proof of double-spending was provided */
return GNUNET_OK;
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
if (0 != GNUNET_memcmp (&mh->dki->h_key,
&h_denom_pub))
return GNUNET_OK; /* indeed, proof with different denomination key provided */
/* invalid proof provided */
GNUNET_break_op (0);
return GNUNET_SYSERR;
default:
/* unexpected error code */
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
/** /**
* Function called when we're done processing the * Function called when we're done processing the
* HTTP /coins/$COIN_PUB/melt request. * HTTP /coins/$COIN_PUB/melt request.
@ -339,8 +208,10 @@ handle_melt_finished (void *cls,
.hr.reply = j, .hr.reply = j,
.hr.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
const struct TALER_EXCHANGE_Keys *keys;
mh->job = NULL; mh->job = NULL;
keys = TALER_EXCHANGE_get_keys (mh->exchange);
switch (response_code) switch (response_code)
{ {
case 0: case 0:
@ -372,36 +243,19 @@ handle_melt_finished (void *cls,
break; break;
case MHD_HTTP_CONFLICT: case MHD_HTTP_CONFLICT:
mr.hr.ec = TALER_JSON_get_error_code (j); mr.hr.ec = TALER_JSON_get_error_code (j);
switch (mr.hr.ec) mr.hr.hint = TALER_JSON_get_error_hint (j);
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_conflict_ (
keys,
j,
mh->dki,
&mh->coin_pub,
&mh->coin_sig,
&mh->md.melted_coin.melt_amount_with_fee))
{ {
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
/* Double spending; check signatures on transaction history */
if (GNUNET_OK !=
verify_melt_signature_spend_conflict (mh,
j))
{
GNUNET_break_op (0);
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
mr.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
if (GNUNET_OK !=
verify_melt_signature_denom_conflict (mh,
j))
{
GNUNET_break_op (0);
mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
mr.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
default:
GNUNET_break_op (0); GNUNET_break_op (0);
mr.hr.http_status = 0; mr.hr.http_status = 0;
mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; mr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
mr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
} }
break; break;
@ -456,7 +310,6 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
json_t *melt_obj; json_t *melt_obj;
CURL *eh; CURL *eh;
struct GNUNET_CURL_Context *ctx; struct GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendSignatureP confirm_sig;
char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
struct TALER_DenominationHashP h_denom_pub; struct TALER_DenominationHashP h_denom_pub;
struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len]; struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len];
@ -480,7 +333,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
&h_denom_pub, &h_denom_pub,
mh->md.melted_coin.h_age_commitment, mh->md.melted_coin.h_age_commitment,
&mh->md.melted_coin.coin_priv, &mh->md.melted_coin.coin_priv,
&confirm_sig); &mh->coin_sig);
GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv,
&mh->coin_pub.eddsa_pub); &mh->coin_pub.eddsa_pub);
melt_obj = GNUNET_JSON_PACK ( melt_obj = GNUNET_JSON_PACK (
@ -489,7 +342,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
TALER_JSON_pack_denom_sig ("denom_sig", TALER_JSON_pack_denom_sig ("denom_sig",
&mh->md.melted_coin.sig), &mh->md.melted_coin.sig),
GNUNET_JSON_pack_data_auto ("confirm_sig", GNUNET_JSON_pack_data_auto ("confirm_sig",
&confirm_sig), &mh->coin_sig),
TALER_JSON_pack_amount ("value_with_fee", TALER_JSON_pack_amount ("value_with_fee",
&mh->md.melted_coin.melt_amount_with_fee), &mh->md.melted_coin.melt_amount_with_fee),
GNUNET_JSON_pack_data_auto ("rc", GNUNET_JSON_pack_data_auto ("rc",

View File

@ -34,6 +34,33 @@
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
/**
* Information we track per deposited coin.
*/
struct Deposit
{
/**
* Coin's public key.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Signature made with the coin.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/**
* Coin's denomination.
*/
struct TALER_DenominationHashP h_denom_pub;
/**
* How much did we say the coin contributed.
*/
struct TALER_Amount contribution;
};
/** /**
* @brief A purse create with deposit handle * @brief A purse create with deposit handle
*/ */
@ -106,6 +133,16 @@ struct TALER_EXCHANGE_PurseCreateDepositHandle
*/ */
struct GNUNET_TIME_Timestamp purse_expiration; struct GNUNET_TIME_Timestamp purse_expiration;
/**
* Array of @e num_deposit deposits.
*/
struct Deposit *deposits;
/**
* How many deposits did we make?
*/
unsigned int num_deposits;
}; };
@ -128,8 +165,10 @@ handle_purse_create_deposit_finished (void *cls,
.hr.reply = j, .hr.reply = j,
.hr.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
const struct TALER_EXCHANGE_Keys *keys;
pch->job = NULL; pch->job = NULL;
keys = TALER_EXCHANGE_get_keys (pch->exchange);
switch (response_code) switch (response_code)
{ {
case 0: case 0:
@ -232,43 +271,151 @@ handle_purse_create_deposit_finished (void *cls,
break; break;
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
{ {
#if FIXME struct TALER_Amount left;
struct TALER_Amount total; struct TALER_CoinSpendPublicKeyP pcoin_pub;
struct TALER_DenominationHashP h_denom_pub; bool found = false;
const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL;
// FIXME: parse coin_pub
// FIXME: lookup dk for that coin from our deposits array!
if (NULL == dk)
{
/* not one of our coins */
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
if (GNUNET_OK != if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dk, TALER_EXCHANGE_check_coin_amount_conflict_ (
pch->exchange->currency, keys,
&coin_pub, j,
history, &pcoin_pub,
&h_denom_pub, &left))
&total))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
// FIXME: check total is too high for the request... for (unsigned int i = 0; i<pch->num_deposits; i++)
#endif {
struct Deposit *deposit = &pch->deposits[i];
if (0 != GNUNET_memcmp (&pcoin_pub,
&deposit->coin_pub))
continue;
if (-1 !=
TALER_amount_cmp (&left,
&deposit->contribution))
{
/* Balance was sufficient after all; recoup MAY have still been possible */
GNUNET_break_op (0);
continue;
}
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_signature_conflict_ (
j,
&deposit->coin_sig))
{
GNUNET_break_op (0);
continue;
}
found = true;
break;
}
if (! found)
{
/* conflict is for a different coin! */
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
break;
}
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
{
struct TALER_Amount left;
struct TALER_CoinSpendPublicKeyP pcoin_pub;
bool found = false;
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_amount_conflict_ (
keys,
j,
&pcoin_pub,
&left))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
for (unsigned int i = 0; i<pch->num_deposits; i++)
{
struct Deposit *deposit = &pch->deposits[i];
if (0 !=
GNUNET_memcmp (&pcoin_pub,
&deposit->coin_pub))
continue;
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_denomination_conflict_ (
j,
&deposit->h_denom_pub))
{
/* Eh, same denomination, hence no conflict */
GNUNET_break_op (0);
continue;
}
found = true;
}
if (! found)
{
/* conflict is for a different coin! */
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
/* meta data conflict is real! */
break; break;
} }
case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA: case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
{ {
// FIXME! struct TALER_CoinSpendPublicKeyP coin_pub;
break; struct TALER_CoinSpendSignatureP coin_sig;
bool found = false;
if (GNUNET_OK !=
TALER_EXCHANGE_check_purse_coin_conflict_ (
&pch->purse_pub,
pch->exchange->url,
j,
&coin_pub,
&coin_sig))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
for (unsigned int i = 0; i<pch->num_deposits; i++)
{
struct Deposit *deposit = &pch->deposits[i];
if (0 !=
GNUNET_memcmp (&coin_pub,
&deposit->coin_pub))
continue;
if (0 ==
GNUNET_memcmp (&coin_sig,
&deposit->coin_sig))
{
GNUNET_break_op (0);
continue;
}
found = true;
break;
}
if (! found)
{
/* conflict is for a different coin! */
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
} }
case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA: case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:
if (GNUNET_OK != if (GNUNET_OK !=
@ -408,6 +555,9 @@ TALER_EXCHANGE_purse_create_with_deposit (
GNUNET_free (pch); GNUNET_free (pch);
return NULL; return NULL;
} }
pch->num_deposits = num_deposits;
pch->deposits = GNUNET_new_array (num_deposits,
struct Deposit);
deposit_arr = json_array (); deposit_arr = json_array ();
GNUNET_assert (NULL != deposit_arr); GNUNET_assert (NULL != deposit_arr);
url = TEAH_path_to_url (exchange, url = TEAH_path_to_url (exchange,
@ -418,9 +568,8 @@ TALER_EXCHANGE_purse_create_with_deposit (
for (unsigned int i = 0; i<num_deposits; i++) for (unsigned int i = 0; i<num_deposits; i++)
{ {
const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
struct Deposit *d = &pch->deposits[i];
json_t *jdeposit; json_t *jdeposit;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
#if FIXME_OEC #if FIXME_OEC
struct TALER_AgeCommitmentHash agh; struct TALER_AgeCommitmentHash agh;
struct TALER_AgeCommitmentHash *aghp = NULL; struct TALER_AgeCommitmentHash *aghp = NULL;
@ -441,14 +590,16 @@ TALER_EXCHANGE_purse_create_with_deposit (
return NULL; return NULL;
} }
#endif #endif
d->contribution = deposit->amount;
d->h_denom_pub = deposit->h_denom_pub;
GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub); &d->coin_pub.eddsa_pub);
TALER_wallet_purse_deposit_sign ( TALER_wallet_purse_deposit_sign (
url, url,
&pch->purse_pub, &pch->purse_pub,
&deposit->amount, &deposit->amount,
&deposit->coin_priv, &deposit->coin_priv,
&coin_sig); &d->coin_sig);
jdeposit = GNUNET_JSON_PACK ( jdeposit = GNUNET_JSON_PACK (
#if FIXME_OEC #if FIXME_OEC
GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_allow_null (
@ -465,9 +616,9 @@ TALER_EXCHANGE_purse_create_with_deposit (
TALER_JSON_pack_denom_sig ("ub_sig", TALER_JSON_pack_denom_sig ("ub_sig",
&deposit->denom_sig), &deposit->denom_sig),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig), &d->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_pub", GNUNET_JSON_pack_data_auto ("coin_pub",
&coin_pub)); &d->coin_pub));
GNUNET_assert (0 == GNUNET_assert (0 ==
json_array_append_new (deposit_arr, json_array_append_new (deposit_arr,
jdeposit)); jdeposit));
@ -529,6 +680,7 @@ TALER_EXCHANGE_purse_create_with_deposit (
curl_easy_cleanup (eh); curl_easy_cleanup (eh);
json_decref (create_obj); json_decref (create_obj);
GNUNET_free (pch->econtract.econtract); GNUNET_free (pch->econtract.econtract);
GNUNET_free (pch->deposits);
GNUNET_free (pch->url); GNUNET_free (pch->url);
GNUNET_free (pch); GNUNET_free (pch);
return NULL; return NULL;
@ -558,6 +710,7 @@ TALER_EXCHANGE_purse_create_with_deposit_cancel (
} }
GNUNET_free (pch->econtract.econtract); GNUNET_free (pch->econtract.econtract);
GNUNET_free (pch->url); GNUNET_free (pch->url);
GNUNET_free (pch->deposits);
TALER_curl_easy_post_finished (&pch->ctx); TALER_curl_easy_post_finished (&pch->ctx);
GNUNET_free (pch); GNUNET_free (pch);
} }

View File

@ -28,6 +28,7 @@
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchange_service.h" #include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
@ -43,6 +44,11 @@ struct Coin
*/ */
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Signature made with the coin.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/** /**
* Coin's denomination. * Coin's denomination.
*/ */
@ -226,30 +232,17 @@ handle_purse_deposit_finished (void *cls,
{ {
case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA: case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
{ {
const char *partner_url = NULL;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_CoinSpendSignatureP coin_sig; struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_Amount amount;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin_sig),
GNUNET_JSON_spec_fixed_auto ("coin_pub",
&coin_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("partner_url",
&partner_url),
NULL),
TALER_JSON_spec_amount ("amount",
keys->currency,
&amount),
GNUNET_JSON_spec_end ()
};
bool found = false; bool found = false;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (j, TALER_EXCHANGE_check_purse_coin_conflict_ (
spec, &pch->purse_pub,
NULL, NULL)) pch->base_url,
j,
&coin_pub,
&coin_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
@ -257,29 +250,21 @@ handle_purse_deposit_finished (void *cls,
break; break;
} }
for (unsigned int i = 0; i<pch->num_deposits; i++) for (unsigned int i = 0; i<pch->num_deposits; i++)
{
if (0 == GNUNET_memcmp (&coin_pub, if (0 == GNUNET_memcmp (&coin_pub,
&pch->coins[i].coin_pub)) &pch->coins[i].coin_pub))
{ {
if (0 == GNUNET_memcmp (&coin_sig,
&pch->coins[i].coin_sig))
{
/* identical signature => not a conflict */
continue;
}
found = true; found = true;
break; break;
} }
if (! found)
{
/* proof is about a coin we did not even deposit */
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
} }
if (NULL == partner_url) if (! found)
partner_url = pch->base_url;
if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify (
partner_url,
&pch->purse_pub,
&amount,
&coin_pub,
&coin_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
@ -291,27 +276,18 @@ handle_purse_deposit_finished (void *cls,
} }
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
{ {
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct GNUNET_JSON_Specification spec[] = { struct TALER_Amount remaining;
GNUNET_JSON_spec_fixed_auto ("coin_pub",
&coin_pub),
GNUNET_JSON_spec_json ("history",
&history),
GNUNET_JSON_spec_end ()
};
bool found = false; bool found = false;
const struct Coin *my_coin; const struct Coin *my_coin;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (j, TALER_EXCHANGE_check_coin_amount_conflict_ (
spec, keys,
NULL, NULL)) j,
&coin_pub,
&remaining))
{ {
GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
@ -334,45 +310,22 @@ handle_purse_deposit_finished (void *cls,
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
dki = TALER_EXCHANGE_get_denomination_key_by_hash ( if (1 == TALER_amount_cmp (&remaining,
keys, &my_coin->contribution))
&my_coin->h_denom_pub);
if (NULL == dki)
{
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
GNUNET_break_op (0);
break;
}
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki,
dki->value.currency,
&coin_pub,
history,
&h_denom_pub,
&total))
{ {
/* transaction should have still fit */
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
json_decref (history);
break; break;
} }
json_decref (history); if (GNUNET_OK !=
if (0 > TALER_EXCHANGE_check_coin_signature_conflict_ (
TALER_amount_add (&total, j,
&total, &my_coin->coin_sig))
&my_coin->contribution))
{ {
/* clearly not OK if our transaction would have caused /* THIS transaction must not be in the conflicting history */
the overflow... */ GNUNET_break_op (0);
break;
}
if (0 >= TALER_amount_cmp (&total,
&dki->value))
{
/* transaction should have still fit */
GNUNET_break (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
@ -382,27 +335,18 @@ handle_purse_deposit_finished (void *cls,
} }
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
{ {
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct Coin *my_coin;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
struct GNUNET_JSON_Specification spec[] = { struct TALER_Amount remaining;
GNUNET_JSON_spec_fixed_auto ("coin_pub",
&coin_pub),
GNUNET_JSON_spec_json ("history",
&history),
GNUNET_JSON_spec_end ()
};
bool found = false; bool found = false;
const struct Coin *my_coin;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (j, TALER_EXCHANGE_check_coin_amount_conflict_ (
spec, keys,
NULL, NULL)) j,
&coin_pub,
&remaining))
{ {
GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
@ -425,31 +369,12 @@ handle_purse_deposit_finished (void *cls,
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
dki = TALER_EXCHANGE_get_denomination_key_by_hash (
keys,
&my_coin->h_denom_pub);
memset (&h_denom_pub,
0,
sizeof (h_denom_pub));
if (GNUNET_OK != if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki, TALER_EXCHANGE_check_coin_denomination_conflict_ (
dki->value.currency, j,
&coin_pub, &my_coin->h_denom_pub))
history,
&h_denom_pub,
&total))
{ {
GNUNET_break_op (0); /* no conflicting denomination detected */
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
json_decref (history);
break;
}
json_decref (history);
if (0 == GNUNET_memcmp (&dki->h_key,
&h_denom_pub))
{
/* sorry, this proves nothing */
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
@ -562,7 +487,6 @@ TALER_EXCHANGE_purse_deposit (
const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
struct Coin *coin = &pch->coins[i]; struct Coin *coin = &pch->coins[i];
json_t *jdeposit; json_t *jdeposit;
struct TALER_CoinSpendSignatureP coin_sig;
#if FIXME_OEC #if FIXME_OEC
struct TALER_AgeCommitmentHash agh; struct TALER_AgeCommitmentHash agh;
struct TALER_AgeCommitmentHash *aghp = NULL; struct TALER_AgeCommitmentHash *aghp = NULL;
@ -593,7 +517,7 @@ TALER_EXCHANGE_purse_deposit (
&pch->purse_pub, &pch->purse_pub,
&deposit->amount, &deposit->amount,
&deposit->coin_priv, &deposit->coin_priv,
&coin_sig); &coin->coin_sig);
jdeposit = GNUNET_JSON_PACK ( jdeposit = GNUNET_JSON_PACK (
#if FIXME_OEC #if FIXME_OEC
GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_allow_null (
@ -612,7 +536,7 @@ TALER_EXCHANGE_purse_deposit (
GNUNET_JSON_pack_data_auto ("coin_pub", GNUNET_JSON_pack_data_auto ("coin_pub",
&coin->coin_pub), &coin->coin_pub),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig)); &coin->coin_sig));
GNUNET_assert (0 == GNUNET_assert (0 ==
json_array_append_new (deposit_arr, json_array_append_new (deposit_arr,
jdeposit)); jdeposit));

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2017-2021 Taler Systems SA Copyright (C) 2017-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the 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 terms of the GNU General Public License as published by the Free Software
@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchange_service.h" #include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
@ -59,6 +60,11 @@ struct TALER_EXCHANGE_RecoupHandle
*/ */
struct TALER_EXCHANGE_DenomPublicKey pk; struct TALER_EXCHANGE_DenomPublicKey pk;
/**
* Our signature requesting the recoup.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/** /**
* Handle for the request. * Handle for the request.
*/ */
@ -139,8 +145,10 @@ handle_recoup_finished (void *cls,
.reply = j, .reply = j,
.http_status = (unsigned int) response_code .http_status = (unsigned int) response_code
}; };
const struct TALER_EXCHANGE_Keys *keys;
ph->job = NULL; ph->job = NULL;
keys = TALER_EXCHANGE_get_keys (ph->exchange);
switch (response_code) switch (response_code)
{ {
case 0: case 0:
@ -166,76 +174,34 @@ handle_recoup_finished (void *cls,
break; break;
case MHD_HTTP_CONFLICT: case MHD_HTTP_CONFLICT:
{ {
/* Insufficient funds, proof attached */ struct TALER_Amount min_key;
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
enum TALER_ErrorCode ec;
dki = &ph->pk; hr.ec = TALER_JSON_get_error_code (j);
history = json_object_get (j, hr.hint = TALER_JSON_get_error_hint (j);
"history");
if (GNUNET_OK != if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki, TALER_EXCHANGE_get_min_denomination_ (keys,
dki->fees.deposit.currency, &min_key))
&ph->coin_pub,
history,
&h_denom_pub,
&total))
{ {
GNUNET_break_op (0); GNUNET_break (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
}
else
{
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
}
ec = TALER_JSON_get_error_code (j);
switch (ec)
{
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
if (0 > TALER_amount_cmp (&total,
&dki->value))
{
/* recoup MAY have still been possible */
/* FIXME: This code may falsely complain, as we do not
know that the smallest denomination offered by the
exchange is here. We should look at the key
structure of ph->exchange, and find the smallest
_currently withdrawable_ denomination and check
if the value remaining would suffice... */
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
break;
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
if (0 == GNUNET_memcmp (&ph->pk.h_key,
&h_denom_pub))
{
/* invalid proof provided */
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
/* valid error from exchange */
break;
default:
GNUNET_break_op (0);
hr.http_status = 0; hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
ph->cb (ph->cb_cls, if (GNUNET_OK !=
&hr, TALER_EXCHANGE_check_coin_conflict_ (
NULL); keys,
TALER_EXCHANGE_recoup_cancel (ph); j,
return; &ph->pk,
&ph->coin_pub,
&ph->coin_sig,
&min_key))
{
GNUNET_break (0);
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
hr.http_status = 0;
break;
}
break;
} }
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
/* Nothing really to verify, exchange says one of the signatures is /* Nothing really to verify, exchange says one of the signatures is
@ -291,8 +257,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
{ {
struct TALER_EXCHANGE_RecoupHandle *ph; struct TALER_EXCHANGE_RecoupHandle *ph;
struct GNUNET_CURL_Context *ctx; struct GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_DenominationHashP h_denom_pub; struct TALER_DenominationHashP h_denom_pub;
json_t *recoup_obj; json_t *recoup_obj;
CURL *eh; CURL *eh;
@ -302,7 +266,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange)); TEAH_handle_is_ready (exchange));
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
TALER_planchet_setup_coin_priv (ps, TALER_planchet_setup_coin_priv (ps,
exchange_vals, exchange_vals,
&coin_priv); &coin_priv);
@ -310,13 +274,13 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
exchange_vals, exchange_vals,
&bks); &bks);
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
&coin_pub.eddsa_pub); &ph->coin_pub.eddsa_pub);
TALER_denom_pub_hash (&pk->key, TALER_denom_pub_hash (&pk->key,
&h_denom_pub); &h_denom_pub);
TALER_wallet_recoup_sign (&h_denom_pub, TALER_wallet_recoup_sign (&h_denom_pub,
&bks, &bks,
&coin_priv, &coin_priv,
&coin_sig); &ph->coin_sig);
recoup_obj = GNUNET_JSON_PACK ( recoup_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("denom_pub_hash", GNUNET_JSON_pack_data_auto ("denom_pub_hash",
&h_denom_pub), &h_denom_pub),
@ -325,7 +289,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
TALER_JSON_pack_exchange_withdraw_values ("ewv", TALER_JSON_pack_exchange_withdraw_values ("ewv",
exchange_vals), exchange_vals),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig), &ph->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks)); &bks));
if (TALER_DENOMINATION_CS == denom_sig->cipher) if (TALER_DENOMINATION_CS == denom_sig->cipher)
@ -352,7 +316,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
char *end; char *end;
end = GNUNET_STRINGS_data_to_string ( end = GNUNET_STRINGS_data_to_string (
&coin_pub, &ph->coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP), sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str, pub_str,
sizeof (pub_str)); sizeof (pub_str));
@ -363,8 +327,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
pub_str); pub_str);
} }
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
ph->coin_pub = coin_pub;
ph->exchange = exchange; ph->exchange = exchange;
ph->pk = *pk; ph->pk = *pk;
memset (&ph->pk.key, memset (&ph->pk.key,

View File

@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_exchange_service.h" #include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
@ -79,6 +80,11 @@ struct TALER_EXCHANGE_RecoupRefreshHandle
*/ */
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Signature affirming the recoup-refresh operation.
*/
struct TALER_CoinSpendSignatureP coin_sig;
}; };
@ -140,8 +146,10 @@ handle_recoup_refresh_finished (void *cls,
.reply = j, .reply = j,
.http_status = (unsigned int) response_code .http_status = (unsigned int) response_code
}; };
const struct TALER_EXCHANGE_Keys *keys;
ph->job = NULL; ph->job = NULL;
keys = TALER_EXCHANGE_get_keys (ph->exchange);
switch (response_code) switch (response_code)
{ {
case 0: case 0:
@ -180,76 +188,34 @@ handle_recoup_refresh_finished (void *cls,
break; break;
case MHD_HTTP_CONFLICT: case MHD_HTTP_CONFLICT:
{ {
/* Insufficient funds, proof attached */ struct TALER_Amount min_key;
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
enum TALER_ErrorCode ec;
dki = &ph->pk; hr.ec = TALER_JSON_get_error_code (j);
history = json_object_get (j, hr.hint = TALER_JSON_get_error_hint (j);
"history");
if (GNUNET_OK != if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki, TALER_EXCHANGE_get_min_denomination_ (keys,
dki->fees.deposit.currency, &min_key))
&ph->coin_pub,
history,
&h_denom_pub,
&total))
{ {
GNUNET_break_op (0); GNUNET_break (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
}
else
{
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
}
ec = TALER_JSON_get_error_code (j);
switch (ec)
{
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
if (0 > TALER_amount_cmp (&total,
&dki->value))
{
/* recoup MAY have still been possible */
/* FIXME: This code may falsely complain, as we do not
know that the smallest denomination offered by the
exchange is here. We should look at the key
structure of ph->exchange, and find the smallest
_currently withdrawable_ denomination and check
if the value remaining would suffice... */
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
break;
case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
if (0 == GNUNET_memcmp (&ph->pk.h_key,
&h_denom_pub))
{
/* invalid proof provided */
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
/* valid error from exchange */
break;
default:
GNUNET_break_op (0);
hr.http_status = 0; hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
ph->cb (ph->cb_cls, if (GNUNET_OK !=
&hr, TALER_EXCHANGE_check_coin_conflict_ (
NULL); keys,
TALER_EXCHANGE_recoup_refresh_cancel (ph); j,
return; &ph->pk,
&ph->coin_pub,
&ph->coin_sig,
&min_key))
{
GNUNET_break (0);
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
hr.http_status = 0;
break;
}
break;
} }
case MHD_HTTP_GONE: case MHD_HTTP_GONE:
/* Kind of normal: the money was already sent to the merchant /* Kind of normal: the money was already sent to the merchant
@ -295,8 +261,6 @@ TALER_EXCHANGE_recoup_refresh (
{ {
struct TALER_EXCHANGE_RecoupRefreshHandle *ph; struct TALER_EXCHANGE_RecoupRefreshHandle *ph;
struct GNUNET_CURL_Context *ctx; struct GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_DenominationHashP h_denom_pub; struct TALER_DenominationHashP h_denom_pub;
json_t *recoup_obj; json_t *recoup_obj;
CURL *eh; CURL *eh;
@ -307,6 +271,14 @@ TALER_EXCHANGE_recoup_refresh (
GNUNET_assert (NULL != recoup_cb); GNUNET_assert (NULL != recoup_cb);
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange)); TEAH_handle_is_ready (exchange));
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle);
ph->exchange = exchange;
ph->pk = *pk;
memset (&ph->pk.key,
0,
sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */
ph->cb = recoup_cb;
ph->cb_cls = recoup_cb_cls;
TALER_planchet_setup_coin_priv (ps, TALER_planchet_setup_coin_priv (ps,
exchange_vals, exchange_vals,
&coin_priv); &coin_priv);
@ -314,13 +286,13 @@ TALER_EXCHANGE_recoup_refresh (
exchange_vals, exchange_vals,
&bks); &bks);
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
&coin_pub.eddsa_pub); &ph->coin_pub.eddsa_pub);
TALER_denom_pub_hash (&pk->key, TALER_denom_pub_hash (&pk->key,
&h_denom_pub); &h_denom_pub);
TALER_wallet_recoup_refresh_sign (&h_denom_pub, TALER_wallet_recoup_refresh_sign (&h_denom_pub,
&bks, &bks,
&coin_priv, &coin_priv,
&coin_sig); &ph->coin_sig);
recoup_obj = GNUNET_JSON_PACK ( recoup_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("denom_pub_hash", GNUNET_JSON_pack_data_auto ("denom_pub_hash",
&h_denom_pub), &h_denom_pub),
@ -329,7 +301,7 @@ TALER_EXCHANGE_recoup_refresh (
TALER_JSON_pack_exchange_withdraw_values ("ewv", TALER_JSON_pack_exchange_withdraw_values ("ewv",
exchange_vals), exchange_vals),
GNUNET_JSON_pack_data_auto ("coin_sig", GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig), &ph->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret", GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks)); &bks));
@ -358,7 +330,7 @@ TALER_EXCHANGE_recoup_refresh (
char *end; char *end;
end = GNUNET_STRINGS_data_to_string ( end = GNUNET_STRINGS_data_to_string (
&coin_pub, &ph->coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP), sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str, pub_str,
sizeof (pub_str)); sizeof (pub_str));
@ -369,15 +341,6 @@ TALER_EXCHANGE_recoup_refresh (
pub_str); pub_str);
} }
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle);
ph->coin_pub = coin_pub;
ph->exchange = exchange;
ph->pk = *pk;
memset (&ph->pk.key,
0,
sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */
ph->cb = recoup_cb;
ph->cb_cls = recoup_cb_cls;
ph->url = TEAH_path_to_url (exchange, ph->url = TEAH_path_to_url (exchange,
arg_str); arg_str);
if (NULL == ph->url) if (NULL == ph->url)

View File

@ -173,9 +173,12 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
const json_t *json) const json_t *json)
{ {
json_t *history; json_t *history;
struct TALER_DenominationHashP h_denom_pub;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("history", GNUNET_JSON_spec_json ("history",
&history), &history),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&h_denom_pub),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
size_t len; size_t len;
@ -234,7 +237,6 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
struct TALER_AgeCommitmentHash h_age_commitment; struct TALER_AgeCommitmentHash h_age_commitment;
bool no_hac; bool no_hac;
// struct TALER_ExtensionContractHashP h_extensions; // FIXME! // struct TALER_ExtensionContractHashP h_extensions; // FIXME!
struct TALER_DenominationHashP h_denom_pub;
struct GNUNET_TIME_Timestamp wallet_timestamp; struct GNUNET_TIME_Timestamp wallet_timestamp;
struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_TIME_Timestamp refund_deadline; struct GNUNET_TIME_Timestamp refund_deadline;
@ -246,8 +248,6 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
&h_contract_terms), &h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_wire", GNUNET_JSON_spec_fixed_auto ("h_wire",
&h_wire), &h_wire),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&h_denom_pub),
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment", GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&h_age_commitment), &h_age_commitment),
@ -429,24 +429,22 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
} }
} }
if (have_refund)
{ {
if (have_refund) if (0 >
TALER_amount_add (&rtotal,
&rtotal,
&rh->refund_amount))
{ {
if (0 > GNUNET_break (0);
TALER_amount_add (&rtotal, GNUNET_JSON_parse_free (spec);
&rtotal, return GNUNET_SYSERR;
&rh->refund_amount))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
}
else
{
rtotal = rh->refund_amount;
} }
} }
else
{
rtotal = rh->refund_amount;
}
if (-1 == TALER_amount_cmp (&dtotal, if (-1 == TALER_amount_cmp (&dtotal,
&rtotal)) &rtotal))
{ {

View File

@ -257,7 +257,7 @@ deposit_cb (void *cls,
__LINE__); __LINE__);
json_dumpf (dr->hr.reply, json_dumpf (dr->hr.reply,
stderr, stderr,
0); JSON_INDENT (2));
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }

View File

@ -145,7 +145,7 @@ deposit_cb (void *cls,
__LINE__); __LINE__);
json_dumpf (dr->hr.reply, json_dumpf (dr->hr.reply,
stderr, stderr,
0); JSON_INDENT (2));
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }