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_reserve_open.c
21 : * @brief Implement the /reserve/$RID/open test command.
22 : * @author Christian Grothoff
23 : */
24 : #include "taler/taler_json_lib.h"
25 : #include <gnunet/gnunet_curl_lib.h>
26 : struct OpenState;
27 : #define TALER_EXCHANGE_POST_RESERVES_OPEN_RESULT_CLOSURE struct OpenState
28 : #include "taler/exchange/post-reserves-RESERVE_PUB-open.h"
29 : #include "taler/taler_testing_lib.h"
30 :
31 :
32 : /**
33 : * Information we track per coin used to pay for opening the
34 : * reserve.
35 : */
36 : struct CoinDetail
37 : {
38 : /**
39 : * Name of the command and index of the coin to use.
40 : */
41 : const char *name;
42 :
43 : /**
44 : * Amount to charge to this coin.
45 : */
46 : struct TALER_Amount amount;
47 : };
48 :
49 :
50 : /**
51 : * State for a "open" CMD.
52 : */
53 : struct OpenState
54 : {
55 : /**
56 : * Label to the command which created the reserve to check,
57 : * needed to resort the reserve key.
58 : */
59 : const char *reserve_reference;
60 :
61 : /**
62 : * Requested expiration time.
63 : */
64 : struct GNUNET_TIME_Relative req_expiration_time;
65 :
66 : /**
67 : * Requested minimum number of purses.
68 : */
69 : uint32_t min_purses;
70 :
71 : /**
72 : * Amount to pay for the opening from the reserve balance.
73 : */
74 : struct TALER_Amount reserve_pay;
75 :
76 : /**
77 : * Handle to the "reserve open" operation.
78 : */
79 : struct TALER_EXCHANGE_PostReservesOpenHandle *rsh;
80 :
81 : /**
82 : * Expected reserve balance.
83 : */
84 : const char *expected_balance;
85 :
86 : /**
87 : * Length of the @e cd array.
88 : */
89 : unsigned int cpl;
90 :
91 : /**
92 : * Coin details, array of length @e cpl.
93 : */
94 : struct CoinDetail *cd;
95 :
96 : /**
97 : * Private key of the reserve being analyzed.
98 : */
99 : const struct TALER_ReservePrivateKeyP *reserve_priv;
100 :
101 : /**
102 : * Public key of the reserve being analyzed.
103 : */
104 : struct TALER_ReservePublicKeyP reserve_pub;
105 :
106 : /**
107 : * Expected HTTP response code.
108 : */
109 : unsigned int expected_response_code;
110 :
111 : /**
112 : * Interpreter state.
113 : */
114 : struct TALER_TESTING_Interpreter *is;
115 : };
116 :
117 :
118 : /**
119 : * Check that the reserve balance and HTTP response code are
120 : * both acceptable.
121 : *
122 : * @param ss closure.
123 : * @param rs HTTP response details
124 : */
125 : static void
126 6 : reserve_open_cb (struct OpenState *ss,
127 : const struct TALER_EXCHANGE_PostReservesOpenResponse *rs)
128 : {
129 6 : struct TALER_TESTING_Interpreter *is = ss->is;
130 :
131 6 : ss->rsh = NULL;
132 6 : if (ss->expected_response_code != rs->hr.http_status)
133 : {
134 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
135 : "Unexpected HTTP response code: %d in %s:%u\n",
136 : rs->hr.http_status,
137 : __FILE__,
138 : __LINE__);
139 0 : json_dumpf (rs->hr.reply,
140 : stderr,
141 : JSON_INDENT (2));
142 0 : TALER_TESTING_interpreter_fail (ss->is);
143 0 : return;
144 : }
145 6 : if (MHD_HTTP_OK != rs->hr.http_status)
146 : {
147 2 : TALER_TESTING_interpreter_next (is);
148 2 : return;
149 : }
150 4 : TALER_TESTING_interpreter_next (is);
151 : }
152 :
153 :
154 : /**
155 : * Run the command.
156 : *
157 : * @param cls closure.
158 : * @param cmd the command being executed.
159 : * @param is the interpreter state.
160 : */
161 : static void
162 6 : open_run (void *cls,
163 : const struct TALER_TESTING_Command *cmd,
164 : struct TALER_TESTING_Interpreter *is)
165 6 : {
166 6 : struct OpenState *ss = cls;
167 : const struct TALER_TESTING_Command *create_reserve;
168 6 : struct TALER_EXCHANGE_PurseDeposit cp[GNUNET_NZL (ss->cpl)];
169 :
170 6 : ss->is = is;
171 : create_reserve
172 6 : = TALER_TESTING_interpreter_lookup_command (is,
173 : ss->reserve_reference);
174 :
175 6 : if (NULL == create_reserve)
176 : {
177 0 : GNUNET_break (0);
178 0 : TALER_TESTING_interpreter_fail (is);
179 0 : return;
180 : }
181 6 : if (GNUNET_OK !=
182 6 : TALER_TESTING_get_trait_reserve_priv (create_reserve,
183 : &ss->reserve_priv))
184 : {
185 0 : GNUNET_break (0);
186 0 : TALER_LOG_ERROR ("Failed to find reserve_priv for open query\n");
187 0 : TALER_TESTING_interpreter_fail (is);
188 0 : return;
189 : }
190 6 : GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv,
191 : &ss->reserve_pub.eddsa_pub);
192 8 : for (unsigned int i = 0; i<ss->cpl; i++)
193 : {
194 2 : struct TALER_EXCHANGE_PurseDeposit *cpi = &cp[i];
195 : const struct TALER_TESTING_Command *cmdi;
196 : const struct TALER_AgeCommitmentProof *age_commitment_proof;
197 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
198 : const struct TALER_DenominationSignature *denom_sig;
199 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
200 : char *cref;
201 : unsigned int cidx;
202 :
203 2 : if (GNUNET_OK !=
204 2 : TALER_TESTING_parse_coin_reference (ss->cd[i].name,
205 : &cref,
206 : &cidx))
207 : {
208 0 : GNUNET_break (0);
209 0 : TALER_LOG_ERROR ("Failed to parse coin reference `%s'\n",
210 : ss->cd[i].name);
211 0 : TALER_TESTING_interpreter_fail (is);
212 0 : return;
213 : }
214 2 : cmdi = TALER_TESTING_interpreter_lookup_command (is,
215 : cref);
216 2 : GNUNET_free (cref);
217 2 : if (NULL == cmdi)
218 : {
219 0 : GNUNET_break (0);
220 0 : TALER_LOG_ERROR ("Command `%s' not found\n",
221 : ss->cd[i].name);
222 0 : TALER_TESTING_interpreter_fail (is);
223 0 : return;
224 : }
225 2 : if ( (GNUNET_OK !=
226 2 : TALER_TESTING_get_trait_age_commitment_proof (cmdi,
227 : cidx,
228 : &age_commitment_proof))
229 2 : ||
230 : (GNUNET_OK !=
231 2 : TALER_TESTING_get_trait_coin_priv (cmdi,
232 : cidx,
233 2 : &coin_priv)) ||
234 : (GNUNET_OK !=
235 2 : TALER_TESTING_get_trait_denom_sig (cmdi,
236 : cidx,
237 2 : &denom_sig)) ||
238 : (GNUNET_OK !=
239 2 : TALER_TESTING_get_trait_denom_pub (cmdi,
240 : cidx,
241 : &denom_pub)) )
242 : {
243 0 : GNUNET_break (0);
244 0 : TALER_LOG_ERROR ("Coin trait not found in `%s'\n",
245 : ss->cd[i].name);
246 0 : TALER_TESTING_interpreter_fail (is);
247 0 : return;
248 : }
249 2 : cpi->age_commitment_proof = age_commitment_proof;
250 2 : cpi->coin_priv = *coin_priv;
251 2 : cpi->denom_sig = *denom_sig;
252 2 : cpi->amount = ss->cd[i].amount;
253 2 : cpi->h_denom_pub = denom_pub->h_key;
254 : }
255 12 : ss->rsh = TALER_EXCHANGE_post_reserves_open_create (
256 : TALER_TESTING_interpreter_get_context (is),
257 : TALER_TESTING_get_exchange_url (is),
258 : TALER_TESTING_get_keys (is),
259 : ss->reserve_priv,
260 6 : &ss->reserve_pay,
261 : ss->cpl,
262 : cp,
263 : GNUNET_TIME_relative_to_timestamp (ss->req_expiration_time),
264 : ss->min_purses);
265 6 : if (NULL == ss->rsh)
266 : {
267 0 : GNUNET_break (0);
268 0 : TALER_TESTING_interpreter_fail (is);
269 0 : return;
270 : }
271 : {
272 : enum TALER_ErrorCode ec;
273 :
274 6 : ec = TALER_EXCHANGE_post_reserves_open_start (ss->rsh,
275 : &reserve_open_cb,
276 : ss);
277 6 : if (TALER_EC_NONE != ec)
278 : {
279 0 : GNUNET_break (0);
280 0 : ss->rsh = NULL;
281 0 : TALER_TESTING_interpreter_fail (is);
282 0 : return;
283 : }
284 : }
285 : }
286 :
287 :
288 : /**
289 : * Cleanup the state from a "reserve open" CMD, and possibly
290 : * cancel a pending operation thereof.
291 : *
292 : * @param cls closure.
293 : * @param cmd the command which is being cleaned up.
294 : */
295 : static void
296 6 : open_cleanup (void *cls,
297 : const struct TALER_TESTING_Command *cmd)
298 : {
299 6 : struct OpenState *ss = cls;
300 :
301 6 : if (NULL != ss->rsh)
302 : {
303 0 : TALER_TESTING_command_incomplete (ss->is,
304 : cmd->label);
305 0 : TALER_EXCHANGE_post_reserves_open_cancel (ss->rsh);
306 0 : ss->rsh = NULL;
307 : }
308 6 : GNUNET_free (ss->cd);
309 6 : GNUNET_free (ss);
310 6 : }
311 :
312 :
313 : struct TALER_TESTING_Command
314 6 : TALER_TESTING_cmd_reserve_open (const char *label,
315 : const char *reserve_reference,
316 : const char *reserve_pay,
317 : struct GNUNET_TIME_Relative expiration_time,
318 : uint32_t min_purses,
319 : unsigned int expected_response_code,
320 : ...)
321 : {
322 : struct OpenState *ss;
323 : va_list ap;
324 : const char *name;
325 : unsigned int i;
326 :
327 6 : GNUNET_assert (NULL != reserve_reference);
328 6 : ss = GNUNET_new (struct OpenState);
329 6 : ss->reserve_reference = reserve_reference;
330 6 : ss->req_expiration_time = expiration_time;
331 6 : ss->min_purses = min_purses;
332 6 : GNUNET_assert (GNUNET_OK ==
333 : TALER_string_to_amount (reserve_pay,
334 : &ss->reserve_pay));
335 6 : ss->expected_response_code = expected_response_code;
336 6 : va_start (ap,
337 : expected_response_code);
338 10 : while (NULL != (name = va_arg (ap, const char *)))
339 4 : ss->cpl++;
340 6 : va_end (ap);
341 6 : GNUNET_assert (0 == (ss->cpl % 2));
342 6 : ss->cpl /= 2; /* name and amount per coin */
343 6 : ss->cd = GNUNET_new_array (ss->cpl,
344 : struct CoinDetail);
345 6 : i = 0;
346 6 : va_start (ap,
347 : expected_response_code);
348 8 : while (NULL != (name = va_arg (ap, const char *)))
349 : {
350 2 : struct CoinDetail *cd = &ss->cd[i];
351 2 : cd->name = name;
352 2 : GNUNET_assert (GNUNET_OK ==
353 : TALER_string_to_amount (va_arg (ap,
354 : const char *),
355 : &cd->amount));
356 2 : i++;
357 : }
358 6 : va_end (ap);
359 : {
360 6 : struct TALER_TESTING_Command cmd = {
361 : .cls = ss,
362 : .label = label,
363 : .run = &open_run,
364 : .cleanup = &open_cleanup
365 : };
366 :
367 6 : return cmd;
368 : }
369 : }
|