complete P2P/W2W conflict handling, deduplicate code across handlers
This commit is contained in:
parent
6c81796d6f
commit
b9963f7525
@ -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 ();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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",
|
||||
×tamp),
|
||||
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",
|
||||
×tamp),
|
||||
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 */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user