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 "taler/taler_json_lib.h"
25 : #include <gnunet/gnunet_curl_lib.h>
26 : struct RecoupRefreshState;
27 : #define TALER_EXCHANGE_POST_RECOUP_REFRESH_RESULT_CLOSURE struct \
28 : RecoupRefreshState
29 : #include "taler/exchange/post-recoup-refresh.h"
30 : #include "taler/taler_testing_lib.h"
31 :
32 :
33 : /**
34 : * State for a "pay back" CMD.
35 : */
36 : struct RecoupRefreshState
37 : {
38 : /**
39 : * Expected HTTP status code.
40 : */
41 : unsigned int expected_response_code;
42 :
43 : /**
44 : * Command that offers a reserve private key,
45 : * plus a coin to be paid back.
46 : */
47 : const char *coin_reference;
48 :
49 : /**
50 : * Entry in the old coin's history generated by this operation.
51 : */
52 : struct TALER_EXCHANGE_CoinHistoryEntry che_old;
53 :
54 : /**
55 : * Entry in the recouped coin's history generated by this operation.
56 : */
57 : struct TALER_EXCHANGE_CoinHistoryEntry che_new;
58 :
59 : /**
60 : * Public key of the refunded coin.
61 : */
62 : struct TALER_CoinSpendPublicKeyP coin_pub_old;
63 :
64 : /**
65 : * Public key of the refunded coin.
66 : */
67 : struct TALER_CoinSpendPublicKeyP coin_pub_new;
68 :
69 : /**
70 : * Amount to be recouped.
71 : */
72 : struct TALER_Amount amount;
73 :
74 : /**
75 : * The interpreter state.
76 : */
77 : struct TALER_TESTING_Interpreter *is;
78 :
79 : /**
80 : * Handle to the ongoing operation.
81 : */
82 : struct TALER_EXCHANGE_PostRecoupRefreshHandle *ph;
83 :
84 : /**
85 : * NULL if coin was not refreshed, otherwise reference
86 : * to the melt operation underlying @a coin_reference.
87 : */
88 : const char *melt_reference;
89 :
90 : };
91 :
92 :
93 : /**
94 : * Check the result of the recoup_refresh request: checks whether
95 : * the HTTP response code is good, and that the coin that
96 : * was paid back belonged to the right old coin.
97 : *
98 : * @param rrs closure
99 : * @param rrr response details
100 : */
101 : static void
102 0 : recoup_refresh_cb (struct RecoupRefreshState *rrs,
103 : const struct TALER_EXCHANGE_PostRecoupRefreshResponse *rrr)
104 : {
105 0 : const struct TALER_EXCHANGE_HttpResponse *hr = &rrr->hr;
106 0 : struct TALER_TESTING_Interpreter *is = rrs->is;
107 : char *cref;
108 : unsigned int idx;
109 :
110 0 : rrs->ph = NULL;
111 0 : if (rrs->expected_response_code != hr->http_status)
112 : {
113 0 : TALER_TESTING_unexpected_status (is,
114 : hr->http_status,
115 : rrs->expected_response_code);
116 0 : return;
117 : }
118 :
119 0 : if (GNUNET_OK !=
120 0 : TALER_TESTING_parse_coin_reference (
121 : rrs->coin_reference,
122 : &cref,
123 : &idx))
124 : {
125 0 : TALER_TESTING_interpreter_fail (is);
126 0 : return;
127 : }
128 : (void) idx; /* do NOT use! We ignore 'idx', must be 0 for melt! */
129 :
130 0 : GNUNET_free (cref);
131 0 : switch (hr->http_status)
132 : {
133 0 : case MHD_HTTP_OK:
134 : /* check old_coin_pub */
135 : {
136 : const struct TALER_TESTING_Command *melt_cmd;
137 : const struct TALER_CoinSpendPrivateKeyP *dirty_priv;
138 : struct TALER_CoinSpendPublicKeyP oc;
139 :
140 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
141 : rrs->melt_reference);
142 0 : if (NULL == melt_cmd)
143 : {
144 0 : GNUNET_break (0);
145 0 : TALER_TESTING_interpreter_fail (is);
146 0 : return;
147 : }
148 0 : if (GNUNET_OK !=
149 0 : TALER_TESTING_get_trait_coin_priv (melt_cmd,
150 : 0,
151 : &dirty_priv))
152 : {
153 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
154 : "Coin %u not found in command %s\n",
155 : 0,
156 : rrs->melt_reference);
157 0 : GNUNET_break (0);
158 0 : TALER_TESTING_interpreter_fail (is);
159 0 : return;
160 : }
161 0 : GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv,
162 : &oc.eddsa_pub);
163 0 : if (0 != GNUNET_memcmp (&oc,
164 : &rrr->details.ok.old_coin_pub))
165 : {
166 0 : GNUNET_break (0);
167 0 : TALER_TESTING_interpreter_fail (is);
168 0 : return;
169 : }
170 : }
171 0 : break;
172 0 : case MHD_HTTP_NOT_FOUND:
173 0 : break;
174 0 : case MHD_HTTP_CONFLICT:
175 0 : break;
176 0 : default:
177 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
178 : "Unmanaged HTTP status code %u/%d.\n",
179 : hr->http_status,
180 : (int) hr->ec);
181 0 : break;
182 : }
183 0 : TALER_TESTING_interpreter_next (is);
184 : }
185 :
186 :
187 : /**
188 : * Run the command.
189 : *
190 : * @param cls closure.
191 : * @param cmd the command to execute.
192 : * @param is the interpreter state.
193 : */
194 : static void
195 0 : recoup_refresh_run (void *cls,
196 : const struct TALER_TESTING_Command *cmd,
197 : struct TALER_TESTING_Interpreter *is)
198 : {
199 0 : struct RecoupRefreshState *rrs = cls;
200 : const struct TALER_TESTING_Command *coin_cmd;
201 : const struct TALER_TESTING_Command *melt_cmd;
202 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
203 : const struct TALER_CoinSpendPrivateKeyP *coin_priv_old;
204 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
205 : const struct TALER_DenominationSignature *coin_sig;
206 : const struct TALER_PublicRefreshMasterSeedP *rms;
207 : const struct TALER_PlanchetMasterSecretP *planchet;
208 : const struct TALER_ExchangeBlindingValues *ewv;
209 : char *cref;
210 : unsigned int idx;
211 : struct TALER_DenominationHashP h_denom_pub;
212 :
213 0 : rrs->is = is;
214 0 : if (GNUNET_OK !=
215 0 : TALER_TESTING_parse_coin_reference (
216 : rrs->coin_reference,
217 : &cref,
218 : &idx))
219 : {
220 0 : TALER_TESTING_interpreter_fail (is);
221 0 : return;
222 : }
223 :
224 0 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
225 : cref);
226 0 : GNUNET_free (cref);
227 0 : if (NULL == coin_cmd)
228 : {
229 0 : GNUNET_break (0);
230 0 : TALER_TESTING_interpreter_fail (is);
231 0 : return;
232 : }
233 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
234 : rrs->melt_reference);
235 0 : if (NULL == melt_cmd)
236 : {
237 0 : GNUNET_break (0);
238 0 : TALER_TESTING_interpreter_fail (is);
239 0 : return;
240 : }
241 0 : if (GNUNET_OK !=
242 0 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
243 : idx,
244 : &coin_priv))
245 : {
246 0 : GNUNET_break (0);
247 0 : TALER_TESTING_interpreter_fail (is);
248 0 : return;
249 : }
250 0 : if (GNUNET_OK !=
251 0 : TALER_TESTING_get_trait_coin_priv (melt_cmd,
252 : 0,
253 : &coin_priv_old))
254 : {
255 0 : GNUNET_break (0);
256 0 : TALER_TESTING_interpreter_fail (is);
257 0 : return;
258 : }
259 0 : GNUNET_CRYPTO_eddsa_key_get_public (
260 0 : &coin_priv->eddsa_priv,
261 : &rrs->coin_pub_new.eddsa_pub);
262 0 : GNUNET_CRYPTO_eddsa_key_get_public (
263 0 : &coin_priv_old->eddsa_priv,
264 : &rrs->coin_pub_old.eddsa_pub);
265 :
266 0 : if (GNUNET_OK !=
267 0 : TALER_TESTING_get_trait_exchange_blinding_values (melt_cmd,
268 : idx,
269 : &ewv))
270 : {
271 0 : GNUNET_break (0);
272 0 : TALER_TESTING_interpreter_fail (is);
273 0 : return;
274 : }
275 0 : if (GNUNET_OK !=
276 0 : TALER_TESTING_get_trait_planchet_secrets (coin_cmd,
277 : idx,
278 : &planchet))
279 : {
280 0 : GNUNET_break (0);
281 0 : TALER_TESTING_interpreter_fail (is);
282 0 : return;
283 : }
284 0 : if (GNUNET_OK !=
285 0 : TALER_TESTING_get_trait_refresh_seed (melt_cmd,
286 : &rms))
287 : {
288 0 : GNUNET_break (0);
289 0 : TALER_TESTING_interpreter_fail (is);
290 0 : return;
291 : }
292 0 : if (GNUNET_OK !=
293 0 : TALER_TESTING_get_trait_denom_pub (coin_cmd,
294 : idx,
295 : &denom_pub))
296 : {
297 0 : GNUNET_break (0);
298 0 : TALER_TESTING_interpreter_fail (is);
299 0 : return;
300 : }
301 0 : if (GNUNET_OK !=
302 0 : TALER_TESTING_get_trait_denom_sig (coin_cmd,
303 : idx,
304 : &coin_sig))
305 : {
306 0 : GNUNET_break (0);
307 0 : TALER_TESTING_interpreter_fail (is);
308 0 : return;
309 : }
310 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
311 : "Trying to recoup_refresh denomination '%s'\n",
312 : TALER_B2S (&denom_pub->h_key));
313 : rrs->che_old.type
314 0 : = TALER_EXCHANGE_CTT_OLD_COIN_RECOUP;
315 : rrs->che_old.amount
316 0 : = rrs->amount;
317 : rrs->che_old.details.old_coin_recoup.new_coin_pub
318 0 : = rrs->coin_pub_new;
319 : rrs->che_new.type
320 0 : = TALER_EXCHANGE_CTT_RECOUP_REFRESH;
321 : rrs->che_new.amount
322 0 : = rrs->amount;
323 : rrs->che_new.details.recoup_refresh.old_coin_pub
324 0 : = rrs->coin_pub_old;
325 0 : TALER_planchet_blinding_secret_create (
326 : planchet,
327 : ewv,
328 : &rrs->che_new.details.recoup_refresh.coin_bks);
329 0 : TALER_denom_pub_hash (&denom_pub->key,
330 : &h_denom_pub);
331 0 : TALER_wallet_recoup_refresh_sign (
332 : &h_denom_pub,
333 0 : &rrs->che_new.details.recoup_refresh.coin_bks,
334 : coin_priv,
335 : &rrs->che_new.details.recoup_refresh.coin_sig);
336 0 : rrs->ph = TALER_EXCHANGE_post_recoup_refresh_create (
337 : TALER_TESTING_interpreter_get_context (is),
338 : TALER_TESTING_get_exchange_url (is),
339 : TALER_TESTING_get_keys (is),
340 : denom_pub,
341 : coin_sig,
342 : ewv,
343 : rms,
344 : planchet,
345 : idx);
346 0 : GNUNET_assert (NULL != rrs->ph);
347 0 : GNUNET_assert (TALER_EC_NONE ==
348 : TALER_EXCHANGE_post_recoup_refresh_start (rrs->ph,
349 : &recoup_refresh_cb,
350 : rrs));
351 : }
352 :
353 :
354 : /**
355 : * Cleanup the "recoup_refresh" CMD state, and possibly cancel
356 : * a pending operation thereof.
357 : *
358 : * @param cls closure.
359 : * @param cmd the command which is being cleaned up.
360 : */
361 : static void
362 0 : recoup_refresh_cleanup (void *cls,
363 : const struct TALER_TESTING_Command *cmd)
364 : {
365 0 : struct RecoupRefreshState *rrs = cls;
366 0 : if (NULL != rrs->ph)
367 : {
368 0 : TALER_EXCHANGE_post_recoup_refresh_cancel (rrs->ph);
369 0 : rrs->ph = NULL;
370 : }
371 0 : GNUNET_free (rrs);
372 0 : }
373 :
374 :
375 : /**
376 : * Offer internal data from a "recoup-refresh" CMD state to other
377 : * commands.
378 : *
379 : * @param cls closure
380 : * @param[out] ret result (could be anything)
381 : * @param trait name of the trait
382 : * @param index index number of the object to offer.
383 : * @return #GNUNET_OK on success
384 : */
385 : static enum GNUNET_GenericReturnValue
386 0 : recoup_refresh_traits (void *cls,
387 : const void **ret,
388 : const char *trait,
389 : unsigned int index)
390 : {
391 0 : struct RecoupRefreshState *rrs = cls;
392 : struct TALER_TESTING_Trait traits[] = {
393 0 : TALER_TESTING_make_trait_coin_history (0,
394 0 : &rrs->che_old),
395 0 : TALER_TESTING_make_trait_coin_pub (0,
396 0 : &rrs->coin_pub_old),
397 0 : TALER_TESTING_make_trait_coin_history (1,
398 0 : &rrs->che_new),
399 0 : TALER_TESTING_make_trait_coin_pub (1,
400 0 : &rrs->coin_pub_new),
401 0 : TALER_TESTING_trait_end ()
402 : };
403 :
404 0 : return TALER_TESTING_get_trait (traits,
405 : ret,
406 : trait,
407 : index);
408 : }
409 :
410 :
411 : struct TALER_TESTING_Command
412 0 : TALER_TESTING_cmd_recoup_refresh (const char *label,
413 : unsigned int expected_response_code,
414 : const char *coin_reference,
415 : const char *melt_reference,
416 : const char *amount)
417 : {
418 : struct RecoupRefreshState *rrs;
419 :
420 0 : rrs = GNUNET_new (struct RecoupRefreshState);
421 0 : rrs->expected_response_code = expected_response_code;
422 0 : rrs->coin_reference = coin_reference;
423 0 : rrs->melt_reference = melt_reference;
424 0 : if (GNUNET_OK !=
425 0 : TALER_string_to_amount (amount,
426 : &rrs->amount))
427 : {
428 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 : "Failed to parse amount `%s' at %s\n",
430 : amount,
431 : label);
432 0 : GNUNET_assert (0);
433 : }
434 : {
435 0 : struct TALER_TESTING_Command cmd = {
436 : .cls = rrs,
437 : .label = label,
438 : .run = &recoup_refresh_run,
439 : .cleanup = &recoup_refresh_cleanup,
440 : .traits = &recoup_refresh_traits
441 : };
442 :
443 0 : return cmd;
444 : }
445 : }
|