Line data Source code
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 "taler/taler_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 169 : 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 169 : struct GNUNET_PQ_QueryParam params[] = {
40 169 : GNUNET_PQ_query_param_auto_from_type (&coin->coin_pub),
41 169 : GNUNET_PQ_query_param_auto_from_type (&coin->denom_pub_hash),
42 169 : coin->no_age_commitment
43 105 : ? GNUNET_PQ_query_param_null ()
44 169 : : GNUNET_PQ_query_param_auto_from_type (&coin->h_age_commitment),
45 169 : TALER_PQ_query_param_denom_sig (&coin->denom_sig),
46 : GNUNET_PQ_query_param_end
47 : };
48 169 : struct GNUNET_PQ_ResultSpec rs[] = {
49 169 : GNUNET_PQ_result_spec_bool ("existed",
50 : &existed),
51 169 : GNUNET_PQ_result_spec_uint64 ("known_coin_id",
52 : known_coin_id),
53 169 : 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 169 : 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 169 : 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 169 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
117 : "insert_known_coin",
118 : params,
119 : rs);
120 169 : switch (qs)
121 : {
122 0 : case GNUNET_DB_STATUS_HARD_ERROR:
123 0 : GNUNET_break (0);
124 0 : return TALER_EXCHANGEDB_CKS_HARD_FAIL;
125 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
126 0 : return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
127 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
128 0 : GNUNET_break (0); /* should be impossible */
129 0 : return TALER_EXCHANGEDB_CKS_HARD_FAIL;
130 169 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
131 169 : if (! existed)
132 97 : return TALER_EXCHANGEDB_CKS_ADDED;
133 72 : break; /* continued below */
134 : }
135 :
136 72 : if ( (! no_denom_pub_hash) &&
137 72 : (0 != GNUNET_memcmp (denom_hash,
138 : &coin->denom_pub_hash)) )
139 : {
140 2 : GNUNET_break_op (0);
141 2 : return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
142 : }
143 :
144 70 : if (no_age_commitment_hash != coin->no_age_commitment)
145 : {
146 0 : if (no_age_commitment_hash)
147 : {
148 0 : GNUNET_break_op (0);
149 0 : return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL;
150 : }
151 : else
152 : {
153 0 : GNUNET_break_op (0);
154 0 : return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL;
155 : }
156 : }
157 70 : else if ( (! no_age_commitment_hash) &&
158 24 : (0 != GNUNET_memcmp (h_age_commitment,
159 : &coin->h_age_commitment)) )
160 : {
161 0 : GNUNET_break_op (0);
162 0 : return TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS;
163 : }
164 :
165 70 : return TALER_EXCHANGEDB_CKS_PRESENT;
166 : }
|