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;
}
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 ();

View File

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

View File

@ -1,6 +1,6 @@
/*
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
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)
{
/* FIXME: conficting contract != insufficient funds */
*mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
&dc->deposit->coin.denom_pub_hash,
&dc->deposit->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}
@ -191,6 +193,7 @@ deposit_transaction (void *cls,
= TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
&dc->deposit->coin.denom_pub_hash,
&dc->deposit->coin.coin_pub);
return GNUNET_DB_STATUS_HARD_ERROR;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -113,6 +113,7 @@ TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
*
* @param connection connection to the client
* @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
* @return MHD result code
*/
@ -120,6 +121,7 @@ MHD_RESULT
TEH_RESPONSE_reply_coin_insufficient_funds (
struct MHD_Connection *connection,
enum TALER_ErrorCode ec,
const struct TALER_DenominationHashP *h_denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub);

View File

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

View File

@ -2978,21 +2978,17 @@ TALER_EXCHANGE_deposits_get_cancel (
* Convenience function. Verifies a coin's transaction history as
* returned by the exchange.
*
* @param dk fee structure for the coin, NULL to skip verifying fees
* @param currency expected currency for the coin
* @param dk fee structure for the coin
* @param coin_pub public key of the coin
* @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
* @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_coin_history (
const struct TALER_EXCHANGE_DenomPublicKey *dk,
const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history,
struct TALER_DenominationHashP *h_denom_pub,
struct TALER_Amount *total);

View File

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

View File

@ -22,6 +22,7 @@
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_common.h"
#include "exchange_api_handle.h"
#include "taler_signatures.h"
@ -684,11 +685,6 @@ struct CoinHistoryParseContext
*/
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.
*/
@ -749,8 +745,6 @@ help_deposit (struct CoinHistoryParseContext *pc,
&h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&h_wire),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&hac),
@ -784,7 +778,7 @@ help_deposit (struct CoinHistoryParseContext *pc,
&h_contract_terms,
no_hac ? NULL : &hac,
NULL /* h_extensions! */,
pc->h_denom_pub,
&pc->dk->h_key,
wallet_timestamp,
&merchant_pub,
refund_deadline,
@ -836,8 +830,6 @@ help_melt (struct CoinHistoryParseContext *pc,
&sig),
GNUNET_JSON_spec_fixed_auto ("rc",
&rc),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
&h_age_commitment),
@ -876,7 +868,7 @@ help_melt (struct CoinHistoryParseContext *pc,
amount,
&melt_fee,
&rc,
pc->h_denom_pub,
&pc->dk->h_key,
no_hac
? NULL
: &h_age_commitment,
@ -1008,8 +1000,6 @@ help_recoup (struct CoinHistoryParseContext *pc,
&coin_sig),
GNUNET_JSON_spec_fixed_auto ("coin_blind",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp),
GNUNET_JSON_spec_end ()
@ -1036,7 +1026,7 @@ help_recoup (struct CoinHistoryParseContext *pc,
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_wallet_recoup_verify (pc->h_denom_pub,
TALER_wallet_recoup_verify (&pc->dk->h_key,
&coin_bks,
pc->coin_pub,
&coin_sig))
@ -1081,8 +1071,6 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
&old_coin_pub),
GNUNET_JSON_spec_fixed_auto ("coin_blind",
&coin_bks),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
pc->h_denom_pub),
GNUNET_JSON_spec_timestamp ("timestamp",
&timestamp),
GNUNET_JSON_spec_end ()
@ -1109,7 +1097,7 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_wallet_recoup_verify (pc->h_denom_pub,
TALER_wallet_recoup_verify (&pc->dk->h_key,
&coin_bks,
pc->coin_pub,
&coin_sig))
@ -1250,12 +1238,11 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_verify_coin_history (
const struct TALER_EXCHANGE_DenomPublicKey *dk,
const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history,
struct TALER_DenominationHashP *h_denom_pub,
struct TALER_Amount *total)
{
const char *currency = dk->value.currency;
const struct
{
const char *type;
@ -1273,8 +1260,7 @@ TALER_EXCHANGE_verify_coin_history (
struct CoinHistoryParseContext pc = {
.dk = dk,
.coin_pub = coin_pub,
.total = total,
.h_denom_pub = h_denom_pub
.total = total
};
size_t len;
@ -1528,11 +1514,10 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
{
struct TALER_PurseMergeSignatureP merge_sig;
struct GNUNET_TIME_Timestamp merge_timestamp;
const char *partner_url = exchange_url;
const char *partner_url = NULL;
struct TALER_ReservePublicKeyP reserve_pub;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
// FIXME: partner_url or partner_base_url?
GNUNET_JSON_spec_string ("partner_url",
&partner_url),
NULL),
@ -1554,6 +1539,8 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (NULL == partner_url)
partner_url = exchange_url;
payto_uri = make_payto (partner_url,
&reserve_pub);
if (GNUNET_OK !=
@ -1581,18 +1568,55 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
}
/**
* Check proof of a contract conflict.
*
* DESIGN-FIXME: this 'proof' doesn't really proof a conflict!
*
* @param ccontract_sig conflicting signature (must
* not match the signature from the proof)
* @param purse_pub public key of the purse
* @param exchange_url the base URL of this exchange
* @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
*/
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)
{
const char *partner_url = NULL;
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_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
TALER_EXCHANGE_check_purse_econtract_conflict_ (
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 */

View File

@ -23,6 +23,7 @@
#define EXCHANGE_API_COMMON_H
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
/**
@ -33,7 +34,7 @@
* @param purse_pub the public key (must match
* the signature from the proof)
* @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
TALER_EXCHANGE_check_purse_create_conflict_ (
@ -51,7 +52,7 @@ TALER_EXCHANGE_check_purse_create_conflict_ (
* the signature from the proof)
* @param exchange_url the base URL of this exchange
* @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
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
* not match the signature from the proof)
* @param purse_pub public key of the purse
* @param exchange_url the base URL of this exchange
* @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
TALER_EXCHANGE_check_purse_econtract_conflict_ (
@ -81,4 +104,94 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (
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

View File

@ -29,6 +29,7 @@
#include "taler_json_lib.h"
#include "taler_auditor_service.h"
#include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "exchange_api_curl_defaults.h"
@ -128,6 +129,11 @@ struct TALER_EXCHANGE_DepositHandle
*/
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 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
* HTTP /deposit request.
@ -316,8 +251,10 @@ handle_deposit_finished (void *cls,
.hr.reply = j,
.hr.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
dh->job = NULL;
keys = TALER_EXCHANGE_get_keys (dh->exchange);
switch (response_code)
{
case 0:
@ -409,19 +346,21 @@ handle_deposit_finished (void *cls,
happen, we should pass the JSON reply to the application */
break;
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 !=
verify_deposit_signature_conflict (dh,
j))
TALER_EXCHANGE_check_coin_conflict_ (
keys,
j,
&dh->dki,
&dh->coin_pub,
&dh->coin_sig,
&dh->amount_with_fee))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
}
else
{
dr.hr.ec = TALER_JSON_get_error_code (j);
dr.hr.hint = TALER_JSON_get_error_hint (j);
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
break;
case MHD_HTTP_GONE:
@ -692,6 +631,8 @@ TALER_EXCHANGE_deposit (
dh->exchange = exchange;
dh->cb = cb;
dh->cb_cls = cb_cls;
dh->coin_sig = *coin_sig;
dh->coin_pub = *coin_pub;
dh->url = TEAH_path_to_url (exchange,
arg_str);
if (NULL == dh->url)

View File

@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "exchange_api_curl_defaults.h"
@ -101,6 +102,11 @@ struct TALER_EXCHANGE_MeltHandle
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Signature affirming the melt.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/**
* @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
* HTTP /coins/$COIN_PUB/melt request.
@ -339,8 +208,10 @@ handle_melt_finished (void *cls,
.hr.reply = j,
.hr.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
mh->job = NULL;
keys = TALER_EXCHANGE_get_keys (mh->exchange);
switch (response_code)
{
case 0:
@ -372,36 +243,19 @@ handle_melt_finished (void *cls,
break;
case MHD_HTTP_CONFLICT:
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);
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);
mr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
break;
@ -456,7 +310,6 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
json_t *melt_obj;
CURL *eh;
struct GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendSignatureP confirm_sig;
char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
struct TALER_DenominationHashP h_denom_pub;
struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len];
@ -480,7 +333,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
&h_denom_pub,
mh->md.melted_coin.h_age_commitment,
&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,
&mh->coin_pub.eddsa_pub);
melt_obj = GNUNET_JSON_PACK (
@ -489,7 +342,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
TALER_JSON_pack_denom_sig ("denom_sig",
&mh->md.melted_coin.sig),
GNUNET_JSON_pack_data_auto ("confirm_sig",
&confirm_sig),
&mh->coin_sig),
TALER_JSON_pack_amount ("value_with_fee",
&mh->md.melted_coin.melt_amount_with_fee),
GNUNET_JSON_pack_data_auto ("rc",

View File

@ -34,6 +34,33 @@
#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
*/
@ -106,6 +133,16 @@ struct TALER_EXCHANGE_PurseCreateDepositHandle
*/
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.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
pch->job = NULL;
keys = TALER_EXCHANGE_get_keys (pch->exchange);
switch (response_code)
{
case 0:
@ -232,43 +271,151 @@ handle_purse_create_deposit_finished (void *cls,
break;
case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
{
#if FIXME
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL;
struct TALER_Amount left;
struct TALER_CoinSpendPublicKeyP pcoin_pub;
bool found = false;
// 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 !=
TALER_EXCHANGE_verify_coin_history (dk,
pch->exchange->currency,
&coin_pub,
history,
&h_denom_pub,
&total))
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;
}
// FIXME: check total is too high for the request...
#endif
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 (-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;
}
case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
{
// FIXME!
break;
struct TALER_CoinSpendPublicKeyP coin_pub;
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:
if (GNUNET_OK !=
@ -408,6 +555,9 @@ TALER_EXCHANGE_purse_create_with_deposit (
GNUNET_free (pch);
return NULL;
}
pch->num_deposits = num_deposits;
pch->deposits = GNUNET_new_array (num_deposits,
struct Deposit);
deposit_arr = json_array ();
GNUNET_assert (NULL != deposit_arr);
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++)
{
const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
struct Deposit *d = &pch->deposits[i];
json_t *jdeposit;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
#if FIXME_OEC
struct TALER_AgeCommitmentHash agh;
struct TALER_AgeCommitmentHash *aghp = NULL;
@ -441,14 +590,16 @@ TALER_EXCHANGE_purse_create_with_deposit (
return NULL;
}
#endif
d->contribution = deposit->amount;
d->h_denom_pub = deposit->h_denom_pub;
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 (
url,
&pch->purse_pub,
&deposit->amount,
&deposit->coin_priv,
&coin_sig);
&d->coin_sig);
jdeposit = GNUNET_JSON_PACK (
#if FIXME_OEC
GNUNET_JSON_pack_allow_null (
@ -465,9 +616,9 @@ TALER_EXCHANGE_purse_create_with_deposit (
TALER_JSON_pack_denom_sig ("ub_sig",
&deposit->denom_sig),
GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig),
&d->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_pub",
&coin_pub));
&d->coin_pub));
GNUNET_assert (0 ==
json_array_append_new (deposit_arr,
jdeposit));
@ -529,6 +680,7 @@ TALER_EXCHANGE_purse_create_with_deposit (
curl_easy_cleanup (eh);
json_decref (create_obj);
GNUNET_free (pch->econtract.econtract);
GNUNET_free (pch->deposits);
GNUNET_free (pch->url);
GNUNET_free (pch);
return NULL;
@ -558,6 +710,7 @@ TALER_EXCHANGE_purse_create_with_deposit_cancel (
}
GNUNET_free (pch->econtract.econtract);
GNUNET_free (pch->url);
GNUNET_free (pch->deposits);
TALER_curl_easy_post_finished (&pch->ctx);
GNUNET_free (pch);
}

View File

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

View File

@ -1,6 +1,6 @@
/*
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
terms of the GNU General Public License as published by the Free Software
@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "exchange_api_curl_defaults.h"
@ -59,6 +60,11 @@ struct TALER_EXCHANGE_RecoupHandle
*/
struct TALER_EXCHANGE_DenomPublicKey pk;
/**
* Our signature requesting the recoup.
*/
struct TALER_CoinSpendSignatureP coin_sig;
/**
* Handle for the request.
*/
@ -139,8 +145,10 @@ handle_recoup_finished (void *cls,
.reply = j,
.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
ph->job = NULL;
keys = TALER_EXCHANGE_get_keys (ph->exchange);
switch (response_code)
{
case 0:
@ -166,76 +174,34 @@ handle_recoup_finished (void *cls,
break;
case MHD_HTTP_CONFLICT:
{
/* Insufficient funds, proof attached */
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
enum TALER_ErrorCode ec;
struct TALER_Amount min_key;
dki = &ph->pk;
history = json_object_get (j,
"history");
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki,
dki->fees.deposit.currency,
&ph->coin_pub,
history,
&h_denom_pub,
&total))
TALER_EXCHANGE_get_min_denomination_ (keys,
&min_key))
{
GNUNET_break_op (0);
hr.http_status = 0;
GNUNET_break (0);
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.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
ph->cb (ph->cb_cls,
&hr,
NULL);
TALER_EXCHANGE_recoup_cancel (ph);
return;
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_conflict_ (
keys,
j,
&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:
/* 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 GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_DenominationHashP h_denom_pub;
json_t *recoup_obj;
CURL *eh;
@ -302,7 +266,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange));
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
TALER_planchet_setup_coin_priv (ps,
exchange_vals,
&coin_priv);
@ -310,13 +274,13 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
exchange_vals,
&bks);
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,
&h_denom_pub);
TALER_wallet_recoup_sign (&h_denom_pub,
&bks,
&coin_priv,
&coin_sig);
&ph->coin_sig);
recoup_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("denom_pub_hash",
&h_denom_pub),
@ -325,7 +289,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
TALER_JSON_pack_exchange_withdraw_values ("ewv",
exchange_vals),
GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig),
&ph->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks));
if (TALER_DENOMINATION_CS == denom_sig->cipher)
@ -352,7 +316,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
char *end;
end = GNUNET_STRINGS_data_to_string (
&coin_pub,
&ph->coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
@ -363,8 +327,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,
pub_str);
}
ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
ph->coin_pub = coin_pub;
ph->exchange = exchange;
ph->pk = *pk;
memset (&ph->pk.key,

View File

@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_json_lib.h"
#include "taler_exchange_service.h"
#include "exchange_api_common.h"
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "exchange_api_curl_defaults.h"
@ -79,6 +80,11 @@ struct TALER_EXCHANGE_RecoupRefreshHandle
*/
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,
.http_status = (unsigned int) response_code
};
const struct TALER_EXCHANGE_Keys *keys;
ph->job = NULL;
keys = TALER_EXCHANGE_get_keys (ph->exchange);
switch (response_code)
{
case 0:
@ -180,76 +188,34 @@ handle_recoup_refresh_finished (void *cls,
break;
case MHD_HTTP_CONFLICT:
{
/* Insufficient funds, proof attached */
json_t *history;
struct TALER_Amount total;
struct TALER_DenominationHashP h_denom_pub;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
enum TALER_ErrorCode ec;
struct TALER_Amount min_key;
dki = &ph->pk;
history = json_object_get (j,
"history");
hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j);
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dki,
dki->fees.deposit.currency,
&ph->coin_pub,
history,
&h_denom_pub,
&total))
TALER_EXCHANGE_get_min_denomination_ (keys,
&min_key))
{
GNUNET_break_op (0);
hr.http_status = 0;
GNUNET_break (0);
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.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
ph->cb (ph->cb_cls,
&hr,
NULL);
TALER_EXCHANGE_recoup_refresh_cancel (ph);
return;
if (GNUNET_OK !=
TALER_EXCHANGE_check_coin_conflict_ (
keys,
j,
&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:
/* 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 GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendSignatureP coin_sig;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_DenominationHashP h_denom_pub;
json_t *recoup_obj;
CURL *eh;
@ -307,6 +271,14 @@ TALER_EXCHANGE_recoup_refresh (
GNUNET_assert (NULL != recoup_cb);
GNUNET_assert (GNUNET_YES ==
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,
exchange_vals,
&coin_priv);
@ -314,13 +286,13 @@ TALER_EXCHANGE_recoup_refresh (
exchange_vals,
&bks);
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,
&h_denom_pub);
TALER_wallet_recoup_refresh_sign (&h_denom_pub,
&bks,
&coin_priv,
&coin_sig);
&ph->coin_sig);
recoup_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("denom_pub_hash",
&h_denom_pub),
@ -329,7 +301,7 @@ TALER_EXCHANGE_recoup_refresh (
TALER_JSON_pack_exchange_withdraw_values ("ewv",
exchange_vals),
GNUNET_JSON_pack_data_auto ("coin_sig",
&coin_sig),
&ph->coin_sig),
GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
&bks));
@ -358,7 +330,7 @@ TALER_EXCHANGE_recoup_refresh (
char *end;
end = GNUNET_STRINGS_data_to_string (
&coin_pub,
&ph->coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
@ -369,15 +341,6 @@ TALER_EXCHANGE_recoup_refresh (
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,
arg_str);
if (NULL == ph->url)

View File

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

View File

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

View File

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