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