commit 3afafbe4a33f0a768046c050921f422f28dfd74a parent fc25f3a4d91f899069877085fb01357cc9487c5c Author: Özgür Kesim <oec@codeblau.de> Date: Mon, 31 Mar 2025 17:57:56 +0200 [exchangedb] changes to withdraw table and handlers - added h_planchets to the withdraw table. Value needed to verify signature - added get_denomination_by_serial - updated callbacks for withdraw table lookups Diffstat:
20 files changed, 351 insertions(+), 142 deletions(-)
diff --git a/src/exchangedb/0009-withdraw.sql b/src/exchangedb/0009-withdraw.sql @@ -30,11 +30,11 @@ BEGIN ',h_commitment BYTEA NOT NULL CONSTRAINT h_commitment_length CHECK(LENGTH(h_commitment)=64)' ',execution_date INT8 NOT NULL' ',amount_with_fee taler_amount NOT NULL' + ',h_planchets BYTEA NOT NULL CONSTRAINT h_planchets_length CHECK(LENGTH(h_planchets)=64)' ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)' ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)' ',max_age SMALLINT CONSTRAINT max_age_positive CHECK(max_age>=0)' ',noreveal_index SMALLINT CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)' - ',h_planchets BYTEA CONSTRAINT h_planchets_length CHECK(LENGTH(h_planchets)=64)' ',h_blind_evs BYTEA[] NOT NULL CONSTRAINT h_blind_evs_length CHECK(cardinality(h_blind_evs)=cardinality(denom_serials))' ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))' ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))' @@ -56,7 +56,7 @@ BEGIN ,partition_suffix ); PERFORM comment_partitioned_column( - 'If the client explicitly commits to age-restricted coins, the running hash over all committed blinded planchets; might be NULL.' + 'The running hash over all committed blinded planchets.' ,'h_planchets' ,table_name ,partition_suffix diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am @@ -189,6 +189,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_get_coin_denomination.h pg_get_coin_denomination.c \ pg_get_coin_transactions.c pg_get_coin_transactions.h \ pg_get_denomination_info.h pg_get_denomination_info.c \ + pg_get_denomination_by_serial.h pg_get_denomination_by_serial.c \ pg_get_denomination_revocation.h pg_get_denomination_revocation.c \ pg_get_drain_profit.h pg_get_drain_profit.c \ pg_get_expired_reserves.c pg_get_expired_reserves.h \ diff --git a/src/exchangedb/perf_deposits_get_ready.c b/src/exchangedb/perf_deposits_get_ready.c @@ -161,6 +161,7 @@ create_denom_key_pair (unsigned int size, if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_denomination_info (plugin->cls, &dki.issue.denom_hash, + NULL, &issue2)) { GNUNET_break (0); diff --git a/src/exchangedb/perf_get_link_data.c b/src/exchangedb/perf_get_link_data.c @@ -151,6 +151,7 @@ create_denom_key_pair (unsigned int size, if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_denomination_info (plugin->cls, &dki.issue.denom_hash, + NULL, &issue2)) { GNUNET_break (0); diff --git a/src/exchangedb/perf_select_refunds_by_coin.c b/src/exchangedb/perf_select_refunds_by_coin.c @@ -158,6 +158,7 @@ create_denom_key_pair (unsigned int size, if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_denomination_info (plugin->cls, &dki.issue.denom_hash, + NULL, &issue2)) { GNUNET_break (0); diff --git a/src/exchangedb/pg_do_withdraw.c b/src/exchangedb/pg_do_withdraw.c @@ -34,7 +34,6 @@ TEH_PG_do_withdraw ( void *cls, const struct TALER_EXCHANGEDB_Withdraw *withdraw, struct GNUNET_TIME_Timestamp now, - bool *found, bool *balance_ok, struct TALER_Amount *reserve_balance, bool *age_ok, @@ -52,15 +51,13 @@ TEH_PG_do_withdraw ( GNUNET_PQ_query_param_timestamp (&now), GNUNET_PQ_query_param_timestamp (&gc), GNUNET_PQ_query_param_auto_from_type (&withdraw->h_commitment), - withdraw->age_restricted ? - GNUNET_PQ_query_param_auto_from_type (&withdraw->h_planchets) : - GNUNET_PQ_query_param_null (), - withdraw->age_restricted ? - GNUNET_PQ_query_param_uint16 (&withdraw->max_age) : - GNUNET_PQ_query_param_null (), - withdraw->age_restricted ? - GNUNET_PQ_query_param_uint16 (&withdraw->noreveal_index) : - GNUNET_PQ_query_param_null (), + GNUNET_PQ_query_param_auto_from_type (&withdraw->h_planchets), + withdraw->age_proof_required + ? GNUNET_PQ_query_param_uint16 (&withdraw->max_age) + : GNUNET_PQ_query_param_null (), + withdraw->age_proof_required + ? GNUNET_PQ_query_param_uint16 (&withdraw->noreveal_index) + : GNUNET_PQ_query_param_null (), TALER_PQ_query_param_array_blinded_coin_hash (withdraw->num_coins, withdraw->h_coin_evs, pg->conn), @@ -72,9 +69,10 @@ TEH_PG_do_withdraw ( pg->conn), GNUNET_PQ_query_param_end }; + bool found; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_bool ("reserve_found", - found), + &found), GNUNET_PQ_result_spec_bool ("balance_ok", balance_ok), TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance", @@ -111,5 +109,10 @@ TEH_PG_do_withdraw ( params, rs); GNUNET_PQ_cleanup_query_params_closures (params); - return qs; + + if (0 > qs) + return qs; + if (! found) + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } diff --git a/src/exchangedb/pg_do_withdraw.h b/src/exchangedb/pg_do_withdraw.h @@ -32,21 +32,19 @@ * @param cls the `struct PostgresClosure` with the plugin-specific state * @param withdraw the request with all parameters * @param now current time (rounded) - * @param[out] found set to true if the reserve was found * @param[out] balance_ok set to true if the balance was sufficient * @param[out] reserve_balance set to the original reserve balance (at the start of this transaction) * @param[out] age_ok set to true if no age requirements are present on the reserve * @param[out] required_age if @e age_ok is false, set to the maximum allowed age when withdrawing from this reserve * @param[out] reserve_birthday if @e age_ok is false, set to the birthday of the reserve * @param[out] conflict set to true if there already is an entry in the database for the given pair (h_commitment, reserve_pub) - * @return query execution status + * @return 0 if no reserve was found, 1 if a reserve was found, else the query execution status */ enum GNUNET_DB_QueryStatus TEH_PG_do_withdraw ( void *cls, const struct TALER_EXCHANGEDB_Withdraw *withdraw, const struct GNUNET_TIME_Timestamp now, - bool *found, bool *balance_ok, struct TALER_Amount *reserve_balance, bool *age_ok, diff --git a/src/exchangedb/pg_get_denomination_by_serial.c b/src/exchangedb/pg_get_denomination_by_serial.c @@ -0,0 +1,93 @@ +/* + This file is part of TALER + Copyright (C) 2025 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 <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_get_denomination_by_serial.c + * @brief Implementation of the get_denomination_by_serial 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_get_denomination_info.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_get_denomination_by_serial ( + void *cls, + uint64_t denom_serial, + struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) +{ + struct PostgresClosure *pg = cls; + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&denom_serial), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("master_sig", + &issue->signature), + GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", + &issue->denom_hash), + GNUNET_PQ_result_spec_timestamp ("valid_from", + &issue->start), + GNUNET_PQ_result_spec_timestamp ("expire_withdraw", + &issue->expire_withdraw), + GNUNET_PQ_result_spec_timestamp ("expire_deposit", + &issue->expire_deposit), + GNUNET_PQ_result_spec_timestamp ("expire_legal", + &issue->expire_legal), + TALER_PQ_RESULT_SPEC_AMOUNT ("coin", + &issue->value), + TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw", + &issue->fees.withdraw), + TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit", + &issue->fees.deposit), + TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh", + &issue->fees.refresh), + TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund", + &issue->fees.refund), + GNUNET_PQ_result_spec_uint32 ("age_mask", + &issue->age_mask.bits), + GNUNET_PQ_result_spec_end + }; + + PREPARE (pg, + "denomination_get_by_serial", + "SELECT" + " master_sig" + ",denom_pub_hash" + ",valid_from" + ",expire_withdraw" + ",expire_deposit" + ",expire_legal" + ",coin" /* value of this denom */ + ",fee_withdraw" + ",fee_deposit" + ",fee_refresh" + ",fee_refund" + ",age_mask" + " FROM denominations" + " WHERE denominations_serial=$1;"); + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "denomination_get_by_serial", + params, + rs); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + return qs; + return qs; +} diff --git a/src/exchangedb/pg_get_denomination_by_serial.h b/src/exchangedb/pg_get_denomination_by_serial.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + Copyright (C) 2025 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 <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_get_denomination_by_serial.h + * @brief implementation of the get_denomination_by_serial function for Postgres + * @author Özgür Kesim + */ +#ifndef PG_GET_DENOMINATION_BY_SERIAL_H +#define PG_GET_DENOMINATION_BY_SERIAL_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" +/** + * Fetch information about a denomination for a given serial. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param denom_serial row in the denomination table + * @param[out] issue set to issue information with value, fees and other info about the coin + * @return transaction status code + */ +enum GNUNET_DB_QueryStatus +TEH_PG_get_denomination_by_serial ( + void *cls, + uint64_t denom_serial, + struct TALER_EXCHANGEDB_DenominationKeyInformation *issue); + +#endif diff --git a/src/exchangedb/pg_get_denomination_info.c b/src/exchangedb/pg_get_denomination_info.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022,2025 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 @@ -17,6 +17,7 @@ * @file exchangedb/pg_get_denomination_info.c * @brief Implementation of the get_denomination_info function for Postgres * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include "taler_error_codes.h" @@ -30,15 +31,19 @@ enum GNUNET_DB_QueryStatus TEH_PG_get_denomination_info ( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, + uint64_t *denom_serial, struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) { struct PostgresClosure *pg = cls; enum GNUNET_DB_QueryStatus qs; + uint64_t serial; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (denom_pub_hash), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("denominations_serial", + &serial), GNUNET_PQ_result_spec_auto_from_type ("master_sig", &issue->signature), GNUNET_PQ_result_spec_timestamp ("valid_from", @@ -67,7 +72,8 @@ TEH_PG_get_denomination_info ( PREPARE (pg, "denomination_get", "SELECT" - " master_sig" + " denominations_serial" + ",master_sig" ",valid_from" ",expire_withdraw" ",expire_deposit" @@ -87,5 +93,7 @@ TEH_PG_get_denomination_info ( if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) return qs; issue->denom_hash = *denom_pub_hash; + if (NULL != denom_serial) + *denom_serial = serial; return qs; } diff --git a/src/exchangedb/pg_get_denomination_info.h b/src/exchangedb/pg_get_denomination_info.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022,2025 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 @@ -17,6 +17,7 @@ * @file exchangedb/pg_get_denomination_info.h * @brief implementation of the get_denomination_info function for Postgres * @author Christian Grothoff + * @author Özgür Kesim */ #ifndef PG_GET_DENOMINATION_INFO_H #define PG_GET_DENOMINATION_INFO_H @@ -29,6 +30,7 @@ * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the public key used for signing coins of this denomination + * @param[out] denom_serial row in the denomination table, might be NULL * @param[out] issue set to issue information with value, fees and other info about the coin * @return transaction status code */ @@ -36,6 +38,7 @@ enum GNUNET_DB_QueryStatus TEH_PG_get_denomination_info ( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, + uint64_t *denom_serial, struct TALER_EXCHANGEDB_DenominationKeyInformation *issue); #endif diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c @@ -188,17 +188,14 @@ add_withdraw (void *cls, wd = GNUNET_new (struct TALER_EXCHANGEDB_Withdraw); { bool no_noreveal_index; - bool no_h_planchets; bool no_max_age; size_t num_h_coin_evs; size_t num_denom_hs; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("h_commitment", &wd->h_commitment), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("h_planchets", - &wd->h_planchets), - &no_h_planchets), + GNUNET_PQ_result_spec_auto_from_type ("h_planchets", + &wd->h_planchets), GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", &wd->reserve_sig), TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", @@ -233,31 +230,16 @@ add_withdraw (void *cls, return; } - if ((no_noreveal_index != no_max_age) || - (no_max_age != no_h_planchets)) + if (no_noreveal_index != no_max_age) { GNUNET_break (0); GNUNET_free (wd); rhc->failed = true; return; } - wd->age_restricted = ! no_max_age; + wd->age_proof_required = ! no_max_age; wd->num_coins = num_denom_hs; wd->reserve_pub = *rhc->reserve_pub; - - if (no_h_planchets) - { - struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start (); - GNUNET_assert (NULL != ctx); - for (size_t i = 0; i < num_h_coin_evs; i++) - { - GNUNET_CRYPTO_hash_context_read (ctx, - &wd->h_coin_evs[i], - sizeof(wd->h_coin_evs[0])); - } - GNUNET_CRYPTO_hash_context_finish (ctx, - &wd->h_planchets.hash); - } } tail = append_rh (rhc); diff --git a/src/exchangedb/pg_get_withdraw.c b/src/exchangedb/pg_get_withdraw.c @@ -37,7 +37,6 @@ TEH_PG_get_withdraw ( size_t num_sigs; size_t num_hashes; bool no_noreveal_index; - bool no_h_planchets; bool no_max_age; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (wch), @@ -46,6 +45,8 @@ TEH_PG_get_withdraw ( struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("h_commitment", &wd->h_commitment), + GNUNET_PQ_result_spec_auto_from_type ("h_planchets", + &wd->h_planchets), GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", &wd->reserve_sig), GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", @@ -61,10 +62,7 @@ TEH_PG_get_withdraw ( GNUNET_PQ_result_spec_uint16 ("noreveal_index", &wd->noreveal_index), &no_noreveal_index), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("h_planchets", - &wd->h_planchets), - &no_h_planchets), + TALER_PQ_result_spec_array_blinded_coin_hash ( pg->conn, "h_blind_evs", @@ -112,20 +110,18 @@ TEH_PG_get_withdraw ( if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ret) return ret; - if ((no_max_age != no_noreveal_index) || - (no_max_age != no_h_planchets)) + if (no_max_age != no_noreveal_index) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "got inconsistent state for max_age, noreveal_index and h_planchets in DB: " - "no_max_age=%s, no_noreveal_index=%s, no_h_planchets=%s\n", + "no_max_age=%s, no_noreveal_index=%s\n", no_max_age ? "true" : "false", - no_noreveal_index ? "true" : "false", - no_h_planchets ? "true" : "false"); + no_noreveal_index ? "true" : "false"); return GNUNET_DB_STATUS_HARD_ERROR; } - wd->age_restricted = ! no_max_age; + wd->age_proof_required = ! no_max_age; if ((wd->num_coins != num_sigs) || (wd->num_coins != num_hashes)) @@ -139,23 +135,5 @@ TEH_PG_get_withdraw ( num_hashes); return GNUNET_DB_STATUS_HARD_ERROR; } - - /* In the non-age-restricted case, we calculate the h_planchets - * from the h_blind_evs themselves. - */ - if (no_h_planchets) - { - struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start (); - GNUNET_assert (NULL != ctx); - for (size_t i = 0; i<wd->num_coins; i++) - { - GNUNET_CRYPTO_hash_context_read (ctx, - &wd->h_coin_evs[i], - sizeof(wd->h_coin_evs[0])); - } - GNUNET_CRYPTO_hash_context_finish (ctx, - &wd->h_planchets.hash); - } - return ret; } diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c @@ -2271,18 +2271,22 @@ irbt_cb_table_withdraw ( TALER_PQ_query_param_amount ( pg->conn, &td->details.withdraw.amount_with_fee), - td->details.withdraw.age_restricted ? - GNUNET_PQ_query_param_uint16 ( - &td->details.withdraw.max_age) : - GNUNET_PQ_query_param_null (), + td->details.withdraw.age_proof_required + ? GNUNET_PQ_query_param_uint16 ( + &td->details.withdraw.max_age) + : GNUNET_PQ_query_param_null (), + td->details.withdraw.age_proof_required + ? GNUNET_PQ_query_param_auto_from_type ( + &td->details.withdraw.h_planchets) + : GNUNET_PQ_query_param_null (), + td->details.withdraw.age_proof_required + ? GNUNET_PQ_query_param_uint32 ( + &td->details.withdraw.noreveal_index) + : GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_auto_from_type ( &td->details.withdraw.reserve_pub), GNUNET_PQ_query_param_auto_from_type ( &td->details.withdraw.reserve_sig), - td->details.withdraw.age_restricted ? - GNUNET_PQ_query_param_uint32 ( - &td->details.withdraw.noreveal_index) : - GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_timestamp ( &td->details.withdraw.execution_date), GNUNET_PQ_query_param_end @@ -2291,16 +2295,17 @@ irbt_cb_table_withdraw ( PREPARE (pg, "insert_into_table_withdraw", "INSERT INTO withdraw" - "(withdraw_commitment_id" + "(withdraw_id" ",h_commitment" ",amount_with_fee" ",max_age" + ",h_planchets" + ",noreveal_index" ",reserve_pub" ",reserve_sig" - ",noreveal_index" ",execution_date" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8,$9);"); + "($1, $2, $3, $4, $5, $6, $7, $8, $9);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_into_table_withdraw", params); diff --git a/src/exchangedb/pg_iterate_denomination_info.c b/src/exchangedb/pg_iterate_denomination_info.c @@ -69,7 +69,10 @@ domination_cb_helper (void *cls, struct TALER_EXCHANGEDB_DenominationKeyInformation issue; struct TALER_DenominationPublicKey denom_pub; struct TALER_DenominationHashP denom_hash; + uint64_t denom_serial; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("denominations_serial", + &denom_serial), GNUNET_PQ_result_spec_auto_from_type ("master_sig", &issue.signature), GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", @@ -124,6 +127,7 @@ domination_cb_helper (void *cls, else { dic->cb (dic->cb_cls, + denom_serial, &denom_pub, &issue); } @@ -158,7 +162,8 @@ TEH_PG_iterate_denomination_info (void *cls, PREPARE (pg, "denomination_iterate", "SELECT" - " master_sig" + " denominations_serial" + ",master_sig" ",denom_pub_hash" ",valid_from" ",expire_withdraw" diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c @@ -2662,8 +2662,9 @@ lrbt_cb_table_withdraw (void *cls, for (unsigned int i = 0; i<num_results; i++) { - bool no_max_age = false; - bool no_noreveal_index = false; + bool no_max_age; + bool no_noreveal_index; + bool no_h_planchets; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ( "withdraw_id", @@ -2671,25 +2672,30 @@ lrbt_cb_table_withdraw (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "h_commitment", &td.details.withdraw.h_commitment), + TALER_PQ_RESULT_SPEC_AMOUNT ( + "amount_with_fee", + &td.details.withdraw.amount_with_fee), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_uint16 ( "max_age", &td.details.withdraw.max_age), &no_max_age), - TALER_PQ_RESULT_SPEC_AMOUNT ( - "amount_with_fee", - &td.details.withdraw.amount_with_fee), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_auto_from_type ( + "h_planchets", + &td.details.withdraw.h_planchets), + &no_h_planchets), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint32 ( + "noreveal_index", + &td.details.withdraw.noreveal_index), + &no_noreveal_index), GNUNET_PQ_result_spec_auto_from_type ( "reserve_pub", &td.details.withdraw.reserve_pub), GNUNET_PQ_result_spec_auto_from_type ( "reserve_sig", &td.details.withdraw.reserve_sig), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_uint32 ( - "noreveal_index", - &td.details.withdraw.noreveal_index), - &no_noreveal_index), GNUNET_PQ_result_spec_timestamp ( "execution_date", &td.details.withdraw.execution_date), @@ -2706,13 +2712,14 @@ lrbt_cb_table_withdraw (void *cls, return; } - if (no_max_age != no_noreveal_index) + if ((no_max_age != no_noreveal_index) || + (no_max_age != no_h_planchets)) { GNUNET_break (0); ctx->error = true; return; } - td.details.withdraw.age_restricted = ! no_max_age; + td.details.withdraw.age_proof_required = ! no_max_age; ctx->cb (ctx->cb_cls, &td); @@ -3715,14 +3722,14 @@ TEH_PG_lookup_records_by_table (void *cls, ",h_commitment" ",amount_with_fee" ",max_age" + ",noreveal_index" + ",h_planchets" ",reserve_pub" ",reserve_sig" ",execution_date" - ",noreveal_index" " FROM withdraw" " WHERE withdraw_id > $1" " ORDER BY withdraw_id ASC;"); - /* FIXME[oec]: MORE FIELDS! */ rh = &lrbt_cb_table_withdraw; break; case TALER_EXCHANGEDB_RT_LEGITIMIZATION_MEASURES: diff --git a/src/exchangedb/pg_select_withdrawals_above_serial_id.c b/src/exchangedb/pg_select_withdrawals_above_serial_id.c @@ -74,29 +74,50 @@ withdraw_serial_helper_cb (void *cls, { size_t num_evs; struct TALER_BlindedCoinHashP *h_blind_evs; - struct TALER_DenominationPublicKey denom_pub; + size_t num_denom_serials; + uint64_t *denom_serials; struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReserveSignatureP reserve_sig; struct GNUNET_TIME_Timestamp execution_date; + struct TALER_WithdrawCommitmentHashP h_commitment; + struct TALER_HashBlindedPlanchetsP h_planchets; struct TALER_Amount amount_with_fee; uint64_t rowid; + uint16_t max_age; + uint16_t noreveal_index; + bool no_noreveal_index; + bool no_max_age; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_array_blinded_coin_hash (pg->conn, "h_blind_evs", &num_evs, &h_blind_evs), - TALER_PQ_result_spec_denom_pub ("denom_pub", - &denom_pub), + GNUNET_PQ_result_spec_array_uint64 (pg->conn, + "denom_serials", + &num_denom_serials, + &denom_serials), GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", &reserve_pub), GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", &reserve_sig), + GNUNET_PQ_result_spec_auto_from_type ("h_planchets", + &h_planchets), + GNUNET_PQ_result_spec_auto_from_type ("h_commitment", + &h_commitment), GNUNET_PQ_result_spec_timestamp ("execution_date", &execution_date), TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", &amount_with_fee), GNUNET_PQ_result_spec_uint64 ("withdraw_id", &rowid), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint16 ("max_age", + &max_age), + &no_max_age), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint16 ("noreveal_index", + &noreveal_index), + &no_noreveal_index), GNUNET_PQ_result_spec_end }; enum GNUNET_GenericReturnValue ret; @@ -110,11 +131,39 @@ withdraw_serial_helper_cb (void *cls, rosc->status = GNUNET_SYSERR; return; } + + if (num_denom_serials != num_evs) + { + GNUNET_break (0); + rosc->status = GNUNET_SYSERR; + return; + } + + if (no_max_age != no_noreveal_index) + { + GNUNET_break (0); + rosc->status = GNUNET_SYSERR; + return; + } + + if ((! no_max_age) && + ((255 <= noreveal_index) || (255 <= max_age))) + { + GNUNET_break (0); + rosc->status = GNUNET_SYSERR; + return; + } + ret = rosc->cb (rosc->cb_cls, rowid, num_evs, h_blind_evs, - &denom_pub, + denom_serials, + &h_commitment, + &h_planchets, + ! no_max_age, + (uint8_t) max_age, + (uint8_t) noreveal_index, &reserve_pub, &reserve_sig, execution_date, @@ -150,22 +199,20 @@ TEH_PG_select_withdrawals_above_serial_id ( PREPARE (pg, "audit_get_withdraw_incr", "SELECT" - " h_blind_evs" - ",denom.denom_pub" + " withdraw_id" + ",h_commitment" + ",h_blind_evs" + ",h_planchets" + ",max_age" + ",noreveal_index" + ",denom_serials" ",reserve_sig" ",reserve_pub" ",execution_date" ",amount_with_fee" - ",wd.withdraw_id" - " FROM withdraw AS wd" - " JOIN (" - " SELECT withdraw_id, unnest(denom_serials) AS denominations_serial" - " FROM withdraw" - " ) AS unnested ON wd.withdraw_id = unnested.withdraw_id" - " JOIN denominations denom" - " USING (denominations_serial)" - " WHERE wd.withdraw_id>=$1" - " ORDER BY wd.withdraw_id ASC;"); + " FROM withdraw" + " WHERE withdraw_id>=$1" + " ORDER BY withdraw_id ASC;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "audit_get_withdraw_incr", params, diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c @@ -73,6 +73,7 @@ #include "pg_get_coin_denomination.h" #include "pg_get_coin_transactions.h" #include "pg_get_denomination_info.h" +#include "pg_get_denomination_by_serial.h" #include "pg_get_denomination_revocation.h" #include "pg_get_drain_profit.h" #include "pg_get_expired_reserves.h" @@ -522,6 +523,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_event_notify; plugin->get_denomination_info = &TEH_PG_get_denomination_info; + plugin->get_denomination_by_serial + = &TEH_PG_get_denomination_by_serial; plugin->iterate_denomination_info = &TEH_PG_iterate_denomination_info; plugin->iterate_denominations diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c @@ -323,6 +323,7 @@ create_denom_key_pair (unsigned int size, if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_denomination_info (plugin->cls, &dki.issue.denom_hash, + NULL, &issue2)) { GNUNET_break (0); @@ -718,7 +719,12 @@ audit_reserve_in_cb (void *cls, * @param rowid unique serial ID for the refresh session in our DB * @param num_evs number of elements in @e h_blind_evs * @param h_blind_evs array @e num_evs of blinded hashes of the coin's public keys - * @param denom_pub public denomination key of the deposited coin + * @param denom_serials array @e num_evs of serial ids of denominations + * @param h_commitment hash of the commitment of the withdraw + * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request + * @param age_proof_required true if the withdraw request required an age proof. + * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins. + * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase. * @param reserve_pub public key of the reserve * @param reserve_sig signature over the withdraw operation * @param execution_date when did the wallet withdraw the coin @@ -730,7 +736,12 @@ audit_reserve_out_cb (void *cls, uint64_t rowid, size_t num_evs, const struct TALER_BlindedCoinHashP *h_blind_evs, - const struct TALER_DenominationPublicKey *denom_pub, + const uint64_t *denom_serials, + const struct TALER_WithdrawCommitmentHashP *h_commitment, + const struct TALER_HashBlindedPlanchetsP *h_planchets, + bool age_proof_required, + uint8_t max_age, + uint8_t noreveal_index, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig, struct GNUNET_TIME_Timestamp execution_date, @@ -739,7 +750,7 @@ audit_reserve_out_cb (void *cls, (void) cls; (void) rowid; (void) h_blind_evs; - (void) denom_pub; + (void) denom_serials; (void) reserve_pub; (void) reserve_sig; (void) execution_date; @@ -787,6 +798,7 @@ test_gc (void) if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->get_denomination_info (plugin->cls, &denom_hash, + NULL, &issue2)) { GNUNET_break (0); @@ -1417,17 +1429,16 @@ run (void *cls) &cbc.withdraw_fee)); { - bool found; bool balance_ok; bool age_ok; bool conflict; uint16_t maximum_age; uint32_t reserve_birthday; - uint64_t denom_serial = 1; /* FIXME: this should be taken from the database */ + uint64_t denom_serial = 1; /* bold assumption */ struct TALER_Amount reserve_balance; struct TALER_EXCHANGEDB_Withdraw commitment = { .amount_with_fee = global_value, - .age_restricted = false, + .age_proof_required = false, .max_age = 0, .noreveal_index = 0, .reserve_pub = reserve_pub, @@ -1438,12 +1449,6 @@ run (void *cls) .denom_serials = &denom_serial, }; -#pragma message "find the denomination serial" - /* Find denomination's serial */ - { - } - - /** * Calculate the commitment */ @@ -1460,14 +1465,13 @@ run (void *cls) plugin->do_withdraw (plugin->cls, &commitment, now, - &found, &balance_ok, &reserve_balance, &age_ok, &maximum_age, &reserve_birthday, &conflict)); - GNUNET_assert (found); + GNUNET_assert (! conflict); GNUNET_assert (balance_ok); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -841,14 +841,15 @@ struct TALER_EXCHANGEDB_TableData struct TALER_WithdrawCommitmentHashP h_commitment; struct TALER_Amount amount_with_fee; struct GNUNET_TIME_Timestamp execution_date; - bool age_restricted; + bool age_proof_required; uint16_t max_age; uint32_t noreveal_index; + struct TALER_HashBlindedPlanchetsP h_planchets; struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReserveSignatureP reserve_sig; uint64_t num_coins; uint64_t *denominations_serials; - void *h_blind_evs; + struct TALER_BlindedCoinHashP *h_blind_evs; struct TALER_BlindedDenominationSignature denom_sigs; } withdraw; @@ -1253,20 +1254,21 @@ struct TALER_EXCHANGEDB_Withdraw struct TALER_Amount amount_with_fee; /** - * true, if age restriction was set for this withdraw. + * true, if a proof of age was required following this withdraw, + * in a subsequent call to /reveal-withdraw. * In this case, @e max_age, @e h_commitment and * @e noreveal_index are to be taken into account */ - bool age_restricted; + bool age_proof_required; /** * Maximum age (in years) that the coins are restricted to, - * if ``age_restricted`` is true. + * if ``age_proof_required`` is true. */ uint16_t max_age; /** - * If ``age_restricted`` is true, index (smaller #TALER_CNC_KAPPA) + * If ``age_proof_required`` is true, index (smaller #TALER_CNC_KAPPA) * which the exchange has chosen to keep unrevealed * during the next cut and choose (aka /reveal-age) step. * This value applies to all n coins in the commitment. @@ -1280,7 +1282,7 @@ struct TALER_EXCHANGEDB_Withdraw struct TALER_WithdrawCommitmentHashP h_commitment; /** - * If @e age_restricted is true, the running hash over all blinded coin + * If @e age_proof_required is true, the running hash over all blinded coin * envelope's TALER_BlindedCoinHashP values. * It runs over ``kappa*num_coins``, starting with the hashes for the coins * for kappa index=0, then index=1 etc., @@ -1305,7 +1307,7 @@ struct TALER_EXCHANGEDB_Withdraw /** * Array of @a num_coins blinded coins envelopes. - * In case of @e age_restricted = true, these are the chosen coins + * In case of @e age_proof_required = true, these are the chosen coins * (according to @e noreveal_index) from the request, which contained * kappa*num_coins blinded coins envelopes. */ @@ -1320,7 +1322,7 @@ struct TALER_EXCHANGEDB_Withdraw /** * Array of @a num_coins serial id's of the denominations, corresponding to * the coins in @a h_coin_evs. - * If @e age_restricted is true, the denominations MUST support age restriction. + * If @e age_proof_required is true, the denominations MUST support age restriction. */ uint64_t *denom_serials; @@ -3297,8 +3299,13 @@ typedef void * @param cls closure * @param rowid unique serial ID for the refresh session in our DB * @param num_evs number of elements in @e h_blind_evs - * @param h_blind_evs Array @e num_evs of blinded hashes of the coin's public keys - * @param denom_pub public denomination key of the deposited coin + * @param h_blind_evs array @e num_evs of blinded hashes of the coin's public keys + * @param denom_serials array @e num_evs of serial ID's of denominations in our DB + * @param h_commitment hash of the commitment of the withdraw + * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request + * @param age_proof_required true if the withdraw request required an age proof. + * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins. + * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase. * @param reserve_pub public key of the reserve * @param reserve_sig signature over the withdraw operation * @param execution_date when did the wallet withdraw the coin @@ -3311,7 +3318,12 @@ typedef enum GNUNET_GenericReturnValue uint64_t rowid, size_t num_evs, const struct TALER_BlindedCoinHashP *h_blind_evs, - const struct TALER_DenominationPublicKey *denom_pub, + const uint64_t *denom_serials, + const struct TALER_WithdrawCommitmentHashP *h_commitment, + const struct TALER_HashBlindedPlanchetsP *h_planchets, + bool age_proof_required, + uint8_t max_age, + uint8_t noreveal_index, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig, struct GNUNET_TIME_Timestamp execution_date, @@ -3650,12 +3662,14 @@ typedef enum GNUNET_GenericReturnValue * this function is called! * * @param cls closure + * @param denom_serial table row of the denomination * @param denom_pub public key of the denomination * @param issue detailed information about the denomination (value, expiration times, fees); */ typedef void (*TALER_EXCHANGEDB_DenominationCallback)( void *cls, + uint64_t denom_serial, const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue); @@ -3937,6 +3951,7 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the public key used for signing coins of this denomination + * @param[out] denom_serial row in the denomination table, might be NULL * @param[out] issue set to issue information with value, fees and other info about the coin * @return transaction status code */ @@ -3944,6 +3959,21 @@ struct TALER_EXCHANGEDB_Plugin (*get_denomination_info)( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, + uint64_t *denom_serial, + struct TALER_EXCHANGEDB_DenominationKeyInformation *issue); + + /** + * Fetch information about a denomination by a given serial id. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param denom_serial row in the denomination table + * @param[out] issue set to issue information with value, fees and other info about the coin + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*get_denomination_by_serial)( + void *cls, + uint64_t denom_serial, struct TALER_EXCHANGEDB_DenominationKeyInformation *issue); @@ -4262,20 +4292,18 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls the `struct PostgresClosure` with the plugin-specific state * @param commitment corresponding commitment for the age-withdraw - * @param[out] found set to true if the reserve was found * @param[out] balance_ok set to true if the balance was sufficient * @param[out] reserve_balance set to original balance of the reserve * @param[out] age_ok set to true if age requirements were met * @param[out] allowed_maximum_age if @e age_ok is FALSE, this is set to the allowed maximum age * @param[out] reserve_birthday if @e age_ok is FALSE, this is set to the reserve's birthday - * @return query execution status + * @return 0 if no reserve was found, 1 if a reserve was found, else the query execution status */ enum GNUNET_DB_QueryStatus (*do_withdraw)( void *cls, const struct TALER_EXCHANGEDB_Withdraw *commitment, struct GNUNET_TIME_Timestamp now, - bool *found, bool *balance_ok, struct TALER_Amount *reserve_balance, bool *age_ok,