support REFUNDS in transaction history in libtalerexchange

This commit is contained in:
Christian Grothoff 2016-05-06 13:33:20 +02:00
parent fbbc49bdad
commit 302070b86e
2 changed files with 165 additions and 20 deletions

View File

@ -44,6 +44,8 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
{ {
size_t len; size_t len;
size_t off; size_t off;
int add;
struct TALER_Amount rtotal;
if (NULL == history) if (NULL == history)
{ {
@ -58,6 +60,8 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
} }
TALER_amount_get_zero (currency, TALER_amount_get_zero (currency,
total); total);
TALER_amount_get_zero (currency,
&rtotal);
for (off=0;off<len;off++) for (off=0;off<len;off++)
{ {
json_t *transaction; json_t *transaction;
@ -89,6 +93,7 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
add = GNUNET_SYSERR;
if (0 == strcasecmp (type, if (0 == strcasecmp (type,
"DEPOSIT")) "DEPOSIT"))
{ {
@ -128,6 +133,7 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
add = GNUNET_YES;
} }
else if (0 == strcasecmp (type, else if (0 == strcasecmp (type,
"MELT")) "MELT"))
@ -167,6 +173,67 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR; 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 else
{ {
@ -175,6 +242,9 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_YES == add)
{
/* This amount should be added to the total */
if (GNUNET_OK != if (GNUNET_OK !=
TALER_amount_add (total, TALER_amount_add (total,
total, total,
@ -185,8 +255,41 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR; 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); 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; return GNUNET_OK;
} }

View File

@ -408,7 +408,7 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
const char *type; const char *type;
struct TALER_Amount value; struct TALER_Amount value;
json_t *history; json_t *history;
const struct TALER_CoinSpendSignatureP *sig; const struct GNUNET_CRYPTO_EddsaSignature *sig;
const struct TALER_EXCHANGEDB_TransactionList *pos; const struct TALER_EXCHANGEDB_TransactionList *pos;
history = json_array (); history = json_array ();
@ -436,12 +436,12 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
&deposit->deposit_fee); &deposit->deposit_fee);
dr.merchant = deposit->merchant_pub; dr.merchant = deposit->merchant_pub;
dr.coin_pub = deposit->coin.coin_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... */ /* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
&dr.purpose, &dr.purpose,
&sig->eddsa_signature, sig,
&deposit->coin.coin_pub.eddsa_pub)) &deposit->coin.coin_pub.eddsa_pub))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -468,12 +468,12 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
TALER_amount_hton (&ms.melt_fee, TALER_amount_hton (&ms.melt_fee,
&melt->melt_fee); &melt->melt_fee);
ms.coin_pub = melt->coin.coin_pub; 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... */ /* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&ms.purpose, &ms.purpose,
&sig->eddsa_signature, sig,
&melt->coin.coin_pub.eddsa_pub)) &melt->coin.coin_pub.eddsa_pub))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -485,6 +485,48 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
} }
break; 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: default:
GNUNET_assert (0); GNUNET_assert (0);
} }
@ -493,7 +535,7 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
"type", type, "type", type,
"amount", TALER_JSON_from_amount (&value), "amount", TALER_JSON_from_amount (&value),
"signature", GNUNET_JSON_from_data (sig, "signature", GNUNET_JSON_from_data (sig,
sizeof (struct TALER_CoinSpendSignatureP)), sizeof (struct GNUNET_CRYPTO_EddsaSignature)),
"details", details)); "details", details));
} }
return history; return history;