From 5e8ef386803c399c1b6d780181b0b8662c418db0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Apr 2017 21:05:27 +0200 Subject: [PATCH] fixing #4980 --- src/exchange-lib/exchange_api_common.c | 147 ++++----- src/exchange-lib/exchange_api_reserve.c | 68 ++-- src/exchange/taler-exchange-httpd_responses.c | 309 +++++++++--------- src/include/taler_exchangedb_plugin.h | 57 +++- src/include/taler_json_lib.h | 12 + src/include/taler_signatures.h | 5 + src/json/json_helper.c | 89 ++++- 7 files changed, 405 insertions(+), 282 deletions(-) diff --git a/src/exchange-lib/exchange_api_common.c b/src/exchange-lib/exchange_api_common.c index 29d0f6d1c..d9d0a6d56 100644 --- a/src/exchange-lib/exchange_api_common.c +++ b/src/exchange-lib/exchange_api_common.c @@ -89,13 +89,22 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, "DEPOSIT")) { struct TALER_DepositRequestPS dr; - struct TALER_Amount dr_amount; struct TALER_CoinSpendSignatureP sig; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("signature", + GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), - GNUNET_JSON_spec_fixed_auto ("details", - &dr), + GNUNET_JSON_spec_fixed_auto ("h_proposal_data", + &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() }; @@ -107,12 +116,11 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - /* TODO #4980 */ - if (sizeof (dr) != ntohl (dr.purpose.size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + dr.purpose.size = htonl (sizeof (dr)); + dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); + TALER_amount_hton (&dr.amount_with_fee, + &amount); + dr.coin_pub = *coin_pub; if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT, &dr.purpose, @@ -122,28 +130,22 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - TALER_amount_ntoh (&dr_amount, - &dr.amount_with_fee); - /* TODO #4980 */ - if (0 != TALER_amount_cmp (&dr_amount, - &amount)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + /* TODO: check that deposit fee and coin value match + our expectations from /keys! */ add = GNUNET_YES; } else if (0 == strcasecmp (type, "MELT")) { struct TALER_RefreshMeltCoinAffirmationPS rm; - struct TALER_Amount rm_amount; struct TALER_CoinSpendSignatureP sig; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("signature", + GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), - GNUNET_JSON_spec_fixed_auto ("details", - &rm), + GNUNET_JSON_spec_fixed_auto ("session_hash", + &rm.session_hash), + TALER_JSON_spec_amount_nbo ("melt_fee", + &rm.melt_fee), GNUNET_JSON_spec_end() }; @@ -155,12 +157,11 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - /* TODO #4980 */ - if (sizeof (rm) != ntohl (rm.purpose.size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + rm.purpose.size = htonl (sizeof (rm)); + rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); + TALER_amount_hton (&rm.amount_with_fee, + &amount); + rm.coin_pub = *coin_pub; if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, &rm.purpose, @@ -170,30 +171,26 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - TALER_amount_ntoh (&rm_amount, - &rm.amount_with_fee); - /* TODO #4980 */ - if (0 != TALER_amount_cmp (&rm_amount, - &amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + /* TODO: check that deposit fee and coin value match + our expectations from /keys! */ add = GNUNET_YES; } else if (0 == strcasecmp (type, "REFUND")) { struct TALER_RefundRequestPS rr; - struct TALER_Amount rr_amount; - struct TALER_Amount rr_fee; - struct TALER_Amount rr_delta; - struct TALER_CoinSpendSignatureP sig; + struct TALER_MerchantSignatureP sig; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("signature", + GNUNET_JSON_spec_fixed_auto ("merchant_sig", &sig), - GNUNET_JSON_spec_fixed_auto ("details", - &rr), + GNUNET_JSON_spec_fixed_auto ("h_proposal_data", + &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() }; @@ -205,38 +202,20 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - /* TODO #4980 */ - if (sizeof (rr) != ntohl (rr.purpose.size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + rr.purpose.size = htonl (sizeof (rr)); + rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND); + rr.coin_pub = *coin_pub; + TALER_amount_hton (&rr.refund_amount, + &amount); if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND, &rr.purpose, - &sig.eddsa_signature, + &sig.eddsa_sig, &rr.merchant.eddsa_pub)) { GNUNET_break_op (0); 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 merchant_pub and h_proposal_data appear in the 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 had no reason to check the merchant's signature (other than a well-formendess check). */ + /* TODO: check that deposit fee and coin value match + our expectations from /keys! */ add = GNUNET_NO; } else if (0 == strcasecmp (type, "PAYBACK")) { struct TALER_PaybackConfirmationPS pc; - struct TALER_Amount pc_amount; struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangeSignatureP exchange_sig; struct GNUNET_JSON_Specification spec[] = { @@ -258,8 +238,10 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, &exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", &exchange_pub), - GNUNET_JSON_spec_fixed_auto ("details", - &pc), + GNUNET_JSON_spec_fixed_auto ("reserve_pub", + &pc.reserve_pub), + GNUNET_JSON_spec_absolute_time_nbo ("timestamp", + &pc.timestamp), GNUNET_JSON_spec_end() }; @@ -271,13 +253,11 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - - /* TODO #4980 */ - if (sizeof (pc) != ntohl (pc.purpose.size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + pc.purpose.size = htonl (sizeof (pc)); + pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); + pc.coin_pub = *coin_pub; + TALER_amount_hton (&pc.payback_amount, + &amount); if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK, &pc.purpose, @@ -287,15 +267,6 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, GNUNET_break_op (0); 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; } else diff --git a/src/exchange-lib/exchange_api_reserve.c b/src/exchange-lib/exchange_api_reserve.c index 0ed8cca00..98f15dcda 100644 --- a/src/exchange-lib/exchange_api_reserve.c +++ b/src/exchange-lib/exchange_api_reserve.c @@ -184,12 +184,15 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, { struct TALER_ReserveSignatureP sig; struct TALER_WithdrawRequestPS withdraw_purpose; - struct TALER_Amount amount_from_purpose; struct GNUNET_JSON_Specification withdraw_spec[] = { - GNUNET_JSON_spec_fixed_auto ("signature", + GNUNET_JSON_spec_fixed_auto ("reserve_sig", &sig), - GNUNET_JSON_spec_fixed_auto ("details", - &withdraw_purpose), + TALER_JSON_spec_amount_nbo ("withdraw_fee", + &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() }; unsigned int i; @@ -203,6 +206,13 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, GNUNET_break_op (0); 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 */ if (GNUNET_OK != 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); return GNUNET_SYSERR; } - TALER_amount_ntoh (&amount_from_purpose, - &withdraw_purpose.amount_with_fee); - /* TODO #4980 */ - if (0 != TALER_amount_cmp (&amount, - &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"); + /* TODO: check that withdraw fee matches expectations! */ + rhistory[off].details.out_authorization_sig + = json_object_get (transaction, + "signature"); /* Check check that the same withdraw transaction isn't listed twice by the exchange. We use the "uuid" array to remember the hashes of all @@ -263,25 +265,22 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, "PAYBACK")) { struct TALER_PaybackConfirmationPS pc; - struct TALER_Amount amount_from_purpose; - struct GNUNET_TIME_Absolute timestamp_from_purpose; struct GNUNET_TIME_Absolute timestamp; const struct TALER_EXCHANGE_Keys *key_state; struct GNUNET_JSON_Specification payback_spec[] = { - GNUNET_JSON_spec_fixed_auto ("details", - &pc), + GNUNET_JSON_spec_fixed_auto ("coin_pub", + &pc.coin_pub), GNUNET_JSON_spec_fixed_auto ("exchange_sig", &rhistory[off].details.payback_details.exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", &rhistory[off].details.payback_details.exchange_pub), - GNUNET_JSON_spec_absolute_time ("timestamp", - ×tamp), - TALER_JSON_spec_amount ("amount", - &rhistory[off].amount), + GNUNET_JSON_spec_absolute_time_nbo ("timestamp", + &pc.timestamp), GNUNET_JSON_spec_end() }; rhistory[off].type = TALER_EXCHANGE_RTT_PAYBACK; + rhistory[off].amount = amount; if (GNUNET_OK != GNUNET_JSON_parse (transaction, payback_spec, @@ -291,22 +290,13 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, return GNUNET_SYSERR; } rhistory[off].details.payback_details.coin_pub = pc.coin_pub; - TALER_amount_ntoh (&amount_from_purpose, - &pc.payback_amount); + TALER_amount_hton (&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; - 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); if (GNUNET_OK != diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index c67c885ec..a335e440c 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -307,7 +307,7 @@ TEH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection, { return TEH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, - "{ s:s, s:I, s:s}", + "{s:s, s:I, s:s}", "error", "missing parameter", "code", (json_int_t) ec, "parameter", param_name); @@ -524,20 +524,15 @@ TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection, static json_t * compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) { - json_t *details; - const char *type; - struct TALER_Amount value; json_t *history; - const struct GNUNET_CRYPTO_EddsaSignature *sig; - const struct TALER_EXCHANGEDB_TransactionList *pos; - + history = json_array (); if (NULL == history) { GNUNET_break (0); /* out of memory!? */ 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) { @@ -546,8 +541,6 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) struct TALER_DepositRequestPS dr; 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.size = htonl (sizeof (struct TALER_DepositRequestPS)); dr.h_proposal_data = deposit->h_proposal_data; @@ -560,12 +553,11 @@ 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.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, + &deposit->csig.eddsa_signature, &deposit->coin.coin_pub.eddsa_pub)) { GNUNET_break (0); @@ -573,16 +565,25 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) return NULL; } - details = GNUNET_JSON_from_data_auto (&dr); - break; + GNUNET_assert (0 == + 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: { struct TALER_RefreshMeltCoinAffirmationPS ms; 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.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); 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, &melt->melt_fee); ms.coin_pub = melt->coin.coin_pub; - 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, + &melt->coin_sig.eddsa_signature, &melt->coin.coin_pub.eddsa_pub)) { GNUNET_break (0); @@ -604,15 +604,22 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) 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; case TALER_EXCHANGEDB_TT_REFUND: { struct TALER_RefundRequestPS rr; const struct TALER_EXCHANGEDB_Refund *refund = pos->details.refund; + struct TALER_Amount value; - type = "REFUND"; if (GNUNET_OK != TALER_amount_subtract (&value, &refund->refund_amount, @@ -633,18 +640,27 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) TALER_amount_hton (&rr.refund_fee, &refund->refund_fee); /* internal sanity check before we hand out a bogus sig... */ - sig = &refund->merchant_sig.eddsa_sig; if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND, &rr.purpose, - sig, + &refund->merchant_sig.eddsa_sig, &refund->merchant_pub.eddsa_pub)) { GNUNET_break (0); json_decref (history); 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; case TALER_EXCHANGEDB_TT_PAYBACK: @@ -654,8 +670,6 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) struct TALER_ExchangePublicKeyP epub; struct TALER_ExchangeSignatureP esig; - type = "PAYBACK"; - value = payback->value; pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); pc.purpose.size = htonl (sizeof (pc)); 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, &epub, &esig); - details = GNUNET_JSON_from_data_auto (&pc); GNUNET_assert (0 == json_array_append_new (history, - json_pack ("{s:s, s:o, s:o, s:o, s:o}", - "type", type, - "amount", TALER_JSON_from_amount (&value), + json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}", + "type", "PAYBACK", + "amount", TALER_JSON_from_amount (&payback->value), "exchange_sig", GNUNET_JSON_from_data_auto (&esig), "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! */ - continue; + break; default: 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; } @@ -739,20 +745,12 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, { struct TALER_Amount deposit_total; struct TALER_Amount withdraw_total; - struct TALER_Amount value; json_t *json_history; 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 (); 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) { @@ -778,115 +776,130 @@ compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh, "amount", TALER_JSON_from_amount (&pos->details.bank->amount)))); break; 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; + + 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)))); } - 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)))); break; case TALER_EXCHANGEDB_RO_PAYBACK_COIN: - payback = pos->details.payback; - if (0 == (1 & ret)) - deposit_total = payback->value; - else - if (GNUNET_OK != - 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); + { + const struct TALER_EXCHANGEDB_Payback *payback; + struct TALER_PaybackConfirmationPS pc; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP 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), - "details", GNUNET_JSON_from_data_auto (&pc)))); + payback = pos->details.payback; + if (0 == (1 & ret)) + deposit_total = payback->value; + else + if (GNUNET_OK != + 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 == + 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; case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK: - value = pos->details.bank->amount; - 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; + struct TALER_ReserveCloseConfirmationPS rcc; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; + struct TALER_Amount value; - rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); - rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS)); - rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.bank->execution_date); - TALER_amount_hton (&rcc.closing_amount, - &value); - rcc.reserve_pub = pos->details.bank->reserve_pub; - TALER_JSON_hash (pos->details.bank->sender_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), - "details", GNUNET_JSON_from_data_auto (&rcc), - "amount", TALER_JSON_from_amount (&value)))); + value = pos->details.closing->amount; + 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; + rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); + 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; } } diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index fe08bd27e..b9c3d79e6 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -28,14 +28,13 @@ /** - * @brief Information we keep on bank transfer(s) that established or - * closed a reserve. + * @brief Information we keep on bank transfer(s) that established a reserve. */ 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; @@ -52,7 +51,7 @@ struct TALER_EXCHANGEDB_BankTransfer 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; @@ -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 */ @@ -244,8 +284,7 @@ struct TALER_EXCHANGEDB_ReserveHistory /** * Details about a bank transfer to the exchange (reserve - * was established) or from the exchange (reserve was - * closed). + * was established). */ struct TALER_EXCHANGEDB_BankTransfer *bank; @@ -259,6 +298,12 @@ struct TALER_EXCHANGEDB_ReserveHistory */ struct TALER_EXCHANGEDB_Payback *payback; + /** + * Details about a bank transfer from the exchange (reserve + * was closed). + */ + struct TALER_EXCHANGEDB_ClosingTransfer *closing; + } details; }; diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index b247ba1f5..248de9c45 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -58,6 +58,18 @@ TALER_JSON_spec_amount (const char *name, 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. * diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 70560b4b3..0b1c7ac33 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -1253,6 +1253,11 @@ struct TALER_ReserveCloseConfirmationPS */ 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. */ diff --git a/src/json/json_helper.c b/src/json/json_helper.c index f964f8f17..1d2a33156 100644 --- a/src/json/json_helper.c +++ b/src/json/json_helper.c @@ -119,7 +119,6 @@ parse_amount (void *cls, } - /** * 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", ¤cy)) + { + 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. *