Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_recoup_refresh.c
21 : * @brief Implement the /recoup-refresh test command.
22 : * @author Marcello Stanisci
23 : */
24 : #include "platform.h"
25 : #include "taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler_testing_lib.h"
28 :
29 :
30 : /**
31 : * State for a "pay back" CMD.
32 : */
33 : struct RecoupRefreshState
34 : {
35 : /**
36 : * Expected HTTP status code.
37 : */
38 : unsigned int expected_response_code;
39 :
40 : /**
41 : * Command that offers a reserve private key,
42 : * plus a coin to be paid back.
43 : */
44 : const char *coin_reference;
45 :
46 : /**
47 : * Amount to be recouped.
48 : */
49 : struct TALER_Amount amount;
50 :
51 : /**
52 : * The interpreter state.
53 : */
54 : struct TALER_TESTING_Interpreter *is;
55 :
56 : /**
57 : * Handle to the ongoing operation.
58 : */
59 : struct TALER_EXCHANGE_RecoupRefreshHandle *ph;
60 :
61 : /**
62 : * NULL if coin was not refreshed, otherwise reference
63 : * to the melt operation underlying @a coin_reference.
64 : */
65 : const char *melt_reference;
66 :
67 : };
68 :
69 :
70 : /**
71 : * Check the result of the recoup_refresh request: checks whether
72 : * the HTTP response code is good, and that the coin that
73 : * was paid back belonged to the right old coin.
74 : *
75 : * @param cls closure
76 : * @param hr HTTP response details
77 : * @param old_coin_pub public key of the dirty coin
78 : */
79 : static void
80 0 : recoup_refresh_cb (void *cls,
81 : const struct TALER_EXCHANGE_HttpResponse *hr,
82 : const struct TALER_CoinSpendPublicKeyP *old_coin_pub)
83 : {
84 0 : struct RecoupRefreshState *rrs = cls;
85 0 : struct TALER_TESTING_Interpreter *is = rrs->is;
86 0 : struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
87 : char *cref;
88 : unsigned int idx;
89 :
90 0 : rrs->ph = NULL;
91 0 : if (rrs->expected_response_code != hr->http_status)
92 : {
93 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
94 : "Unexpected response code %u/%d to command %s in %s:%u\n",
95 : hr->http_status,
96 : (int) hr->ec,
97 : cmd->label,
98 : __FILE__,
99 : __LINE__);
100 0 : json_dumpf (hr->reply,
101 : stderr,
102 : 0);
103 0 : fprintf (stderr, "\n");
104 0 : TALER_TESTING_interpreter_fail (is);
105 0 : return;
106 : }
107 :
108 0 : if (GNUNET_OK !=
109 0 : TALER_TESTING_parse_coin_reference (
110 : rrs->coin_reference,
111 : &cref,
112 : &idx))
113 : {
114 0 : TALER_TESTING_interpreter_fail (is);
115 0 : return;
116 : }
117 : (void) idx; /* do NOT use! We ignore 'idx', must be 0 for melt! */
118 :
119 0 : GNUNET_free (cref);
120 0 : switch (hr->http_status)
121 : {
122 0 : case MHD_HTTP_OK:
123 : /* check old_coin_pub */
124 : {
125 : const struct TALER_TESTING_Command *melt_cmd;
126 : const struct TALER_CoinSpendPrivateKeyP *dirty_priv;
127 : struct TALER_CoinSpendPublicKeyP oc;
128 :
129 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
130 : rrs->melt_reference);
131 0 : if (NULL == melt_cmd)
132 : {
133 0 : GNUNET_break (0);
134 0 : TALER_TESTING_interpreter_fail (is);
135 0 : return;
136 : }
137 0 : if (GNUNET_OK !=
138 0 : TALER_TESTING_get_trait_coin_priv (melt_cmd,
139 : 0,
140 : &dirty_priv))
141 : {
142 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
143 : "Coin %u not found in command %s\n",
144 : 0,
145 : rrs->melt_reference);
146 0 : GNUNET_break (0);
147 0 : TALER_TESTING_interpreter_fail (is);
148 0 : return;
149 : }
150 0 : GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv,
151 : &oc.eddsa_pub);
152 0 : if (0 != GNUNET_memcmp (&oc,
153 : old_coin_pub))
154 : {
155 0 : GNUNET_break (0);
156 0 : TALER_TESTING_interpreter_fail (is);
157 0 : return;
158 : }
159 : }
160 0 : break;
161 0 : case MHD_HTTP_NOT_FOUND:
162 0 : break;
163 0 : case MHD_HTTP_CONFLICT:
164 0 : break;
165 0 : default:
166 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
167 : "Unmanaged HTTP status code %u/%d.\n",
168 : hr->http_status,
169 : (int) hr->ec);
170 0 : break;
171 : }
172 0 : TALER_TESTING_interpreter_next (is);
173 : }
174 :
175 :
176 : /**
177 : * Run the command.
178 : *
179 : * @param cls closure.
180 : * @param cmd the command to execute.
181 : * @param is the interpreter state.
182 : */
183 : static void
184 0 : recoup_refresh_run (void *cls,
185 : const struct TALER_TESTING_Command *cmd,
186 : struct TALER_TESTING_Interpreter *is)
187 : {
188 0 : struct RecoupRefreshState *rrs = cls;
189 : const struct TALER_TESTING_Command *coin_cmd;
190 : const struct TALER_TESTING_Command *melt_cmd;
191 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
192 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
193 : const struct TALER_DenominationSignature *coin_sig;
194 : const struct TALER_RefreshMasterSecretP *rplanchet;
195 : const struct TALER_PlanchetMasterSecretP *planchet;
196 : const struct TALER_ExchangeWithdrawValues *ewv;
197 : char *cref;
198 : unsigned int idx;
199 :
200 0 : rrs->is = is;
201 0 : if (GNUNET_OK !=
202 0 : TALER_TESTING_parse_coin_reference (
203 : rrs->coin_reference,
204 : &cref,
205 : &idx))
206 : {
207 0 : TALER_TESTING_interpreter_fail (is);
208 0 : return;
209 : }
210 :
211 0 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
212 : cref);
213 0 : GNUNET_free (cref);
214 0 : if (NULL == coin_cmd)
215 : {
216 0 : GNUNET_break (0);
217 0 : TALER_TESTING_interpreter_fail (is);
218 0 : return;
219 : }
220 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
221 : rrs->melt_reference);
222 0 : if (NULL == melt_cmd)
223 : {
224 0 : GNUNET_break (0);
225 0 : TALER_TESTING_interpreter_fail (is);
226 0 : return;
227 : }
228 0 : if (GNUNET_OK !=
229 0 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
230 : idx,
231 : &coin_priv))
232 : {
233 0 : GNUNET_break (0);
234 0 : TALER_TESTING_interpreter_fail (is);
235 0 : return;
236 : }
237 0 : if (GNUNET_OK !=
238 0 : TALER_TESTING_get_trait_exchange_wd_value (melt_cmd,
239 : idx,
240 : &ewv))
241 : {
242 0 : GNUNET_break (0);
243 0 : TALER_TESTING_interpreter_fail (is);
244 0 : return;
245 : }
246 0 : if (GNUNET_OK !=
247 0 : TALER_TESTING_get_trait_planchet_secrets (coin_cmd,
248 : idx,
249 : &planchet))
250 : {
251 0 : GNUNET_break (0);
252 0 : TALER_TESTING_interpreter_fail (is);
253 0 : return;
254 : }
255 0 : if (GNUNET_OK !=
256 0 : TALER_TESTING_get_trait_refresh_secret (melt_cmd,
257 : &rplanchet))
258 : {
259 0 : GNUNET_break (0);
260 0 : TALER_TESTING_interpreter_fail (is);
261 0 : return;
262 : }
263 0 : if (GNUNET_OK !=
264 0 : TALER_TESTING_get_trait_denom_pub (coin_cmd,
265 : idx,
266 : &denom_pub))
267 : {
268 0 : GNUNET_break (0);
269 0 : TALER_TESTING_interpreter_fail (is);
270 0 : return;
271 : }
272 0 : if (GNUNET_OK !=
273 0 : TALER_TESTING_get_trait_denom_sig (coin_cmd,
274 : idx,
275 : &coin_sig))
276 : {
277 0 : GNUNET_break (0);
278 0 : TALER_TESTING_interpreter_fail (is);
279 0 : return;
280 : }
281 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
282 : "Trying to recoup_refresh denomination '%s'\n",
283 : TALER_B2S (&denom_pub->h_key));
284 0 : rrs->ph = TALER_EXCHANGE_recoup_refresh (is->exchange,
285 : denom_pub,
286 : coin_sig,
287 : ewv,
288 : rplanchet,
289 : planchet,
290 : idx,
291 : &recoup_refresh_cb,
292 : rrs);
293 0 : GNUNET_assert (NULL != rrs->ph);
294 : }
295 :
296 :
297 : /**
298 : * Cleanup the "recoup_refresh" CMD state, and possibly cancel
299 : * a pending operation thereof.
300 : *
301 : * @param cls closure.
302 : * @param cmd the command which is being cleaned up.
303 : */
304 : static void
305 0 : recoup_refresh_cleanup (void *cls,
306 : const struct TALER_TESTING_Command *cmd)
307 : {
308 0 : struct RecoupRefreshState *rrs = cls;
309 0 : if (NULL != rrs->ph)
310 : {
311 0 : TALER_EXCHANGE_recoup_refresh_cancel (rrs->ph);
312 0 : rrs->ph = NULL;
313 : }
314 0 : GNUNET_free (rrs);
315 0 : }
316 :
317 :
318 : struct TALER_TESTING_Command
319 0 : TALER_TESTING_cmd_recoup_refresh (const char *label,
320 : unsigned int expected_response_code,
321 : const char *coin_reference,
322 : const char *melt_reference,
323 : const char *amount)
324 : {
325 : struct RecoupRefreshState *rrs;
326 :
327 0 : rrs = GNUNET_new (struct RecoupRefreshState);
328 0 : rrs->expected_response_code = expected_response_code;
329 0 : rrs->coin_reference = coin_reference;
330 0 : rrs->melt_reference = melt_reference;
331 0 : if (GNUNET_OK !=
332 0 : TALER_string_to_amount (amount,
333 : &rrs->amount))
334 : {
335 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336 : "Failed to parse amount `%s' at %s\n",
337 : amount,
338 : label);
339 0 : GNUNET_assert (0);
340 : }
341 : {
342 0 : struct TALER_TESTING_Command cmd = {
343 : .cls = rrs,
344 : .label = label,
345 : .run = &recoup_refresh_run,
346 : .cleanup = &recoup_refresh_cleanup
347 : };
348 :
349 0 : return cmd;
350 : }
351 : }
|