From f3a4b00907410444055f47d4ff2074b44f981ad3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 5 Apr 2022 17:15:50 +0200 Subject: [PATCH] adapt to latest GNUnet API: GNUNET_JSON_spec_mark_optional() changed --- contrib/gana | 2 +- src/exchange/Makefile.am | 1 + src/exchange/taler-exchange-httpd_deposit.c | 6 +- src/exchange/taler-exchange-httpd_melt.c | 19 +-- src/exchange/taler-exchange-httpd_metrics.h | 3 +- .../taler-exchange-httpd_purses_create.c | 154 ++++++++++++------ .../taler-exchange-httpd_purses_create.h | 47 ++++++ .../taler-exchange-httpd_recoup-refresh.c | 6 +- src/exchange/taler-exchange-httpd_recoup.c | 6 +- .../taler-exchange-httpd_refreshes_reveal.c | 20 +-- src/exchangedb/exchange-0001-part.sql | 96 ++++++++++- src/exchangedb/plugin_exchangedb_postgres.c | 104 +++++++++++- src/include/taler_exchangedb_plugin.h | 27 ++- src/lib/exchange_api_common.c | 22 ++- src/lib/exchange_api_deposit.c | 3 +- src/lib/exchange_api_handle.c | 9 +- src/lib/exchange_api_link.c | 9 +- .../exchange_api_purse_create_with_deposit.c | 5 +- src/lib/exchange_api_refund.c | 12 +- ...ing_api_cmd_auditor_deposit_confirmation.c | 3 +- 20 files changed, 441 insertions(+), 113 deletions(-) create mode 100644 src/exchange/taler-exchange-httpd_purses_create.h diff --git a/contrib/gana b/contrib/gana index 8e5eda950..313216ee1 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 8e5eda9507f42af6a394d218a65082b3294855b7 +Subproject commit 313216ee11fe604d39ea5f1b71b1ad96a60391d9 diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index dd8edfb90..c4c1656a5 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -103,6 +103,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \ taler-exchange-httpd_metrics.c taler-exchange-httpd_metrics.h \ taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \ + taler-exchange-httpd_purses_create.c taler-exchange-httpd_purses_create.h \ taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \ taler-exchange-httpd_recoup-refresh.c taler-exchange-httpd_recoup-refresh.h \ taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \ diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 011f5f159..0ce5add33 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -224,14 +224,16 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.h_contract_terms), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &deposit.coin.h_age_commitment)), + &deposit.coin.h_age_commitment), + &deposit.coin.no_age_commitment), GNUNET_JSON_spec_fixed_auto ("coin_sig", &deposit.csig), GNUNET_JSON_spec_timestamp ("timestamp", &deposit.timestamp), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("refund_deadline", - &deposit.refund_deadline)), + &deposit.refund_deadline), + NULL), GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", &deposit.wire_deadline), GNUNET_JSON_spec_end () diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 2ff03023a..3d6f05c0a 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -120,9 +120,9 @@ struct MeltContext bool coin_is_dirty; /** - * True if @e rms is set. + * True if @e rms is missing. */ - bool have_rms; + bool no_rms; }; @@ -161,9 +161,9 @@ melt_transaction (void *cls, if (0 > (qs = TEH_plugin->do_melt (TEH_plugin->cls, - rmc->have_rms - ? &rmc->rms - : NULL, + rmc->no_rms + ? NULL + : &rmc->rms, &rmc->refresh_session, rmc->known_coin_id, &rmc->zombie_required, @@ -443,7 +443,8 @@ TEH_handler_melt (struct MHD_Connection *connection, &rmc.refresh_session.coin.denom_pub_hash), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("age_commitment_hash", - &rmc.refresh_session.coin.h_age_commitment)), + &rmc.refresh_session.coin.h_age_commitment), + &rmc.refresh_session.coin.no_age_commitment), GNUNET_JSON_spec_fixed_auto ("confirm_sig", &rmc.refresh_session.coin_sig), TALER_JSON_spec_amount ("value_with_fee", @@ -453,7 +454,8 @@ TEH_handler_melt (struct MHD_Connection *connection, &rmc.refresh_session.rc), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("rms", - &rmc.rms)), + &rmc.rms), + &rmc.no_rms), GNUNET_JSON_spec_end () }; @@ -469,9 +471,6 @@ TEH_handler_melt (struct MHD_Connection *connection, return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; } - rmc.have_rms = (NULL != json_object_get (root, - "rms")); - { MHD_RESULT res; diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h index a74fa44fd..d1d20d9aa 100644 --- a/src/exchange/taler-exchange-httpd_metrics.h +++ b/src/exchange/taler-exchange-httpd_metrics.h @@ -35,7 +35,8 @@ enum TEH_MetricTypeRequest TEH_MT_REQUEST_DEPOSIT = 1, TEH_MT_REQUEST_WITHDRAW = 2, TEH_MT_REQUEST_MELT = 3, - TEH_MT_REQUEST_COUNT = 4 /* MUST BE LAST! */ + TEH_MT_REQUEST_PURSE_CREATE = 4, + TEH_MT_REQUEST_COUNT = 5 /* MUST BE LAST! */ }; /** diff --git a/src/exchange/taler-exchange-httpd_purses_create.c b/src/exchange/taler-exchange-httpd_purses_create.c index 326dad5c3..610320da0 100644 --- a/src/exchange/taler-exchange-httpd_purses_create.c +++ b/src/exchange/taler-exchange-httpd_purses_create.c @@ -63,6 +63,11 @@ struct Coin * 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; }; @@ -141,6 +146,10 @@ struct PurseCreateContext */ unsigned int num_coins; + /** + * Minimum age for deposits into this purse. + */ + uint32_t min_age; }; @@ -165,7 +174,7 @@ reply_create_success (struct MHD_Connection *connection, pcc->exchange_timestamp, pcc->purse_expiration, &pcc->amount, - &pcc->total_deposited, + &pcc->deposit_total, pcc->purse_pub, &pcc->merge_pub, &pcc->h_contract_terms, @@ -180,8 +189,8 @@ reply_create_success (struct MHD_Connection *connection, return TALER_MHD_REPLY_JSON_PACK ( connection, MHD_HTTP_OK, - GNUNET_JSON_pack_amount ("total_deposited", - &pcc->deposit_total), + TALER_JSON_pack_amount ("total_deposited", + &pcc->deposit_total), GNUNET_JSON_pack_timestamp ("exchange_timestamp", pcc->exchange_timestamp), GNUNET_JSON_pack_data_auto ("exchange_sig", @@ -215,7 +224,7 @@ create_transaction (void *cls, /* 1) create purse */ qs = TEH_plugin->insert_purse_request (TEH_plugin->cls, - &pcc->purse_pub, + pcc->purse_pub, &pcc->merge_pub, pcc->purse_expiration, &pcc->h_contract_terms, @@ -244,6 +253,7 @@ create_transaction (void *cls, struct TALER_Amount target_amount; struct TALER_Amount balance; struct TALER_PurseContractSignatureP purse_sig; + uint32_t min_age; TEH_plugin->rollback (TEH_plugin->cls); qs = TEH_plugin->select_purse_request (TEH_plugin->cls, @@ -251,6 +261,7 @@ create_transaction (void *cls, &merge_pub, &purse_expiration, &h_contract_terms, + &min_age, &target_amount, &balance, &purse_sig); @@ -270,8 +281,10 @@ create_transaction (void *cls, MHD_HTTP_CONFLICT, TALER_JSON_pack_ec ( TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA), - GNUNET_JSON_pack_amount ("amount", - &amount), + TALER_JSON_pack_amount ("amount", + &target_amount), + GNUNET_JSON_pack_uint64 ("min_age", + min_age), GNUNET_JSON_pack_timestamp ("purse_expiration", purse_expiration), GNUNET_JSON_pack_data_auto ("purse_sig", @@ -287,15 +300,17 @@ create_transaction (void *cls, { struct Coin *coin = &pcc->coins[i]; bool balance_ok = false; + bool conflict = true; qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls, - &pcc->purse_pub, + pcc->purse_pub, &coin->cpi.coin_pub, &coin->amount, &coin->coin_sig, &coin->amount_minus_fee, - &balance_ok); - if (qs < 0) + &balance_ok, + &conflict); + if (qs <= 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) return qs; @@ -313,14 +328,58 @@ create_transaction (void *cls, = TEH_RESPONSE_reply_coin_insufficient_funds ( connection, TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, - &coin->pci.coin_pub); + &coin->cpi.coin_pub); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (conflict) + { + struct TALER_Amount amount; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_CoinSpendSignatureP coin_sig; + char *partner_url = NULL; + + TEH_plugin->rollback (TEH_plugin->cls); + qs = TEH_plugin->get_purse_deposit (TEH_plugin->cls, + pcc->purse_pub, + &coin->cpi.coin_pub, + &amount, + &coin_sig, + &partner_url); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + TALER_LOG_WARNING ( + "Failed to fetch purse deposit information from database\n"); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "get purse deposit"); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + *mhd_ret + = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_CONFLICT, + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA), + GNUNET_JSON_pack_data_auto ("coin_pub", + &coin_pub), + GNUNET_JSON_pack_data_auto ("coin_sig", + &coin_sig), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("partner_url", + partner_url)), + TALER_JSON_pack_amount ("amount", + &amount)); + GNUNET_free (partner_url); return GNUNET_DB_STATUS_HARD_ERROR; } } /* 3) if present, persist contract */ in_conflict = true; qs = TEH_plugin->insert_contract (TEH_plugin->cls, - &pcc->purse_pub, + pcc->purse_pub, &pcc->contract_pub, pcc->econtract_size, pcc->econtract, @@ -345,12 +404,12 @@ create_transaction (void *cls, void *econtract; struct GNUNET_HashCode h_econtract; - qs = select_contract (cls, - &pcc->purse_pub, - &pub_ckey, - &econtract_sig, - &econtract_size, - &econtract); + qs = TEH_plugin->select_contract (TEH_plugin->cls, + pcc->purse_pub, + &pub_ckey, + &econtract_sig, + &econtract_size, + &econtract); if (qs <= 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -411,8 +470,8 @@ parse_coin (struct MHD_Connection *connection, &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), + &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", @@ -432,10 +491,10 @@ parse_coin (struct MHD_Connection *connection, if (GNUNET_OK != TALER_wallet_purse_deposit_verify (TEH_base_url, - &pcc->purse_pub, + pcc->purse_pub, &pcc->amount, - &coin->coin_pub, - &coin->csig)) + &coin->cpi.coin_pub, + &coin->coin_sig)) { TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n"); GNUNET_JSON_parse_free (spec); @@ -451,13 +510,13 @@ parse_coin (struct MHD_Connection *connection, struct TEH_DenominationKey *dk; MHD_RESULT mret; - dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash, + 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: + return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR; } if (0 > TALER_amount_cmp (&dk->meta.value, &coin->amount)) @@ -478,10 +537,10 @@ parse_coin (struct MHD_Connection *connection, return (MHD_YES == TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - &coin->denom_pub_hash, + &coin->cpi.denom_pub_hash, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, "PURSE CREATE")) - ? GNUNET_NO : GNUNET_SYSERR: + ? GNUNET_NO : GNUNET_SYSERR; } if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) { @@ -490,7 +549,7 @@ parse_coin (struct MHD_Connection *connection, return (MHD_YES == TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - &coin->denom_pub_hash, + &coin->cpi.denom_pub_hash, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, "PURSE CREATE")) ? GNUNET_NO : GNUNET_SYSERR; @@ -502,12 +561,12 @@ parse_coin (struct MHD_Connection *connection, return (MHD_YES == TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - &deposit.coin.denom_pub_hash, + &coin->cpi.denom_pub_hash, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, "PURSE CREATE")) ? GNUNET_NO : GNUNET_SYSERR; } - if (dk->denom_pub.cipher != deposit.coin.denom_sig.cipher) + if (dk->denom_pub.cipher != coin->cpi.denom_sig.cipher) { /* denomination cipher and denomination signature cipher not the same */ GNUNET_JSON_parse_free (spec); @@ -531,9 +590,9 @@ parse_coin (struct MHD_Connection *connection, NULL); } GNUNET_assert (0 <= - TALER_amount_subtact (&coin->amount_minus_fee, - &coin->amount, - &coin->deposit_fee)); + TALER_amount_subtract (&coin->amount_minus_fee, + &coin->amount, + &coin->deposit_fee)); /* check coin signature */ switch (dk->denom_pub.cipher) { @@ -547,7 +606,7 @@ parse_coin (struct MHD_Connection *connection, break; } if (GNUNET_YES != - TALER_test_coin_valid (&deposit.coin, + TALER_test_coin_valid (&coin->cpi, &dk->denom_pub)) { TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); @@ -578,9 +637,9 @@ parse_coin (struct MHD_Connection *connection, /* make sure coin is 'known' in database */ for (unsigned int tries = 0; triescpi, connection, - &pcc.known_coin_id, + &coin->known_coin_id, &mhd_ret); /* no transaction => no serialization failures should be possible */ if (GNUNET_DB_STATUS_SOFT_ERROR != qs) @@ -611,7 +670,7 @@ TEH_handler_purses_create ( { struct PurseCreateContext pcc = { .purse_pub = purse_pub, - .exchange_timestamp = GNUNET_TIME_timestamp_get (); + .exchange_timestamp = GNUNET_TIME_timestamp_get () }; json_t *deposits; json_t *deposit; @@ -623,15 +682,18 @@ TEH_handler_purses_create ( GNUNET_JSON_spec_uint32 ("min_age", &pcc.min_age), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_var_size ("econtract", - &pcc.econtract, - &pcc.ecotract_size)), + GNUNET_JSON_spec_varsize ("econtract", + &pcc.econtract, + &pcc.econtract_size), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("econtract_sig", - &pcc.econtract_sig)), + &pcc.econtract_sig), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("contract_pub", - &pcc.contract_pub)), + &pcc.contract_pub), + NULL), GNUNET_JSON_spec_fixed_auto ("merge_pub", &pcc.merge_pub), GNUNET_JSON_spec_fixed_auto ("purse_sig", @@ -694,7 +756,7 @@ TEH_handler_purses_create ( GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_PARAMETER_MALFORMED, + TALER_EC_GENERIC_PARAMETER_MALFORMED, "deposits"); } gf = TEH_keys_global_fee_by_time (TEH_keys_get_state (), @@ -709,8 +771,8 @@ TEH_handler_purses_create ( NULL); } /* parse deposits */ - pcc.coins = GNUNET_new_array (struct Coin, - pcc.num_coins); + pcc.coins = GNUNET_new_array (pcc.num_coins, + struct Coin); json_array_foreach (deposits, idx, deposit) { enum GNUNET_GenericReturnValue res; @@ -742,12 +804,12 @@ TEH_handler_purses_create ( TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; if (GNUNET_OK != - TALER_wallet_purse_create_verify (&pcc.purse_expiration, + TALER_wallet_purse_create_verify (pcc.purse_expiration, &pcc.h_contract_terms, &pcc.merge_pub, pcc.min_age, &pcc.amount, - &pcc.purse_pub, + pcc.purse_pub, &pcc.purse_sig)) { TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n"); diff --git a/src/exchange/taler-exchange-httpd_purses_create.h b/src/exchange/taler-exchange-httpd_purses_create.h new file mode 100644 index 000000000..4dfbabf9b --- /dev/null +++ b/src/exchange/taler-exchange-httpd_purses_create.h @@ -0,0 +1,47 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_purses_create.h + * @brief Handle /purses/$PID/create requests + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_PURSES_CREATE_H +#define TALER_EXCHANGE_HTTPD_PURSES_CREATE_H + +#include +#include +#include "taler-exchange-httpd.h" + + +/** + * Handle a "/purses/$PURSE_PUB/create" request. Parses the JSON, and, if + * successful, passes the JSON data to #create_transaction() to further check + * the details of the operation specified. If everything checks out, this + * will ultimately lead to the "purses create" being executed, or rejected. + * + * @param connection the MHD connection to handle + * @param purse_pub public key of the purse + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_purses_create (struct MHD_Connection *connection, + const struct + TALER_PurseContractPublicKeyP *purse_pub, + const json_t *root); + + +#endif diff --git a/src/exchange/taler-exchange-httpd_recoup-refresh.c b/src/exchange/taler-exchange-httpd_recoup-refresh.c index dbeadfbd4..a00eeba4e 100644 --- a/src/exchange/taler-exchange-httpd_recoup-refresh.c +++ b/src/exchange/taler-exchange-httpd_recoup-refresh.c @@ -390,10 +390,12 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection, &coin_sig), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &coin.h_age_commitment)), + &coin.h_age_commitment), + &coin.no_age_commitment), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("cs_nonce", - &nonce)), + &nonce), + NULL), GNUNET_JSON_spec_end () }; diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c index 101e7bffd..6bda8af9e 100644 --- a/src/exchange/taler-exchange-httpd_recoup.c +++ b/src/exchange/taler-exchange-httpd_recoup.c @@ -403,10 +403,12 @@ TEH_handler_recoup (struct MHD_Connection *connection, &coin_sig), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &coin.h_age_commitment)), + &coin.h_age_commitment), + &coin.no_age_commitment), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("cs_nonce", - &nonce)), + &nonce), + NULL), GNUNET_JSON_spec_end () }; diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index e1cf6ab2c..8a5d8a802 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -132,9 +132,9 @@ struct RevealContext unsigned int num_fresh_coins; /** - * True if @e rms was provided. + * True if @e rms was not provided. */ - bool have_rms; + bool no_rms; }; @@ -337,9 +337,9 @@ check_commitment (struct RevealContext *rctx, } TALER_refresh_get_commitment (&rc_expected, TALER_CNC_KAPPA, - rctx->have_rms - ? &rctx->rms - : NULL, + rctx->no_rms + ? NULL + : &rctx->rms, rctx->num_fresh_coins, rcs, &rctx->melt.session.coin.coin_pub, @@ -484,7 +484,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, if (NULL == dks[i]) return ret; if ( (TALER_DENOMINATION_CS == dks[i]->denom_pub.cipher) && - (! rctx->have_rms) ) + (rctx->no_rms) ) { return TALER_MHD_reply_with_error ( connection, @@ -937,10 +937,12 @@ TEH_handler_reveal (struct TEH_RequestContext *rc, &new_denoms_h), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("old_age_commitment", - &old_age_commitment)), + &old_age_commitment), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("rms", - &rctx.rms)), + &rctx.rms), + &rctx.no_rms), GNUNET_JSON_spec_end () }; @@ -981,8 +983,6 @@ TEH_handler_reveal (struct TEH_RequestContext *rc, return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } } - rctx.have_rms = (NULL != json_object_get (root, - "rms")); /* Check we got enough transfer private keys */ /* Note we do +1 as 1 row (cut-and-choose!) is missing! */ diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index c068ff6f9..2c416f03e 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -2469,22 +2469,108 @@ END $$; - - - - CREATE OR REPLACE FUNCTION exchange_do_purse_deposit( + IN in_partner_id INT8, IN in_purse_pub BYTEA, IN in_amount_with_fee_val INT8, IN in_amount_with_fee_frac INT4, IN in_coin_pub BYTEA, IN in_coin_sig BYTEA, + IN in_amount_without_fee_val INT8, + IN in_amount_without_fee_frac INT4, OUT out_balance_ok BOOLEAN, OUT out_conflict BOOLEAN) LANGUAGE plpgsql AS $$ BEGIN - -- FIXME + +-- Store the deposit request. +INSERT INTO purse_deposits + (partner_serial_id + ,purse_pub + ,coin_pub + ,amount_with_fee_val + ,amount_with_fee_frac + ,coin_sig) + VALUES + (in_partner_id + ,in_purse_pub + ,in_coin_pub + ,in_amount_with_fee_val + ,in_amount_with_fee_frac + ,in_coin_sig) + ON CONFLICT DO NOTHING; + +IF NOT FOUND +THEN + -- Idempotency check: check if coin_sig is the same, + -- if so, success, otherwise conflict! + SELECT + 1 + FROM purse_deposits + WHERE coin_pub = in_coin_pub + AND purse_pub = in_purse_pub + AND coin_sig = in_cion_sig; + IF NOT FOUND + THEN + -- Deposit exists, but with differences. Not allowed. + out_balance_ok=FALSE; + out_conflict=TRUE; + RETURN; + END IF; +END IF; + + +-- Debit the coin +-- Check and update balance of the coin. +UPDATE known_coins + SET + remaining_frac=remaining_frac-in_amount_with_fee_frac + + CASE + WHEN remaining_frac < in_amount_with_fee_frac + THEN 100000000 + ELSE 0 + END, + remaining_val=remaining_val-in_amount_with_fee_val + - CASE + WHEN remaining_frac < in_amount_with_fee_frac + THEN 1 + ELSE 0 + END + WHERE coin_pub=in_coin_pub + AND ( (remaining_val > in_amount_with_fee_val) OR + ( (remaining_frac >= in_amount_with_fee_frac) AND + (remaining_val >= in_amount_with_fee_val) ) ); + +IF NOT FOUND +THEN + -- Insufficient balance. + out_balance_ok=FALSE; + out_conflict=FALSE; + RETURN; +END IF; + + +-- Credit the purse. +UPDATE purse_requests + SET + balance_frac=balance_frac+in_amount_without_fee_frac + - CASE + WHEN balance_frac+in_amount_without_fee_frac >= 100000000 + THEN 100000000 + ELSE 0 + END, + balance_val=balance_val+in_amount_without_fee_val + + CASE + WHEN balance_frac+in_amount_without_fee_frac >= 100000000 + THEN 1 + ELSE 0 + END + WHERE purse_pub=in_purse_pub; + +out_conflict=FALSE; +out_balance_ok=TRUE; + END $$; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 64cc0a77e..aed69725b 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -799,6 +799,15 @@ prepare_statements (struct PostgresClosure *pg) " FROM exchange_do_deposit" " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);", 17), + /* used in postgres_do_purse_deposit() */ + GNUNET_PQ_make_prepare ( + "call_purse_deposit", + "SELECT " + " out_balance_ok AS balance_ok" + ",out_conflict AS conflict" + " FROM exchange_do_purse_deposit" + " ($1,$2,$3,$4,$5,$6,$7,$8);", + 8), /* Used in #postgres_do_melt() to melt a coin. */ GNUNET_PQ_make_prepare ( "call_melt", @@ -3342,13 +3351,19 @@ prepare_statements (struct PostgresClosure *pg) " FROM purse_requests" " WHERE merge_pub=$1;", 1), - /* Used in #postgres_do_purse_deposit() */ + /* Used in #postgres_get_purse_deposit */ GNUNET_PQ_make_prepare ( - "call_purse_deposit", - "SELECT 1" - " FROM exchange_do_purse_deposit" - " ($1, $2, $3, $4, $5);", - 5), + "select_purse_deposit_by_coin_pub", + "SELECT " + " coin_sig" + ",amount_with_fee_val" + ",amount_with_fee_frac" + ",partner_base_url" + " FROM purse_deposits" + " LEFT JOIN partners USING (partner_serial_id)" + " WHERE coin_pub=$2" + " AND purse_pub=$1;", + 2), /* Used in #postgres_do_purse_merge() */ GNUNET_PQ_make_prepare ( "call_purse_merge", @@ -13181,10 +13196,79 @@ postgres_do_purse_deposit ( const struct TALER_Amount *amount, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_minus_fee, - bool *balance_ok) + bool *balance_ok, + bool *conflict) { - GNUNET_break (0); // FIXME - return GNUNET_DB_STATUS_HARD_ERROR; + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (0), /* FIXME: partner ID */ + GNUNET_PQ_query_param_auto_from_type (purse_pub), + TALER_PQ_query_param_amount (amount), + GNUNET_PQ_query_param_auto_from_type (coin_pub), + GNUNET_PQ_query_param_auto_from_type (coin_sig), + TALER_PQ_query_param_amount (amount_minus_fee), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("balance_ok", + balance_ok), + GNUNET_PQ_result_spec_bool ("conflict", + conflict), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "call_purse_deposit", + params, + rs); +} + + +/** + * Function called to obtain a coin deposit data from + * depositing the coin into a purse. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param purse_pub purse to credit + * @param coin_pub coin to deposit (debit) + * @param[out] amount set fraction of the coin's value that was deposited (with fee) + * @param[out] coin_sig set to signature affirming the operation + * @param[out] partner_url set to the URL of the partner exchange, or NULL for ourselves, must be freed by caller + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_get_purse_deposit ( + void *cls, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_Amount *amount, + struct TALER_CoinSpendSignatureP *coin_sig, + char **partner_url) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (purse_pub), + GNUNET_PQ_query_param_auto_from_type (coin_pub), + GNUNET_PQ_query_param_end + }; + bool is_null; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("coin_sig", + coin_sig), + TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", + amount), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("partner_base_url", + partner_url), + &is_null), + GNUNET_PQ_result_spec_end + }; + + *partner_url = NULL; + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "select_purse_deposit_by_coin_pub", + params, + rs); } @@ -13589,6 +13673,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &postgres_select_purse_by_merge_pub; plugin->do_purse_deposit = &postgres_do_purse_deposit; + plugin->get_purse_deposit + = &postgres_get_purse_deposit; plugin->do_purse_merge = &postgres_do_purse_merge; plugin->select_purse_merge diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 9c9410d66..c065f581f 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4567,6 +4567,8 @@ struct TALER_EXCHANGEDB_Plugin * remaining balance is below @a amount; * in this case, the return value will be * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure + * @param[out] conflict the same coin was deposited into + * this purse with a different amount already * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -4577,7 +4579,30 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_Amount *amount, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_minus_fee, - bool *balance_ok); + bool *balance_ok, + bool *conflict); + + + /** + * Function called to obtain a coin deposit data from + * depositing the coin into a purse. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param purse_pub purse to credit + * @param coin_pub coin to deposit (debit) + * @param[out] amount set fraction of the coin's value that was deposited (with fee) + * @param[out] coin_sig set to signature affirming the operation + * @param[out] partner_url set to the URL of the partner exchange, or NULL for ourselves, must be freed by caller + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*get_purse_deposit)( + void *cls, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_Amount *amount, + struct TALER_CoinSpendSignatureP *coin_sig, + char **partner_url); /** diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index df1c43e4f..04f2a4df8 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -447,7 +447,8 @@ TALER_EXCHANGE_verify_coin_history ( struct TALER_MerchantPublicKeyP merchant_pub; struct GNUNET_TIME_Timestamp refund_deadline = {0}; struct TALER_CoinSpendSignatureP sig; - struct TALER_AgeCommitmentHash hac = {0}; + struct TALER_AgeCommitmentHash hac; + bool no_hac; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), @@ -459,12 +460,14 @@ TALER_EXCHANGE_verify_coin_history ( h_denom_pub), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &hac)), + &hac), + &no_hac), GNUNET_JSON_spec_timestamp ("timestamp", &wallet_timestamp), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline)), + &refund_deadline), + NULL), TALER_JSON_spec_amount_any ("deposit_fee", &fee), GNUNET_JSON_spec_fixed_auto ("merchant_pub", @@ -486,7 +489,7 @@ TALER_EXCHANGE_verify_coin_history ( &fee, &h_wire, &h_contract_terms, - TALER_AgeCommitmentHash_isNullOrZero (&hac) ? NULL : &hac, + no_hac ? NULL : &hac, NULL /* h_extensions! */, h_denom_pub, wallet_timestamp, @@ -519,7 +522,8 @@ TALER_EXCHANGE_verify_coin_history ( { struct TALER_CoinSpendSignatureP sig; struct TALER_RefreshCommitmentP rc; - struct TALER_AgeCommitmentHash h_age_commitment = {0}; + struct TALER_AgeCommitmentHash h_age_commitment; + bool no_hac; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), @@ -529,7 +533,8 @@ TALER_EXCHANGE_verify_coin_history ( h_denom_pub), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &h_age_commitment)), + &h_age_commitment), + &no_hac), TALER_JSON_spec_amount_any ("melt_fee", &fee), GNUNET_JSON_spec_end () @@ -566,8 +571,9 @@ TALER_EXCHANGE_verify_coin_history ( &fee, &rc, h_denom_pub, - TALER_AgeCommitmentHash_isNullOrZero (&h_age_commitment) ? - NULL : &h_age_commitment, + no_hac + ? NULL + : &h_age_commitment, coin_pub, &sig)) { diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c index 947a42561..67f595bfb 100644 --- a/src/lib/exchange_api_deposit.c +++ b/src/lib/exchange_api_deposit.c @@ -333,7 +333,8 @@ handle_deposit_finished (void *cls, &dh->exchange_pub), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("transaction_base_url", - &dr.details.success.transaction_base_url)), + &dr.details.success.transaction_base_url), + NULL), GNUNET_JSON_spec_timestamp ("exchange_timestamp", &dh->exchange_timestamp), GNUNET_JSON_spec_end () diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index feab4ac44..6528c56d1 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -750,7 +750,8 @@ decode_keys_json (const json_t *resp_obj, ¤cy), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("wallet_balance_limit_without_kyc", - &key_data->wallet_balance_limit_without_kyc)), + &key_data->wallet_balance_limit_without_kyc), + NULL), GNUNET_JSON_spec_end () }; @@ -895,11 +896,13 @@ decode_keys_json (const json_t *resp_obj, struct GNUNET_JSON_Specification ext_spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("extensions", - &extensions)), + &extensions), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ( "extensions_sig", - &extensions_sig)), + &extensions_sig), + NULL), GNUNET_JSON_spec_end () }; diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c index 5840cac63..ddc763c33 100644 --- a/src/lib/exchange_api_link.c +++ b/src/lib/exchange_api_link.c @@ -97,6 +97,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, union TALER_DenominationBlindingKeyP bks; struct TALER_ExchangeWithdrawValues alg_values; struct TALER_CsNonce nonce; + bool no_nonce; uint32_t coin_idx; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_denom_pub ("denom_pub", @@ -111,7 +112,8 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, &coin_idx), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("cs_nonce", - &nonce)), + &nonce), + &no_nonce), GNUNET_JSON_spec_end () }; struct TALER_TransferSecretP secret; @@ -119,9 +121,6 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, struct TALER_CoinPubHashP c_hash; /* parse reply */ - memset (&nonce, - 0, - sizeof (nonce)); if (GNUNET_OK != GNUNET_JSON_parse (json, spec, @@ -180,7 +179,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh, } if (TALER_DENOMINATION_CS == alg_values.cipher) { - if (GNUNET_is_zero (&nonce)) + if (no_nonce) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); diff --git a/src/lib/exchange_api_purse_create_with_deposit.c b/src/lib/exchange_api_purse_create_with_deposit.c index d8f86de0d..f5871fa34 100644 --- a/src/lib/exchange_api_purse_create_with_deposit.c +++ b/src/lib/exchange_api_purse_create_with_deposit.c @@ -259,7 +259,7 @@ TALER_EXCHANGE_purse_create_with_deposit ( struct TALER_ContractDiffiePublicP contract_pub; char arg_str[sizeof (pch->purse_pub) * 2 + 32]; char *url; - uint32_t min_age; + uint32_t min_age = 0; pch = GNUNET_new (struct TALER_EXCHANGE_PurseCreateDepositHandle); pch->exchange = exchange; @@ -272,7 +272,8 @@ TALER_EXCHANGE_purse_create_with_deposit ( &pch->purse_value_after_fees), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint32 ("minimum_age", - &min_age)), + &min_age), + NULL), GNUNET_JSON_spec_end () }; diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c index 6369a5bd6..004661b00 100644 --- a/src/lib/exchange_api_refund.c +++ b/src/lib/exchange_api_refund.c @@ -231,7 +231,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, struct TALER_Amount deposit_fee; struct TALER_MerchantWireHashP h_wire; struct TALER_PrivateContractHashP h_contract_terms; - struct TALER_AgeCommitmentHash h_age_commitment = {{{0}}}; + struct TALER_AgeCommitmentHash h_age_commitment; + bool no_hac; // struct TALER_ExtensionContractHashP h_extensions; // FIXME! struct TALER_DenominationHashP h_denom_pub; struct GNUNET_TIME_Timestamp wallet_timestamp; @@ -249,7 +250,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, &h_denom_pub), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &h_age_commitment)), + &h_age_commitment), + &no_hac), GNUNET_JSON_spec_timestamp ("timestamp", &wallet_timestamp), GNUNET_JSON_spec_timestamp ("refund_deadline", @@ -275,8 +277,10 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, &deposit_fee, &h_wire, &h_contract_terms, - &h_age_commitment, - NULL /* h_extensions! */, + no_hac + ? NULL + : &h_age_commitment, + NULL /* FIXME-OEC: h_extensions! */, &h_denom_pub, wallet_timestamp, &merchant_pub, diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c index beeeb551c..d99b12937 100644 --- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c +++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c @@ -291,7 +291,8 @@ deposit_confirmation_run (void *cls, ×tamp), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline)), + &refund_deadline), + NULL), GNUNET_JSON_spec_end () };