This commit is contained in:
Christian Grothoff 2017-04-18 21:05:27 +02:00
parent 164c125528
commit 5e8ef38680
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
7 changed files with 405 additions and 282 deletions

View File

@ -89,13 +89,22 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
"DEPOSIT")) "DEPOSIT"))
{ {
struct TALER_DepositRequestPS dr; struct TALER_DepositRequestPS dr;
struct TALER_Amount dr_amount;
struct TALER_CoinSpendSignatureP sig; struct TALER_CoinSpendSignatureP sig;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("signature", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&sig), &sig),
GNUNET_JSON_spec_fixed_auto ("details", GNUNET_JSON_spec_fixed_auto ("h_proposal_data",
&dr), &dr.h_proposal_data),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&dr.h_wire),
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
&dr.timestamp),
GNUNET_JSON_spec_absolute_time_nbo ("refund_deadline",
&dr.refund_deadline),
TALER_JSON_spec_amount_nbo ("deposit_fee",
&dr.deposit_fee),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&dr.merchant),
GNUNET_JSON_spec_end() GNUNET_JSON_spec_end()
}; };
@ -107,12 +116,11 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
/* TODO #4980 */ dr.purpose.size = htonl (sizeof (dr));
if (sizeof (dr) != ntohl (dr.purpose.size)) dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
{ TALER_amount_hton (&dr.amount_with_fee,
GNUNET_break_op (0); &amount);
return GNUNET_SYSERR; dr.coin_pub = *coin_pub;
}
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,
@ -122,28 +130,22 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TALER_amount_ntoh (&dr_amount, /* TODO: check that deposit fee and coin value match
&dr.amount_with_fee); our expectations from /keys! */
/* TODO #4980 */
if (0 != TALER_amount_cmp (&dr_amount,
&amount))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
add = GNUNET_YES; add = GNUNET_YES;
} }
else if (0 == strcasecmp (type, else if (0 == strcasecmp (type,
"MELT")) "MELT"))
{ {
struct TALER_RefreshMeltCoinAffirmationPS rm; struct TALER_RefreshMeltCoinAffirmationPS rm;
struct TALER_Amount rm_amount;
struct TALER_CoinSpendSignatureP sig; struct TALER_CoinSpendSignatureP sig;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("signature", GNUNET_JSON_spec_fixed_auto ("coin_sig",
&sig), &sig),
GNUNET_JSON_spec_fixed_auto ("details", GNUNET_JSON_spec_fixed_auto ("session_hash",
&rm), &rm.session_hash),
TALER_JSON_spec_amount_nbo ("melt_fee",
&rm.melt_fee),
GNUNET_JSON_spec_end() GNUNET_JSON_spec_end()
}; };
@ -155,12 +157,11 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
/* TODO #4980 */ rm.purpose.size = htonl (sizeof (rm));
if (sizeof (rm) != ntohl (rm.purpose.size)) rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
{ TALER_amount_hton (&rm.amount_with_fee,
GNUNET_break_op (0); &amount);
return GNUNET_SYSERR; rm.coin_pub = *coin_pub;
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&rm.purpose, &rm.purpose,
@ -170,30 +171,26 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TALER_amount_ntoh (&rm_amount, /* TODO: check that deposit fee and coin value match
&rm.amount_with_fee); our expectations from /keys! */
/* TODO #4980 */
if (0 != TALER_amount_cmp (&rm_amount,
&amount))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_YES; add = GNUNET_YES;
} }
else if (0 == strcasecmp (type, else if (0 == strcasecmp (type,
"REFUND")) "REFUND"))
{ {
struct TALER_RefundRequestPS rr; struct TALER_RefundRequestPS rr;
struct TALER_Amount rr_amount; struct TALER_MerchantSignatureP sig;
struct TALER_Amount rr_fee;
struct TALER_Amount rr_delta;
struct TALER_CoinSpendSignatureP sig;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("signature", GNUNET_JSON_spec_fixed_auto ("merchant_sig",
&sig), &sig),
GNUNET_JSON_spec_fixed_auto ("details", GNUNET_JSON_spec_fixed_auto ("h_proposal_data",
&rr), &rr.h_proposal_data),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&rr.merchant),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&rr.rtransaction_id),
TALER_JSON_spec_amount_nbo ("refund_fee",
&rr.refund_fee),
GNUNET_JSON_spec_end() GNUNET_JSON_spec_end()
}; };
@ -205,38 +202,20 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
/* TODO #4980 */ rr.purpose.size = htonl (sizeof (rr));
if (sizeof (rr) != ntohl (rr.purpose.size)) rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
{ rr.coin_pub = *coin_pub;
GNUNET_break_op (0); TALER_amount_hton (&rr.refund_amount,
return GNUNET_SYSERR; &amount);
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr.purpose, &rr.purpose,
&sig.eddsa_signature, &sig.eddsa_sig,
&rr.merchant.eddsa_pub)) &rr.merchant.eddsa_pub))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TALER_amount_ntoh (&rr_amount,
&rr.refund_amount);
if (GNUNET_OK !=
TALER_amount_subtract (&rr_delta,
&rr_amount,
&rr_fee))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
/* TODO #4980 */
if (0 != TALER_amount_cmp (&rr_delta,
&amount))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
/* NOTE: theoretically, we could also check that the given /* NOTE: theoretically, we could also check that the given
merchant_pub and h_proposal_data appear in the merchant_pub and h_proposal_data appear in the
history under deposits. However, there is really no benefit history under deposits. However, there is really no benefit
@ -244,13 +223,14 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
(an auditor ought to check, though). Then again, we similarly (an auditor ought to check, though). Then again, we similarly
had no reason to check the merchant's signature (other than a had no reason to check the merchant's signature (other than a
well-formendess check). */ well-formendess check). */
/* TODO: check that deposit fee and coin value match
our expectations from /keys! */
add = GNUNET_NO; add = GNUNET_NO;
} }
else if (0 == strcasecmp (type, else if (0 == strcasecmp (type,
"PAYBACK")) "PAYBACK"))
{ {
struct TALER_PaybackConfirmationPS pc; struct TALER_PaybackConfirmationPS pc;
struct TALER_Amount pc_amount;
struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig; struct TALER_ExchangeSignatureP exchange_sig;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
@ -258,8 +238,10 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
&exchange_sig), &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&exchange_pub), &exchange_pub),
GNUNET_JSON_spec_fixed_auto ("details", GNUNET_JSON_spec_fixed_auto ("reserve_pub",
&pc), &pc.reserve_pub),
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
&pc.timestamp),
GNUNET_JSON_spec_end() GNUNET_JSON_spec_end()
}; };
@ -271,13 +253,11 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
pc.purpose.size = htonl (sizeof (pc));
/* TODO #4980 */ pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
if (sizeof (pc) != ntohl (pc.purpose.size)) pc.coin_pub = *coin_pub;
{ TALER_amount_hton (&pc.payback_amount,
GNUNET_break_op (0); &amount);
return GNUNET_SYSERR;
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
&pc.purpose, &pc.purpose,
@ -287,15 +267,6 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TALER_amount_ntoh (&pc_amount,
&pc.payback_amount);
/* TODO #4980 */
if (0 != TALER_amount_cmp (&pc_amount,
&amount))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
add = GNUNET_YES; add = GNUNET_YES;
} }
else else

View File

@ -184,12 +184,15 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
{ {
struct TALER_ReserveSignatureP sig; struct TALER_ReserveSignatureP sig;
struct TALER_WithdrawRequestPS withdraw_purpose; struct TALER_WithdrawRequestPS withdraw_purpose;
struct TALER_Amount amount_from_purpose;
struct GNUNET_JSON_Specification withdraw_spec[] = { struct GNUNET_JSON_Specification withdraw_spec[] = {
GNUNET_JSON_spec_fixed_auto ("signature", GNUNET_JSON_spec_fixed_auto ("reserve_sig",
&sig), &sig),
GNUNET_JSON_spec_fixed_auto ("details", TALER_JSON_spec_amount_nbo ("withdraw_fee",
&withdraw_purpose), &withdraw_purpose.withdraw_fee),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&withdraw_purpose.h_denomination_pub),
GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
&withdraw_purpose.h_coin_envelope),
GNUNET_JSON_spec_end() GNUNET_JSON_spec_end()
}; };
unsigned int i; unsigned int i;
@ -203,6 +206,13 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
withdraw_purpose.purpose.size
= htonl (sizeof (withdraw_purpose));
withdraw_purpose.purpose.purpose
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
withdraw_purpose.reserve_pub = *reserve_pub;
TALER_amount_hton (&withdraw_purpose.amount_with_fee,
&amount);
/* Check that the signature is a valid withdraw request */ /* Check that the signature is a valid withdraw request */
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
@ -214,18 +224,10 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_JSON_parse_free (withdraw_spec); GNUNET_JSON_parse_free (withdraw_spec);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TALER_amount_ntoh (&amount_from_purpose, /* TODO: check that withdraw fee matches expectations! */
&withdraw_purpose.amount_with_fee); rhistory[off].details.out_authorization_sig
/* TODO #4980 */ = json_object_get (transaction,
if (0 != TALER_amount_cmp (&amount, "signature");
&amount_from_purpose))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (withdraw_spec);
return GNUNET_SYSERR;
}
rhistory[off].details.out_authorization_sig = json_object_get (transaction,
"signature");
/* Check check that the same withdraw transaction /* Check check that the same withdraw transaction
isn't listed twice by the exchange. We use the isn't listed twice by the exchange. We use the
"uuid" array to remember the hashes of all "uuid" array to remember the hashes of all
@ -263,25 +265,22 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
"PAYBACK")) "PAYBACK"))
{ {
struct TALER_PaybackConfirmationPS pc; struct TALER_PaybackConfirmationPS pc;
struct TALER_Amount amount_from_purpose;
struct GNUNET_TIME_Absolute timestamp_from_purpose;
struct GNUNET_TIME_Absolute timestamp; struct GNUNET_TIME_Absolute timestamp;
const struct TALER_EXCHANGE_Keys *key_state; const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification payback_spec[] = { struct GNUNET_JSON_Specification payback_spec[] = {
GNUNET_JSON_spec_fixed_auto ("details", GNUNET_JSON_spec_fixed_auto ("coin_pub",
&pc), &pc.coin_pub),
GNUNET_JSON_spec_fixed_auto ("exchange_sig", GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&rhistory[off].details.payback_details.exchange_sig), &rhistory[off].details.payback_details.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&rhistory[off].details.payback_details.exchange_pub), &rhistory[off].details.payback_details.exchange_pub),
GNUNET_JSON_spec_absolute_time ("timestamp", GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
&timestamp), &pc.timestamp),
TALER_JSON_spec_amount ("amount",
&rhistory[off].amount),
GNUNET_JSON_spec_end() GNUNET_JSON_spec_end()
}; };
rhistory[off].type = TALER_EXCHANGE_RTT_PAYBACK; rhistory[off].type = TALER_EXCHANGE_RTT_PAYBACK;
rhistory[off].amount = amount;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (transaction, GNUNET_JSON_parse (transaction,
payback_spec, payback_spec,
@ -291,22 +290,13 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
rhistory[off].details.payback_details.coin_pub = pc.coin_pub; rhistory[off].details.payback_details.coin_pub = pc.coin_pub;
TALER_amount_ntoh (&amount_from_purpose, TALER_amount_hton (&pc.payback_amount,
&pc.payback_amount); &amount);
pc.purpose.size = htonl (sizeof (pc));
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
pc.reserve_pub = *reserve_pub;
timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
rhistory[off].details.payback_details.timestamp = timestamp; rhistory[off].details.payback_details.timestamp = timestamp;
timestamp_from_purpose = GNUNET_TIME_absolute_ntoh (pc.timestamp);
/* TODO #4980 */
if ( (0 != memcmp (&pc.reserve_pub,
reserve_pub,
sizeof (*reserve_pub))) ||
(timestamp_from_purpose.abs_value_us !=
timestamp.abs_value_us) ||
(0 != TALER_amount_cmp (&amount_from_purpose,
&rhistory[off].amount)) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
key_state = TALER_EXCHANGE_get_keys (exchange); key_state = TALER_EXCHANGE_get_keys (exchange);
if (GNUNET_OK != if (GNUNET_OK !=

View File

@ -307,7 +307,7 @@ TEH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
{ {
return TEH_RESPONSE_reply_json_pack (connection, return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST, MHD_HTTP_BAD_REQUEST,
"{ s:s, s:I, s:s}", "{s:s, s:I, s:s}",
"error", "missing parameter", "error", "missing parameter",
"code", (json_int_t) ec, "code", (json_int_t) ec,
"parameter", param_name); "parameter", param_name);
@ -524,12 +524,7 @@ TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
static json_t * static json_t *
compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
{ {
json_t *details;
const char *type;
struct TALER_Amount value;
json_t *history; json_t *history;
const struct GNUNET_CRYPTO_EddsaSignature *sig;
const struct TALER_EXCHANGEDB_TransactionList *pos;
history = json_array (); history = json_array ();
if (NULL == history) if (NULL == history)
@ -537,7 +532,7 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
GNUNET_break (0); /* out of memory!? */ GNUNET_break (0); /* out of memory!? */
return NULL; return NULL;
} }
for (pos = tl; NULL != pos; pos = pos->next) for (const struct TALER_EXCHANGEDB_TransactionList *pos = tl; NULL != pos; pos = pos->next)
{ {
switch (pos->type) switch (pos->type)
{ {
@ -546,8 +541,6 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
struct TALER_DepositRequestPS dr; struct TALER_DepositRequestPS dr;
const struct TALER_EXCHANGEDB_Deposit *deposit = pos->details.deposit; const struct TALER_EXCHANGEDB_Deposit *deposit = pos->details.deposit;
type = "DEPOSIT";
value = deposit->amount_with_fee;
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
dr.h_proposal_data = deposit->h_proposal_data; dr.h_proposal_data = deposit->h_proposal_data;
@ -560,12 +553,11 @@ 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.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, &deposit->csig.eddsa_signature,
&deposit->coin.coin_pub.eddsa_pub)) &deposit->coin.coin_pub.eddsa_pub))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -573,16 +565,25 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
return NULL; return NULL;
} }
details = GNUNET_JSON_from_data_auto (&dr); GNUNET_assert (0 ==
break; json_array_append_new (history,
json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
"type", "DEPOSIT",
"amount", TALER_JSON_from_amount (&deposit->amount_with_fee),
"deposit_fee", TALER_JSON_from_amount (&deposit->deposit_fee),
"timestamp", GNUNET_JSON_from_time_abs (deposit->timestamp),
"refund_deadline", GNUNET_JSON_from_time_abs (deposit->refund_deadline),
"merchant_pub", GNUNET_JSON_from_data_auto (&deposit->merchant_pub),
"h_proposal_data", GNUNET_JSON_from_data_auto (&deposit->h_proposal_data),
"h_wire", GNUNET_JSON_from_data_auto (&deposit->h_wire),
"coin_sig", GNUNET_JSON_from_data_auto (&deposit->csig))));
break;
} }
case TALER_EXCHANGEDB_TT_REFRESH_MELT: case TALER_EXCHANGEDB_TT_REFRESH_MELT:
{ {
struct TALER_RefreshMeltCoinAffirmationPS ms; struct TALER_RefreshMeltCoinAffirmationPS ms;
const struct TALER_EXCHANGEDB_RefreshMelt *melt = pos->details.melt; const struct TALER_EXCHANGEDB_RefreshMelt *melt = pos->details.melt;
type = "MELT";
value = melt->amount_with_fee;
ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
ms.session_hash = melt->session_hash; ms.session_hash = melt->session_hash;
@ -591,12 +592,11 @@ 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.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, &melt->coin_sig.eddsa_signature,
&melt->coin.coin_pub.eddsa_pub)) &melt->coin.coin_pub.eddsa_pub))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -604,15 +604,22 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
return NULL; return NULL;
} }
details = GNUNET_JSON_from_data_auto (&ms); GNUNET_assert (0 ==
json_array_append_new (history,
json_pack ("{s:s, s:o, s:o, s:o, s:o}",
"type", "MELT",
"amount", TALER_JSON_from_amount (&melt->amount_with_fee),
"melt_fee", TALER_JSON_from_amount (&melt->melt_fee),
"session_hash", GNUNET_JSON_from_data_auto (&melt->session_hash),
"coin_sig", GNUNET_JSON_from_data_auto (&melt->coin_sig))));
} }
break; break;
case TALER_EXCHANGEDB_TT_REFUND: case TALER_EXCHANGEDB_TT_REFUND:
{ {
struct TALER_RefundRequestPS rr; struct TALER_RefundRequestPS rr;
const struct TALER_EXCHANGEDB_Refund *refund = pos->details.refund; const struct TALER_EXCHANGEDB_Refund *refund = pos->details.refund;
struct TALER_Amount value;
type = "REFUND";
if (GNUNET_OK != if (GNUNET_OK !=
TALER_amount_subtract (&value, TALER_amount_subtract (&value,
&refund->refund_amount, &refund->refund_amount,
@ -633,18 +640,27 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
TALER_amount_hton (&rr.refund_fee, TALER_amount_hton (&rr.refund_fee,
&refund->refund_fee); &refund->refund_fee);
/* internal sanity check before we hand out a bogus sig... */ /* internal sanity check before we hand out a bogus sig... */
sig = &refund->merchant_sig.eddsa_sig;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr.purpose, &rr.purpose,
sig, &refund->merchant_sig.eddsa_sig,
&refund->merchant_pub.eddsa_pub)) &refund->merchant_pub.eddsa_pub))
{ {
GNUNET_break (0); GNUNET_break (0);
json_decref (history); json_decref (history);
return NULL; return NULL;
} }
details = GNUNET_JSON_from_data_auto (&rr);
GNUNET_assert (0 ==
json_array_append_new (history,
json_pack ("{s:s, s:o, s:o, s:o, s:o, s:I, s:o}",
"type", "REFUND",
"amount", TALER_JSON_from_amount (&value),
"refund_fee", TALER_JSON_from_amount (&refund->refund_fee),
"h_proposal_data", GNUNET_JSON_from_data_auto (&refund->h_proposal_data),
"merchant_pub", GNUNET_JSON_from_data_auto (&refund->merchant_pub),
"rtransaction_id", (json_int_t) refund->rtransaction_id,
"merchant_sig", GNUNET_JSON_from_data_auto (&refund->merchant_sig))));
} }
break; break;
case TALER_EXCHANGEDB_TT_PAYBACK: case TALER_EXCHANGEDB_TT_PAYBACK:
@ -654,8 +670,6 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
struct TALER_ExchangePublicKeyP epub; struct TALER_ExchangePublicKeyP epub;
struct TALER_ExchangeSignatureP esig; struct TALER_ExchangeSignatureP esig;
type = "PAYBACK";
value = payback->value;
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
pc.purpose.size = htonl (sizeof (pc)); pc.purpose.size = htonl (sizeof (pc));
pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp); pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
@ -666,28 +680,20 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
TEH_KS_sign (&pc.purpose, TEH_KS_sign (&pc.purpose,
&epub, &epub,
&esig); &esig);
details = GNUNET_JSON_from_data_auto (&pc);
GNUNET_assert (0 == GNUNET_assert (0 ==
json_array_append_new (history, json_array_append_new (history,
json_pack ("{s:s, s:o, s:o, s:o, s:o}", json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
"type", type, "type", "PAYBACK",
"amount", TALER_JSON_from_amount (&value), "amount", TALER_JSON_from_amount (&payback->value),
"exchange_sig", GNUNET_JSON_from_data_auto (&esig), "exchange_sig", GNUNET_JSON_from_data_auto (&esig),
"exchange_pub", GNUNET_JSON_from_data_auto (&epub), "exchange_pub", GNUNET_JSON_from_data_auto (&epub),
"details", details))); "reserve_pub", GNUNET_JSON_from_data_auto (&payback->reserve_pub),
"timestamp", GNUNET_JSON_from_time_abs (payback->timestamp))));
} }
/* do not go to the default handler, we already appended! */ break;
continue;
default: default:
GNUNET_assert (0); GNUNET_assert (0);
} }
GNUNET_assert (0 ==
json_array_append_new (history,
json_pack ("{s:s, s:o, s:o, s:o}",
"type", type,
"amount", TALER_JSON_from_amount (&value),
"signature", GNUNET_JSON_from_data_auto (sig),
"details", details)));
} }
return history; return history;
} }
@ -739,20 +745,12 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
{ {
struct TALER_Amount deposit_total; struct TALER_Amount deposit_total;
struct TALER_Amount withdraw_total; struct TALER_Amount withdraw_total;
struct TALER_Amount value;
json_t *json_history; json_t *json_history;
int ret; int ret;
const struct TALER_EXCHANGEDB_ReserveHistory *pos;
struct TALER_WithdrawRequestPS wr;
const struct TALER_EXCHANGEDB_Payback *payback;
struct TALER_PaybackConfirmationPS pc;
struct TALER_ReserveCloseConfirmationPS rcc;
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
json_history = json_array (); json_history = json_array ();
ret = 0; ret = 0;
for (pos = rh; NULL != pos; pos = pos->next) for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh; NULL != pos; pos = pos->next)
{ {
switch (pos->type) switch (pos->type)
{ {
@ -778,115 +776,130 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
"amount", TALER_JSON_from_amount (&pos->details.bank->amount)))); "amount", TALER_JSON_from_amount (&pos->details.bank->amount))));
break; break;
case TALER_EXCHANGEDB_RO_WITHDRAW_COIN: case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
value = pos->details.withdraw->amount_with_fee;
if (0 == (2 & ret))
{ {
withdraw_total = value; struct GNUNET_HashCode h_denom_pub;
} struct TALER_Amount value;
else
{
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
json_decref (json_history);
return NULL;
}
}
ret |= 2;
wr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
wr.reserve_pub = pos->details.withdraw->reserve_pub;
TALER_amount_hton (&wr.amount_with_fee,
&value);
TALER_amount_hton (&wr.withdraw_fee,
&pos->details.withdraw->withdraw_fee);
GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key,
&wr.h_denomination_pub);
wr.h_coin_envelope = pos->details.withdraw->h_coin_envelope;
GNUNET_assert (0 ==
json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:o, s:o}",
"type", "WITHDRAW",
"signature", GNUNET_JSON_from_data_auto (&pos->details.withdraw->reserve_sig),
"details", GNUNET_JSON_from_data_auto (&wr),
"amount", TALER_JSON_from_amount (&value)))); value = pos->details.withdraw->amount_with_fee;
if (0 == (2 & ret))
{
withdraw_total = value;
}
else
{
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
json_decref (json_history);
return NULL;
}
}
ret |= 2;
GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key,
&h_denom_pub);
GNUNET_assert (0 ==
json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
"type", "WITHDRAW",
"reserve_sig", GNUNET_JSON_from_data_auto (&pos->details.withdraw->reserve_sig),
"h_coin_envelope", GNUNET_JSON_from_data_auto (&pos->details.withdraw->h_coin_envelope),
"h_denom_pub", GNUNET_JSON_from_data_auto (&h_denom_pub),
"withdraw_fee", TALER_JSON_from_amount (&pos->details.withdraw->withdraw_fee),
"amount", TALER_JSON_from_amount (&value))));
}
break; break;
case TALER_EXCHANGEDB_RO_PAYBACK_COIN: case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
payback = pos->details.payback; {
if (0 == (1 & ret)) const struct TALER_EXCHANGEDB_Payback *payback;
deposit_total = payback->value; struct TALER_PaybackConfirmationPS pc;
else struct TALER_ExchangePublicKeyP pub;
if (GNUNET_OK != struct TALER_ExchangeSignatureP sig;
TALER_amount_add (&deposit_total,
&deposit_total,
&payback->value))
{
json_decref (json_history);
return NULL;
}
ret |= 1;
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
TALER_amount_hton (&pc.payback_amount,
&payback->value);
pc.coin_pub = payback->coin.coin_pub;
pc.reserve_pub = payback->reserve_pub;
TEH_KS_sign (&pc.purpose,
&pub,
&sig);
GNUNET_assert (0 == payback = pos->details.payback;
json_array_append_new (json_history, if (0 == (1 & ret))
json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}", deposit_total = payback->value;
"type", "PAYBACK", else
"exchange_pub", GNUNET_JSON_from_data_auto (&pub), if (GNUNET_OK !=
"exchange_sig", GNUNET_JSON_from_data_auto (&sig), TALER_amount_add (&deposit_total,
"timestamp", GNUNET_JSON_from_time_abs (payback->timestamp), &deposit_total,
"amount", TALER_JSON_from_amount (&payback->value), &payback->value))
"details", GNUNET_JSON_from_data_auto (&pc)))); {
json_decref (json_history);
return NULL;
}
ret |= 1;
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
pc.timestamp = GNUNET_TIME_absolute_hton (payback->timestamp);
TALER_amount_hton (&pc.payback_amount,
&payback->value);
pc.coin_pub = payback->coin.coin_pub;
pc.reserve_pub = payback->reserve_pub;
TEH_KS_sign (&pc.purpose,
&pub,
&sig);
GNUNET_assert (0 ==
json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
"type", "PAYBACK",
"exchange_pub", GNUNET_JSON_from_data_auto (&pub),
"exchange_sig", GNUNET_JSON_from_data_auto (&sig),
"timestamp", GNUNET_JSON_from_time_abs (payback->timestamp),
"amount", TALER_JSON_from_amount (&payback->value),
"coin_pub", GNUNET_JSON_from_data_auto (&payback->coin.coin_pub))));
}
break; break;
case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK: case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
value = pos->details.bank->amount;
if (0 == (2 & ret))
{ {
withdraw_total = value; struct TALER_ReserveCloseConfirmationPS rcc;
} struct TALER_ExchangePublicKeyP pub;
else struct TALER_ExchangeSignatureP sig;
{ struct TALER_Amount value;
if (GNUNET_OK !=
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
json_decref (json_history);
return NULL;
}
}
ret |= 2;
rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); value = pos->details.closing->amount;
rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS)); if (0 == (2 & ret))
rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.bank->execution_date); {
TALER_amount_hton (&rcc.closing_amount, withdraw_total = value;
&value); }
rcc.reserve_pub = pos->details.bank->reserve_pub; else
TALER_JSON_hash (pos->details.bank->sender_account_details, {
&rcc.h_wire); if (GNUNET_OK !=
TEH_KS_sign (&rcc.purpose, TALER_amount_add (&withdraw_total,
&pub, &withdraw_total,
&sig); &value))
GNUNET_assert (0 == {
json_array_append_new (json_history, json_decref (json_history);
json_pack ("{s:s, s:o, s:o, s:o, s:o}", return NULL;
"type", "CLOSING", }
"exchange_pub", GNUNET_JSON_from_data_auto (&pub), }
"exchange_sig", GNUNET_JSON_from_data_auto (&sig), ret |= 2;
"details", GNUNET_JSON_from_data_auto (&rcc), rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
"amount", TALER_JSON_from_amount (&value)))); rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS));
rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.closing->execution_date);
TALER_amount_hton (&rcc.closing_amount,
&value);
TALER_amount_hton (&rcc.closing_fee,
&pos->details.closing->closing_fee);
rcc.reserve_pub = pos->details.closing->reserve_pub;
TALER_JSON_hash (pos->details.closing->receiver_account_details,
&rcc.h_wire);
TEH_KS_sign (&rcc.purpose,
&pub,
&sig);
GNUNET_assert (0 ==
json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:o, s:o, s:o}",
"type", "CLOSING",
"exchange_pub", GNUNET_JSON_from_data_auto (&pub),
"exchange_sig", GNUNET_JSON_from_data_auto (&sig),
"timestamp", GNUNET_JSON_from_time_abs (pos->details.closing->execution_date),
"h_wire", GNUNET_JSON_from_data_auto (&rcc.h_wire),
"amount", TALER_JSON_from_amount (&value),
"closing_fee", TALER_JSON_from_amount (&pos->details.closing->closing_fee))));
}
break; break;
} }
} }

View File

@ -28,14 +28,13 @@
/** /**
* @brief Information we keep on bank transfer(s) that established or * @brief Information we keep on bank transfer(s) that established a reserve.
* closed a reserve.
*/ */
struct TALER_EXCHANGEDB_BankTransfer struct TALER_EXCHANGEDB_BankTransfer
{ {
/** /**
* Public key of the reserve that was filled or depleted. * Public key of the reserve that was filled.
*/ */
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
@ -52,7 +51,7 @@ struct TALER_EXCHANGEDB_BankTransfer
struct GNUNET_TIME_Absolute execution_date; struct GNUNET_TIME_Absolute execution_date;
/** /**
* Detailed wire information about the sending (or receiving) account. * Detailed wire information about the sending account.
*/ */
json_t *sender_account_details; json_t *sender_account_details;
@ -65,6 +64,47 @@ struct TALER_EXCHANGEDB_BankTransfer
}; };
/**
* @brief Information we keep on bank transfer(s) that
* closed a reserve.
*/
struct TALER_EXCHANGEDB_ClosingTransfer
{
/**
* Public key of the reserve that was depleted.
*/
struct TALER_ReservePublicKeyP reserve_pub;
/**
* Amount that was transferred to the exchange.
*/
struct TALER_Amount amount;
/**
* Amount that was charged by the exchange.
*/
struct TALER_Amount closing_fee;
/**
* When did the exchange execute the transaction?
*/
struct GNUNET_TIME_Absolute execution_date;
/**
* Detailed wire information about the receiving account.
*/
json_t *receiver_account_details;
/**
* Detailed wire transfer information that uniquely identifies the
* wire transfer.
*/
json_t *transfer_details;
};
/** /**
* @brief A summary of a Reserve * @brief A summary of a Reserve
*/ */
@ -244,8 +284,7 @@ struct TALER_EXCHANGEDB_ReserveHistory
/** /**
* Details about a bank transfer to the exchange (reserve * Details about a bank transfer to the exchange (reserve
* was established) or from the exchange (reserve was * was established).
* closed).
*/ */
struct TALER_EXCHANGEDB_BankTransfer *bank; struct TALER_EXCHANGEDB_BankTransfer *bank;
@ -259,6 +298,12 @@ struct TALER_EXCHANGEDB_ReserveHistory
*/ */
struct TALER_EXCHANGEDB_Payback *payback; struct TALER_EXCHANGEDB_Payback *payback;
/**
* Details about a bank transfer from the exchange (reserve
* was closed).
*/
struct TALER_EXCHANGEDB_ClosingTransfer *closing;
} details; } details;
}; };

View File

@ -58,6 +58,18 @@ TALER_JSON_spec_amount (const char *name,
struct TALER_Amount *r_amount); struct TALER_Amount *r_amount);
/**
* Provide specification to parse given JSON object to an amount
* in network byte order.
*
* @param name name of the amount field in the JSON
* @param[out] r_amount where the amount has to be written
*/
struct GNUNET_JSON_Specification
TALER_JSON_spec_amount_nbo (const char *name,
struct TALER_AmountNBO *r_amount);
/** /**
* Generate line in parser specification for denomination public key. * Generate line in parser specification for denomination public key.
* *

View File

@ -1253,6 +1253,11 @@ struct TALER_ReserveCloseConfirmationPS
*/ */
struct TALER_AmountNBO closing_amount; struct TALER_AmountNBO closing_amount;
/**
* How much did the exchange charge for closing the reserve?
*/
struct TALER_AmountNBO closing_fee;
/** /**
* Public key of the reserve that received the payback. * Public key of the reserve that received the payback.
*/ */

View File

@ -119,7 +119,6 @@ parse_amount (void *cls,
} }
/** /**
* Provide specification to parse given JSON object to an amount. * Provide specification to parse given JSON object to an amount.
* *
@ -143,6 +142,94 @@ TALER_JSON_spec_amount (const char *name,
} }
/**
* Parse given JSON object to Amount in NBO.
*
* @param cls closure, NULL
* @param root the json object representing data
* @param[out] spec where to write the data
* @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
*/
static int
parse_amount_nbo (void *cls,
json_t *root,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_AmountNBO *r_amount = spec->ptr;
struct TALER_Amount amount;
json_int_t value;
json_int_t fraction;
const char *currency;
memset (&amount,
0,
sizeof (struct TALER_Amount));
if (0 != json_unpack (root,
"{s:I, s:I, s:s}",
"value", &value,
"fraction", &fraction,
"currency", &currency))
{
char *json_enc;
if (NULL == (json_enc = json_dumps (root,
JSON_COMPACT | JSON_ENCODE_ANY)))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Malformed JSON amount: %s\n",
json_enc);
free (json_enc);
return GNUNET_SYSERR;
}
if ( (value < 0) ||
(fraction < 0) ||
(value > UINT64_MAX) ||
(fraction > UINT32_MAX) )
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (strlen (currency) >= TALER_CURRENCY_LEN)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
amount.value = (uint64_t) value;
amount.fraction = (uint32_t) fraction;
strcpy (amount.currency, currency);
(void) TALER_amount_normalize (&amount);
TALER_amount_hton (r_amount,
&amount);
return GNUNET_OK;
}
/**
* Provide specification to parse given JSON object to an amount.
*
* @param name name of the amount field in the JSON
* @param[out] r_amount where the amount has to be written
*/
struct GNUNET_JSON_Specification
TALER_JSON_spec_amount_nbo (const char *name,
struct TALER_AmountNBO *r_amount)
{
struct GNUNET_JSON_Specification ret = {
.parser = &parse_amount_nbo,
.cleaner = NULL,
.cls = NULL,
.field = name,
.ptr = r_amount,
.ptr_size = 0,
.size_ptr = NULL
};
return ret;
}
/** /**
* Generate line in parser specification for denomination public key. * Generate line in parser specification for denomination public key.
* *