summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-05-06 13:33:20 +0200
committerChristian Grothoff <christian@grothoff.org>2016-05-06 13:33:20 +0200
commit302070b86e8c8130f66dc00573af96005c48f7f4 (patch)
tree0cbc2a7cd015c271d4e55e32f8c1911f46aecd97
parentfbbc49bdada7195f525d5cdff03c68825eabc9a0 (diff)
downloadexchange-302070b86e8c8130f66dc00573af96005c48f7f4.tar.gz
exchange-302070b86e8c8130f66dc00573af96005c48f7f4.tar.bz2
exchange-302070b86e8c8130f66dc00573af96005c48f7f4.zip
support REFUNDS in transaction history in libtalerexchange
-rw-r--r--src/exchange-lib/exchange_api_common.c131
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c54
2 files changed, 165 insertions, 20 deletions
diff --git a/src/exchange-lib/exchange_api_common.c b/src/exchange-lib/exchange_api_common.c
index 15c73acca..6831984fc 100644
--- a/src/exchange-lib/exchange_api_common.c
+++ b/src/exchange-lib/exchange_api_common.c
@@ -40,10 +40,12 @@ int
TALER_EXCHANGE_verify_coin_history (const char *currency,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
json_t *history,
- struct TALER_Amount *total)
+ struct TALER_Amount *total)
{
size_t len;
size_t off;
+ int add;
+ struct TALER_Amount rtotal;
if (NULL == history)
{
@@ -58,6 +60,8 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
}
TALER_amount_get_zero (currency,
total);
+ TALER_amount_get_zero (currency,
+ &rtotal);
for (off=0;off<len;off++)
{
json_t *transaction;
@@ -89,6 +93,7 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
+ add = GNUNET_SYSERR;
if (0 == strcasecmp (type,
"DEPOSIT"))
{
@@ -123,11 +128,12 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
&dr->amount_with_fee);
if (0 != TALER_amount_cmp (&dr_amount,
&amount))
- {
- GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
- }
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ add = GNUNET_YES;
}
else if (0 == strcasecmp (type,
"MELT"))
@@ -167,6 +173,67 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
+ add = GNUNET_YES;
+ }
+ else if (0 == strcasecmp (type,
+ "REFUND"))
+ {
+ const struct TALER_RefundRequestPS *rr;
+ struct TALER_Amount rr_amount;
+ struct TALER_Amount rr_fee;
+ struct TALER_Amount rr_delta;
+
+ if (details_size != sizeof (struct TALER_RefundRequestPS))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ rr = (const struct TALER_RefundRequestPS *) details;
+ if (details_size != ntohl (rr->purpose.size))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+ &rr->purpose,
+ &sig.eddsa_signature,
+ &rr->merchant.eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ TALER_amount_ntoh (&rr_amount,
+ &rr->refund_amount);
+ TALER_amount_ntoh (&rr_fee,
+ &rr->refund_fee);
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&rr_delta,
+ &rr_amount,
+ &rr_fee))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ if (0 != TALER_amount_cmp (&rr_delta,
+ &amount))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ /* NOTE/FIXME: theoretically, we could also check that the given
+ transaction_id and merchant_pub and h_contract appear in the
+ history under deposits. However, there is really no benefit
+ for the exchange to lie here, so not checking is probably OK
+ (an auditor ought to check, though). Then again, we similarly
+ had no reason to check the merchant's signature (other than a
+ well-formendess check). */
+ add = GNUNET_NO;
}
else
{
@@ -175,18 +242,54 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
- if (GNUNET_OK !=
- TALER_amount_add (total,
- total,
- &amount))
+ if (GNUNET_YES == add)
{
- /* overflow in history already!? inconceivable! Bad exchange! */
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return GNUNET_SYSERR;
+ /* This amount should be added to the total */
+ if (GNUNET_OK !=
+ TALER_amount_add (total,
+ total,
+ &amount))
+ {
+ /* overflow in history already!? inconceivable! Bad exchange! */
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ /* This amount should be subtracted from the total.
+
+ However, for the implementation, we first *add* up all of
+ these negative amounts, as we might get refunds before
+ deposits from a semi-evil exchange. Then, at the end, we do
+ the subtraction by calculating "total = total - rtotal" */
+ GNUNET_assert (GNUNET_NO == add);
+ if (GNUNET_OK !=
+ TALER_amount_add (&rtotal,
+ &rtotal,
+ &amount))
+ {
+ /* overflow in refund history? inconceivable! Bad exchange! */
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
}
GNUNET_JSON_parse_free (spec);
}
+
+ /* Finally, subtract 'rtotal' from total to handle the subtractions */
+ if (GNUNET_OK !=
+ TALER_amount_subtract (total,
+ total,
+ &rtotal))
+ {
+ /* underflow in history? inconceivable! Bad exchange! */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
return GNUNET_OK;
}
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index 2011a5e49..6a21d8ac4 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -408,7 +408,7 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
const char *type;
struct TALER_Amount value;
json_t *history;
- const struct TALER_CoinSpendSignatureP *sig;
+ const struct GNUNET_CRYPTO_EddsaSignature *sig;
const struct TALER_EXCHANGEDB_TransactionList *pos;
history = json_array ();
@@ -436,12 +436,12 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
&deposit->deposit_fee);
dr.merchant = deposit->merchant_pub;
dr.coin_pub = deposit->coin.coin_pub;
- sig = &deposit->csig;
+ sig = &deposit->csig.eddsa_signature;
/* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
&dr.purpose,
- &sig->eddsa_signature,
+ sig,
&deposit->coin.coin_pub.eddsa_pub))
{
GNUNET_break (0);
@@ -468,12 +468,12 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
TALER_amount_hton (&ms.melt_fee,
&melt->melt_fee);
ms.coin_pub = melt->coin.coin_pub;
- sig = &melt->coin_sig;
+ sig = &melt->coin_sig.eddsa_signature;
/* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&ms.purpose,
- &sig->eddsa_signature,
+ sig,
&melt->coin.coin_pub.eddsa_pub))
{
GNUNET_break (0);
@@ -485,6 +485,48 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
}
break;
+ case TALER_EXCHANGEDB_TT_REFUND:
+ {
+ struct TALER_RefundRequestPS rr;
+ const struct TALER_EXCHANGEDB_Refund *refund = pos->details.refund;
+
+ type = "REFUND";
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&value,
+ &refund->refund_amount,
+ &refund->refund_fee))
+ {
+ GNUNET_break (0);
+ json_decref (history);
+ return NULL;
+ }
+ rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
+ rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
+ rr.h_contract = refund->h_contract;
+ rr.transaction_id = GNUNET_htonll (refund->transaction_id);
+ rr.coin_pub = refund->coin.coin_pub;
+ rr.merchant = refund->merchant_pub;
+ rr.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
+ TALER_amount_hton (&rr.refund_amount,
+ &refund->refund_amount);
+ TALER_amount_hton (&rr.refund_fee,
+ &refund->refund_fee);
+ /* internal sanity check before we hand out a bogus sig... */
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+ &rr.purpose,
+ sig,
+ &refund->merchant_pub.eddsa_pub))
+ {
+ GNUNET_break (0);
+ json_decref (history);
+ return NULL;
+ }
+ sig = &refund->merchant_sig.eddsa_sig;
+ details = GNUNET_JSON_from_data (&rr.purpose,
+ sizeof (struct TALER_RefundRequestPS));
+ }
+ break;
default:
GNUNET_assert (0);
}
@@ -493,7 +535,7 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
"type", type,
"amount", TALER_JSON_from_amount (&value),
"signature", GNUNET_JSON_from_data (sig,
- sizeof (struct TALER_CoinSpendSignatureP)),
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature)),
"details", details));
}
return history;