diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_purses_deposit.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_purses_deposit.c | 347 |
1 files changed, 86 insertions, 261 deletions
diff --git a/src/exchange/taler-exchange-httpd_purses_deposit.c b/src/exchange/taler-exchange-httpd_purses_deposit.c index 38bd84729..8e4d5e41a 100644 --- a/src/exchange/taler-exchange-httpd_purses_deposit.c +++ b/src/exchange/taler-exchange-httpd_purses_deposit.c @@ -25,10 +25,10 @@ #include <gnunet/gnunet_json_lib.h> #include <jansson.h> #include <microhttpd.h> -#include <pthread.h> #include "taler_dbevents.h" #include "taler_json_lib.h" #include "taler_mhd_lib.h" +#include "taler-exchange-httpd_common_deposit.h" #include "taler-exchange-httpd_purses_deposit.h" #include "taler-exchange-httpd_responses.h" #include "taler_exchangedb_lib.h" @@ -36,43 +36,6 @@ /** - * Information about an individual coin being deposited. - */ -struct Coin -{ - /** - * Public information about the coin. - */ - struct TALER_CoinPublicInfo cpi; - - /** - * Signature affirming spending the coin. - */ - struct TALER_CoinSpendSignatureP coin_sig; - - /** - * Amount to be put into the purse from this coin. - */ - struct TALER_Amount amount; - - /** - * Deposit fee applicable for this coin. - */ - struct TALER_Amount deposit_fee; - - /** - * Amount to be put into the purse from this coin. - */ - struct TALER_Amount amount_minus_fee; - - /** - * ID of the coin in known_coins. - */ - uint64_t known_coin_id; -}; - - -/** * Closure for #deposit_transaction. */ struct PurseDepositContext @@ -98,11 +61,6 @@ struct PurseDepositContext struct GNUNET_TIME_Timestamp purse_expiration; /** - * Key with the merge capability (needed for signing). - */ - struct TALER_PurseMergePublicKeyP merge_pub; - - /** * Hash of the contract (needed for signing). */ struct TALER_PrivateContractHashP h_contract_terms; @@ -115,7 +73,7 @@ struct PurseDepositContext /** * Array of coins being deposited. */ - struct Coin *coins; + struct TEH_PurseDepositedCoin *coins; /** * Length of the @e coins array. @@ -152,7 +110,6 @@ reply_deposit_success (struct MHD_Connection *connection, &pcc->amount, &pcc->deposit_total, pcc->purse_pub, - &pcc->merge_pub, &pcc->h_contract_terms, &pub, &sig))) @@ -175,8 +132,6 @@ reply_deposit_success (struct MHD_Connection *connection, pcc->purse_expiration), GNUNET_JSON_pack_data_auto ("h_contract_terms", &pcc->h_contract_terms), - GNUNET_JSON_pack_data_auto ("merge_pub", - &pcc->merge_pub), GNUNET_JSON_pack_data_auto ("exchange_sig", &sig), GNUNET_JSON_pack_data_auto ("exchange_pub", @@ -205,12 +160,20 @@ deposit_transaction (void *cls, struct PurseDepositContext *pcc = cls; enum GNUNET_DB_QueryStatus qs; + qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; for (unsigned int i = 0; i<pcc->num_coins; i++) { - struct Coin *coin = &pcc->coins[i]; + struct TEH_PurseDepositedCoin *coin = &pcc->coins[i]; bool balance_ok = false; bool conflict = true; + bool too_late = true; + qs = TEH_make_coin_known (&coin->cpi, + connection, + &coin->known_coin_id, + mhd_ret); + if (qs < 0) + return qs; qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls, pcc->purse_pub, &coin->cpi.coin_pub, @@ -218,18 +181,20 @@ deposit_transaction (void *cls, &coin->coin_sig, &coin->amount_minus_fee, &balance_ok, + &too_late, &conflict); if (qs <= 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) return qs; + GNUNET_break (0 != qs); TALER_LOG_WARNING ( "Failed to store purse deposit information in database\n"); *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_STORE_FAILED, "do purse deposit"); - return qs; + return GNUNET_DB_STATUS_HARD_ERROR; } if (! balance_ok) { @@ -237,14 +202,27 @@ 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; } + if (too_late) + { + TEH_plugin->rollback (TEH_plugin->cls); + *mhd_ret + = TALER_MHD_reply_with_ec ( + connection, + TALER_EC_EXCHANGE_PURSE_DEPOSIT_DECIDED_ALREADY, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + } if (conflict) { struct TALER_Amount amount; struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendSignatureP coin_sig; + struct TALER_DenominationHashP h_denom_pub; + struct TALER_AgeCommitmentHash phac; char *partner_url = NULL; TEH_plugin->rollback (TEH_plugin->cls); @@ -252,6 +230,8 @@ deposit_transaction (void *cls, pcc->purse_pub, &coin->cpi.coin_pub, &amount, + &h_denom_pub, + &phac, &coin_sig, &partner_url); if (qs < 0) @@ -274,6 +254,10 @@ deposit_transaction (void *cls, TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA), GNUNET_JSON_pack_data_auto ("coin_pub", &coin_pub), + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &h_denom_pub), + GNUNET_JSON_pack_data_auto ("h_age_commitment", + &phac), GNUNET_JSON_pack_data_auto ("coin_sig", &coin_sig), GNUNET_JSON_pack_allow_null ( @@ -293,8 +277,8 @@ deposit_transaction (void *cls, * Parse a coin and check signature of the coin and the denomination * signature over the coin. * - * @param[in,out] our HTTP connection - * @param[in,out] request context + * @param[in,out] connection our HTTP connection + * @param[in,out] pcc request context * @param[out] coin coin to initialize * @param jcoin coin to parse * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned, @@ -303,205 +287,33 @@ deposit_transaction (void *cls, static enum GNUNET_GenericReturnValue parse_coin (struct MHD_Connection *connection, struct PurseDepositContext *pcc, - struct Coin *coin, + struct TEH_PurseDepositedCoin *coin, const json_t *jcoin) { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("amount", - TEH_currency, - &coin->amount), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &coin->cpi.denom_pub_hash), - TALER_JSON_spec_denom_sig ("ub_sig", - &coin->cpi.denom_sig), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &coin->cpi.h_age_commitment), - &coin->cpi.no_age_commitment), - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin->coin_sig), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &coin->cpi.coin_pub), - GNUNET_JSON_spec_end () - }; - - { - enum GNUNET_GenericReturnValue res; + enum GNUNET_GenericReturnValue iret; - res = TALER_MHD_parse_json_data (connection, - jcoin, - spec); - if (GNUNET_OK != res) - return res; - } if (GNUNET_OK != - TALER_wallet_purse_deposit_verify (TEH_base_url, - pcc->purse_pub, - &coin->amount, - &coin->cpi.coin_pub, - &coin->coin_sig)) - { - TALER_LOG_WARNING ("Invalid signature on /purses/$PID/deposit request\n"); - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_PURSE_DEPOSIT_COIN_SIGNATURE_INVALID, - TEH_base_url)) - ? GNUNET_NO : GNUNET_SYSERR; - } - /* check denomination exists and is valid */ - { - struct TEH_DenominationKey *dk; - MHD_RESULT mret; - - dk = TEH_keys_denomination_by_hash (&coin->cpi.denom_pub_hash, - connection, - &mret); - if (NULL == dk) - { - GNUNET_JSON_parse_free (spec); - return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR; - } - if (0 > TALER_amount_cmp (&dk->meta.value, - &coin->amount)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time)) - { - /* This denomination is past the expiration time for deposits */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &coin->cpi.denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - "PURSE DEPOSIT")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &coin->cpi.denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "PURSE DEPOSIT")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (dk->recoup_possible) - { - /* This denomination has been revoked */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &coin->cpi.denom_pub_hash, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - "PURSE DEPOSIT")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (dk->denom_pub.cipher != coin->cpi.denom_sig.cipher) - { - /* denomination cipher and denomination signature cipher not the same */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - } - - coin->deposit_fee = dk->meta.fees.deposit; - if (0 < TALER_amount_cmp (&coin->deposit_fee, - &coin->amount)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE, - NULL); - } - GNUNET_assert (0 <= - TALER_amount_subtract (&coin->amount_minus_fee, - &coin->amount, - &coin->deposit_fee)); - /* check coin signature */ - switch (dk->denom_pub.cipher) - { - case TALER_DENOMINATION_RSA: - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++; - break; - case TALER_DENOMINATION_CS: - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++; - break; - default: - break; - } - if (GNUNET_YES != - TALER_test_coin_valid (&coin->cpi, - &dk->denom_pub)) - { - TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (0 > - TALER_amount_add (&pcc->deposit_total, - &pcc->deposit_total, - &coin->amount_minus_fee)) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT, - "total deposit contribution"); - } - } + (iret = TEH_common_purse_deposit_parse_coin (connection, + coin, + jcoin))) + return iret; + if (GNUNET_OK != + (iret = TEH_common_deposit_check_purse_deposit ( + connection, + coin, + pcc->purse_pub, + pcc->min_age))) + return iret; + if (0 > + TALER_amount_add (&pcc->deposit_total, + &pcc->deposit_total, + &coin->amount_minus_fee)) { - MHD_RESULT mhd_ret = MHD_NO; - enum GNUNET_DB_QueryStatus qs; - - /* make sure coin is 'known' in database */ - for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++) - { - qs = TEH_make_coin_known (&coin->cpi, - connection, - &coin->known_coin_id, - &mhd_ret); - /* no transaction => no serialization failures should be possible */ - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; - } - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - GNUNET_break (0); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - "make_coin_known")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (qs < 0) - return (MHD_YES == mhd_ret) ? GNUNET_NO : GNUNET_SYSERR; + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT, + "total deposit contribution"); } return GNUNET_OK; } @@ -517,12 +329,12 @@ TEH_handler_purses_deposit ( .purse_pub = purse_pub, .exchange_timestamp = GNUNET_TIME_timestamp_get () }; - json_t *deposits; + const json_t *deposits; json_t *deposit; unsigned int idx; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("deposits", - &deposits), + GNUNET_JSON_spec_array_const ("deposits", + &deposits), GNUNET_JSON_spec_end () }; @@ -551,7 +363,6 @@ TEH_handler_purses_deposit ( (pcc.num_coins > TALER_MAX_FRESH_COINS) ) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, @@ -560,16 +371,22 @@ TEH_handler_purses_deposit ( { enum GNUNET_DB_QueryStatus qs; + struct GNUNET_TIME_Timestamp create_timestamp; struct GNUNET_TIME_Timestamp merge_timestamp; + bool was_deleted; + bool was_refunded; qs = TEH_plugin->select_purse ( TEH_plugin->cls, pcc.purse_pub, + &create_timestamp, &pcc.purse_expiration, &pcc.amount, &pcc.deposit_total, &pcc.h_contract_terms, - &merge_timestamp); + &merge_timestamp, + &was_deleted, + &was_refunded); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -592,22 +409,26 @@ TEH_handler_purses_deposit ( case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; /* handled below */ } - if (GNUNET_TIME_absolute_is_past (pcc.purse_expiration.abs_time)) + if (was_refunded || + was_deleted) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_GONE, - TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED, - NULL); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_GONE, + was_deleted + ? TALER_EC_EXCHANGE_GENERIC_PURSE_DELETED + : TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED, + GNUNET_TIME_timestamp2s (pcc.purse_expiration)); } } /* parse deposits */ pcc.coins = GNUNET_new_array (pcc.num_coins, - struct Coin); + struct TEH_PurseDepositedCoin); json_array_foreach (deposits, idx, deposit) { enum GNUNET_GenericReturnValue res; - struct Coin *coin = &pcc.coins[idx]; + struct TEH_PurseDepositedCoin *coin = &pcc.coins[idx]; res = parse_coin (connection, &pcc, @@ -615,7 +436,8 @@ TEH_handler_purses_deposit ( deposit); if (GNUNET_OK != res) { - GNUNET_JSON_parse_free (spec); + for (unsigned int i = 0; i<idx; i++) + TEH_common_purse_deposit_free_coin (&pcc.coins[i]); GNUNET_free (pcc.coins); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } @@ -625,7 +447,8 @@ TEH_handler_purses_deposit ( TEH_plugin->preflight (TEH_plugin->cls)) { GNUNET_break (0); - GNUNET_JSON_parse_free (spec); + for (unsigned int i = 0; i<pcc.num_coins; i++) + TEH_common_purse_deposit_free_coin (&pcc.coins[i]); GNUNET_free (pcc.coins); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -645,7 +468,8 @@ TEH_handler_purses_deposit ( &deposit_transaction, &pcc)) { - GNUNET_JSON_parse_free (spec); + for (unsigned int i = 0; i<pcc.num_coins; i++) + TEH_common_purse_deposit_free_coin (&pcc.coins[i]); GNUNET_free (pcc.coins); return mhd_ret; } @@ -672,8 +496,9 @@ TEH_handler_purses_deposit ( res = reply_deposit_success (connection, &pcc); + for (unsigned int i = 0; i<pcc.num_coins; i++) + TEH_common_purse_deposit_free_coin (&pcc.coins[i]); GNUNET_free (pcc.coins); - GNUNET_JSON_parse_free (spec); return res; } } |