ensure_coin_known.c (5586B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file exchangedb/ensure_coin_known.c 18 * @brief Implementation of the ensure_coin_known function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "exchangedb_lib.h" 22 #include "taler/taler_pq_lib.h" 23 #include "exchange-database/ensure_coin_known.h" 24 #include "helper.h" 25 26 27 enum TALER_EXCHANGEDB_CoinKnownStatus 28 TALER_EXCHANGEDB_ensure_coin_known ( 29 struct TALER_EXCHANGEDB_PostgresContext *pg, 30 const struct TALER_CoinPublicInfo *coin, 31 uint64_t *known_coin_id, 32 struct TALER_DenominationHashP *denom_hash, 33 struct TALER_AgeCommitmentHashP *h_age_commitment) 34 { 35 enum GNUNET_DB_QueryStatus qs; 36 bool existed; 37 bool no_denom_pub_hash; 38 bool no_age_commitment_hash; 39 struct GNUNET_PQ_QueryParam params[] = { 40 GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub), 41 GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash), 42 coin->no_age_commitment 43 ? GNUNET_PQ_query_param_null () 44 : GNUNET_PQ_query_param_auto_from_type (&coin->h_age_commitment), 45 TALER_PQ_query_param_denom_sig (&coin->denom_sig), 46 GNUNET_PQ_query_param_end 47 }; 48 struct GNUNET_PQ_ResultSpec rs[] = { 49 GNUNET_PQ_result_spec_bool ("existed", 50 &existed), 51 GNUNET_PQ_result_spec_uint64 ("known_coin_id", 52 known_coin_id), 53 GNUNET_PQ_result_spec_allow_null ( 54 GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", 55 denom_hash), 56 &no_denom_pub_hash), 57 GNUNET_PQ_result_spec_allow_null ( 58 GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash", 59 h_age_commitment), 60 &no_age_commitment_hash), 61 GNUNET_PQ_result_spec_end 62 }; 63 64 /* 65 See also: 66 https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql/37543015#37543015 67 */ 68 PREPARE (pg, 69 "insert_known_coin", 70 "WITH dd" 71 " (denominations_serial" 72 " ,coin" 73 " ) AS (" 74 " SELECT " 75 " denominations_serial" 76 " ,coin" 77 " FROM denominations" 78 " WHERE denom_pub_hash=$2" 79 " ), input_rows" 80 " (coin_pub) AS (" 81 " VALUES ($1::BYTEA)" 82 " ), ins AS (" 83 " INSERT INTO known_coins " 84 " (coin_pub" 85 " ,denominations_serial" 86 " ,age_commitment_hash" 87 " ,denom_sig" 88 " ,remaining" 89 " ) SELECT " 90 " $1" 91 " ,denominations_serial" 92 " ,$3" 93 " ,$4" 94 " ,coin" 95 " FROM dd" 96 " ON CONFLICT DO NOTHING" /* CONFLICT on (coin_pub) */ 97 " RETURNING " 98 " known_coin_id" 99 " ) " 100 "SELECT " 101 " FALSE AS existed" 102 " ,known_coin_id" 103 " ,NULL AS denom_pub_hash" 104 " ,NULL AS age_commitment_hash" 105 " FROM ins " 106 "UNION ALL " 107 "SELECT " 108 " TRUE AS existed" 109 " ,known_coin_id" 110 " ,denom_pub_hash" 111 " ,kc.age_commitment_hash" 112 " FROM input_rows" 113 " JOIN known_coins kc USING (coin_pub)" 114 " JOIN denominations USING (denominations_serial)" 115 " LIMIT 1"); 116 qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 117 "insert_known_coin", 118 params, 119 rs); 120 switch (qs) 121 { 122 case GNUNET_DB_STATUS_HARD_ERROR: 123 GNUNET_break (0); 124 return TALER_EXCHANGEDB_CKS_HARD_FAIL; 125 case GNUNET_DB_STATUS_SOFT_ERROR: 126 return TALER_EXCHANGEDB_CKS_SOFT_FAIL; 127 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 128 GNUNET_break (0); /* should be impossible */ 129 return TALER_EXCHANGEDB_CKS_HARD_FAIL; 130 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 131 if (! existed) 132 return TALER_EXCHANGEDB_CKS_ADDED; 133 break; /* continued below */ 134 } 135 136 if ( (! no_denom_pub_hash) && 137 (0 != GNUNET_memcmp (denom_hash, 138 &coin->denom_pub_hash)) ) 139 { 140 GNUNET_break_op (0); 141 return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT; 142 } 143 144 if (no_age_commitment_hash != coin->no_age_commitment) 145 { 146 if (no_age_commitment_hash) 147 { 148 GNUNET_break_op (0); 149 return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL; 150 } 151 else 152 { 153 GNUNET_break_op (0); 154 return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL; 155 } 156 } 157 else if ( (! no_age_commitment_hash) && 158 (0 != GNUNET_memcmp (h_age_commitment, 159 &coin->h_age_commitment)) ) 160 { 161 GNUNET_break_op (0); 162 return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS; 163 } 164 165 return TALER_EXCHANGEDB_CKS_PRESENT; 166 }