diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c index 31ff57c6b..828877ab5 100644 --- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c +++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c @@ -74,9 +74,9 @@ struct AgeRevealContext struct TALER_Amount total_fee; /** - * #num_coins hashes of blinded coins. + * #num_coins hashes of blinded coin planchets. */ - struct TALER_BlindedCoinHashP *coin_evs; + struct TALER_BlindedPlanchet *coin_evs; /** * secrets for #num_coins*(kappa - 1) disclosed coins. @@ -90,6 +90,39 @@ struct AgeRevealContext struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment; }; + +/** + * Information per planchet in the batch. + */ +struct PlanchetContext +{ + + /** + * Hash of the (blinded) message to be signed by the Exchange. + */ + struct TALER_BlindedCoinHashP h_coin_envelope; + + /** + * Value of the coin being exchanged (matching the denomination key) + * plus the transaction fee. We include this in what is being + * signed so that we can verify a reserve's remaining total balance + * without needing to access the respective denomination key + * information each time. + */ + struct TALER_Amount amount_with_fee; + + /** + * Blinded planchet. + */ + struct TALER_BlindedPlanchet blinded_planchet; + + /** + * Set to the resulting signed coin data to be returned to the client. + */ + struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; + +}; + /** * Helper function to free resources in the context */ @@ -198,11 +231,11 @@ parse_age_withdraw_reveal_json ( /* Parse blinded envelopes */ actx->coin_evs = GNUNET_new_array (actx->num_coins, - struct TALER_BlindedCoinHashP); + struct TALER_BlindedPlanchet); json_array_foreach (j_coin_evs, idx, value) { struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, &actx->coin_evs[idx]), + TALER_JSON_spec_blinded_planchet (NULL, &actx->coin_evs[idx]), GNUNET_JSON_spec_end () }; @@ -220,6 +253,22 @@ parse_age_withdraw_reveal_json ( msg); goto EXIT; } + + /* Check for duplicate planchets */ + for (unsigned int i = 0; i < idx; i++) + { + if (0 == TALER_blinded_planchet_cmp (&actx->coin_evs[idx], + &actx->coin_evs[i])) + { + GNUNET_break_op (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "duplicate planchet"); + goto EXIT; + } + + } }; /* Parse diclosed keys */ @@ -308,7 +357,7 @@ find_original_commitment ( case GNUNET_DB_STATUS_SOFT_ERROR: /* FIXME oec: Do we queue a result in this case or retry? */ default: - GNUNET_break (0); /* should be impossible */ + GNUNET_break (0); *result = TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, @@ -414,6 +463,7 @@ denomination_is_valid ( * @param connection The HTTP connection to the client * @param len The lengths of the array @a denoms_h * @param denoms_h array of hashes of denomination public keys + * @param coin_evs array of blinded coin planchets * @param[out] dks On success, will be filled with the denomination keys. Caller must deallocate. * @param amount_with_fee The committed amount including fees * @param[out] total_amount On success, will contain the total sum of all denominations @@ -427,6 +477,7 @@ are_denominations_valid ( struct MHD_Connection *connection, uint32_t len, const struct TALER_DenominationHashP *denoms_h, + const struct TALER_BlindedPlanchet *coin_evs, struct TEH_DenominationKey **dks, const struct TALER_Amount *amount_with_fee, struct TALER_Amount *total_amount, @@ -458,7 +509,16 @@ are_denominations_valid ( &denoms_h[i], dks[i], result)) + return GNUNET_SYSERR; + + /* Ensure the ciphers from the planchets match the denominations' */ + if (dks[i]->denom_pub.cipher != coin_evs[i].cipher) { + GNUNET_break_op (0); + *result = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, + NULL); return GNUNET_SYSERR; } @@ -468,9 +528,9 @@ are_denominations_valid ( total_amount, &dks[i]->meta.value)) { - GNUNET_break (0); + GNUNET_break_op (0); *result = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, + MHD_HTTP_BAD_REQUEST, TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW, "amount"); return GNUNET_SYSERR; @@ -482,9 +542,9 @@ are_denominations_valid ( total_fee, &dks[i]->meta.fees.withdraw)) { - GNUNET_break (0); + GNUNET_break_op (0); *result = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, + MHD_HTTP_BAD_REQUEST, TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW, "fee"); return GNUNET_SYSERR; @@ -504,9 +564,10 @@ are_denominations_valid ( if (0 != TALER_amount_cmp (&sum, amount_with_fee)) { GNUNET_break_op (0); - *result = TALER_MHD_reply_with_ec (connection, - TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT, - NULL); + *result = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_INCORRECT, + NULL); return GNUNET_SYSERR; } } @@ -537,7 +598,7 @@ are_denominations_valid ( * @param max_age Maximum age allowed for the age restriction * @param noreveal_idx Index that was given to the client in response to the age-withdraw request * @param num_coins Number of coins - * @param coin_evs The Hashes of the undisclosed, blinded coins, @a num_coins many + * @param coin_evs The blindet planchets of the undisclosed coins, @a num_coins many * @param denom_keys The array of denomination keys, @a num_coins. Needed to detect Clause-Schnorr-based denominations * @param disclosed_coin_secrets The secrets of the disclosed coins, (TALER_CNC_KAPPA - 1)*num_coins many * @param[out] result On error, a HTTP-response will be queued and result set accordingly @@ -550,7 +611,7 @@ verify_commitment_and_max_age ( const uint32_t max_age, const uint32_t noreveal_idx, const uint32_t num_coins, - const struct TALER_BlindedCoinHashP *coin_evs, + const struct TALER_BlindedPlanchet *coin_evs, const struct TEH_DenominationKey *denom_keys, const struct TALER_PlanchetMasterSecretP *disclosed_coin_secrets, MHD_RESULT *result) @@ -735,21 +796,47 @@ verify_commitment_and_max_age ( * @return GNUNET_OK on success, GNUNET_SYSERR otherwise */ static enum GNUNET_GenericReturnValue -sign_and_persist_blinded_coins ( +finalize_age_withdraw_and_sign ( struct MHD_Connection *connection, - const struct TALER_AgeWithdrawCommitmentHashP *h_commitment_orig, + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, const uint32_t num_coins, - const struct TALER_BlindedCoinHashP *coin_evs, + const struct TALER_BlindedPlanchet *coin_evs, const struct TEH_DenominationKey *denom_keys, MHD_RESULT *result) { enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; + struct TEH_CoinSignData csds[num_coins]; + struct TALER_BlindedDenominationSignature bss[num_coins]; + + for (uint32_t i = 0; iconnection, + root, + spec); + if (GNUNET_OK != ret) { - ret = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_OK != ret) - { - GNUNET_break_op (0); - return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; - } + GNUNET_break_op (0); + return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; } @@ -814,6 +899,7 @@ TEH_handler_age_withdraw_reveal ( rc->connection, actx.num_coins, actx.denoms_h, + actx.coin_evs, &actx.denom_keys, &actx.commitment.amount_with_fee, &actx.total_amount, @@ -821,8 +907,8 @@ TEH_handler_age_withdraw_reveal ( &result)) break; - /* Verify the computed h_commitment equals the committed one and that - * coins have a maximum age group corresponding max_age (age-mask dependent) */ + /* Verify the computed h_commitment equals the committed one and that coins + * have a maximum age group corresponding max_age (age-mask dependent) */ if (GNUNET_OK != verify_commitment_and_max_age ( rc->connection, &actx.commitment.h_commitment, @@ -836,7 +922,7 @@ TEH_handler_age_withdraw_reveal ( break; /* Finally, sign and persist the coins */ - if (GNUNET_OK != sign_and_persist_blinded_coins ( + if (GNUNET_OK != finalize_age_withdraw_and_sign ( rc->connection, &actx.commitment.h_commitment, actx.num_coins, diff --git a/src/exchangedb/pg_insert_age_withdraw_reveal.c b/src/exchangedb/pg_insert_age_withdraw_reveal.c new file mode 100644 index 000000000..336ed384f --- /dev/null +++ b/src/exchangedb/pg_insert_age_withdraw_reveal.c @@ -0,0 +1,106 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file exchangedb/pg_insert_age_withdraw_reveal.c + * @brief Implementation of the insert_age_withdraw_reveal function for Postgres + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_insert_refresh_reveal.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_insert_age_withdraw_reveal ( + void *cls, + /*TODO:oec*/ + ) +{ + struct PostgresClosure *pg = cls; + + if (TALER_CNC_KAPPA != num_tprivs + 1) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + /* TODO */ +#if 0 + PREPARE (pg, + "insert_withdraw_age_revealed_coin", + "INSERT INTO withdraw_age_reveals " + "(h_commitment " + ",freshcoin_index " + ",denominations_serial " + ",h_coin_ev " + ",ev_sig" + ") SELECT $1, $2, $3, " + " denominations_serial, $5, $6, $7, $8" + " FROM denominations" + " WHERE denom_pub_hash=$4" + + " ON CONFLICT DO NOTHING;"); + for (uint32_t i = 0; iorig_coin_link_sig), + GNUNET_PQ_query_param_auto_from_type (&rrc->h_denom_pub), + TALER_PQ_query_param_blinded_planchet (&rrc->blinded_planchet), + TALER_PQ_query_param_exchange_withdraw_values (&rrc->exchange_vals), + GNUNET_PQ_query_param_auto_from_type (&rrc->coin_envelope_hash), + TALER_PQ_query_param_blinded_denom_sig (&rrc->coin_sig), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_refresh_revealed_coin", + params); + if (0 > qs) + return qs; + } + + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&melt_serial_id), + GNUNET_PQ_query_param_auto_from_type (tp), + GNUNET_PQ_query_param_fixed_size ( + tprivs, + num_tprivs * sizeof (struct TALER_TransferPrivateKeyP)), + GNUNET_PQ_query_param_end + }; + + /* Used in #postgres_insert_refresh_reveal() to store the transfer + keys we learned */ + PREPARE (pg, + "insert_refresh_transfer_keys", + "INSERT INTO refresh_transfer_keys " + "(melt_serial_id" + ",transfer_pub" + ",transfer_privs" + ") VALUES ($1, $2, $3)" + " ON CONFLICT DO NOTHING;"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_refresh_transfer_keys", + params); + } +#endif +} diff --git a/src/exchangedb/pg_insert_age_withdraw_reveal.h b/src/exchangedb/pg_insert_age_withdraw_reveal.h new file mode 100644 index 000000000..a98ee4efa --- /dev/null +++ b/src/exchangedb/pg_insert_age_withdraw_reveal.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file exchangedb/pg_insert_age_withdraw_reveal.h + * @brief implementation of the insert_age_withdraw_reveal function for Postgres + * @author Özgür Kesim + */ +#ifndef PG_INSERT_AGE_WITHDRAW_REVEAL_H +#define PG_INSERT_AGE_WITHDRAW_REVEAL_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" +/** + * Store in the database which coin(s) the wallet wanted to create + * in a given age-withdraw operation and all of the other information + * we learned or created in the /age-withdraw/reveal step. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param h_commitment The commitment of the original age-withdraw request + * @param num_coins The number of revealed coins + * @param revealed_coins The coins + * TODO:oec + * @return query status for the transaction + */ +enum GNUNET_DB_QueryStatus +TEH_PG_insert_refresh_reveal ( + void *cls, + /* TODO: oec */ + ); + +#endif diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 5b724953b..43f6b73e8 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -3805,6 +3805,37 @@ struct TALER_EXCHANGEDB_Plugin bool *balance_ok, uint64_t *ruuid); + /** + * Store in the database which coin(s) the wallet wanted to withdraw with + * age restriction enabled in a given age-withdraw operation and the relevant + * information we learned or created in the reveal steop + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param h_commitment The hash of the original age-withdraw commitment, which is a key into the withdraw_age_commitments table + * @param num_coins number of coins to generate, size of the @a coin_evs array + * TODO: oec + * @return query execution status + */ + enum GNUNET_DB_QueryStatus + (*insert_age_withdraw_reveal)( + void *cls, + uint64_t h_commitment, + uint32_t num_coins + /* TODO: oec */ + ); + + /** + * Lookup in the database for the fresh coins with age-restriction that + * we created in the given age-withdraw operation. + * + * TODO: oec + */ + enum GNUNET_DB_QueryStatus + (*get_age_withdraw_reveal)( + void *cls, + uint64_t h_commitment + /* TODO: oec */ + ); /** * Retrieve the details to a policy given by its hash_code