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
6 : under the terms of the GNU General Public License as published by
7 : the Free Software Foundation; either version 3, or (at your
8 : 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 GNU
13 : 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_contract_get.c
21 : * @brief command for testing GET /contracts/$CPUB
22 : * @author Christian Grothoff
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 : #include "taler_signatures.h"
29 : #include "backoff.h"
30 :
31 :
32 : /**
33 : * State for a "contract get" CMD.
34 : */
35 : struct ContractGetState
36 : {
37 :
38 : /**
39 : * JSON string describing the resulting contract.
40 : */
41 : json_t *contract_terms;
42 :
43 : /**
44 : * Private key to decrypt the contract.
45 : */
46 : struct TALER_ContractDiffiePrivateP contract_priv;
47 :
48 : /**
49 : * Set to the returned merge key.
50 : */
51 : struct TALER_PurseMergePrivateKeyP merge_priv;
52 :
53 : /**
54 : * Public key of the purse.
55 : */
56 : struct TALER_PurseContractPublicKeyP purse_pub;
57 :
58 : /**
59 : * Reference to the command that uploaded the contract.
60 : */
61 : const char *contract_ref;
62 :
63 : /**
64 : * ContractGet handle while operation is running.
65 : */
66 : struct TALER_EXCHANGE_ContractsGetHandle *dh;
67 :
68 : /**
69 : * Interpreter state.
70 : */
71 : struct TALER_TESTING_Interpreter *is;
72 :
73 : /**
74 : * Expected HTTP response code.
75 : */
76 : unsigned int expected_response_code;
77 :
78 : /**
79 : * True if this is for a 'merge' operation,
80 : * 'false' if this is for a 'deposit' operation.
81 : */
82 : bool merge;
83 :
84 : };
85 :
86 :
87 : /**
88 : * Callback to analyze the /contracts/$CPUB response, just used to check if
89 : * the response code is acceptable.
90 : *
91 : * @param cls closure.
92 : * @param dr get response details
93 : */
94 : static void
95 0 : get_cb (void *cls,
96 : const struct TALER_EXCHANGE_ContractGetResponse *dr)
97 : {
98 0 : struct ContractGetState *ds = cls;
99 : const struct TALER_TESTING_Command *ref;
100 :
101 0 : ds->dh = NULL;
102 0 : if (ds->expected_response_code != dr->hr.http_status)
103 : {
104 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
105 : "Unexpected response code %u to command %s in %s:%u\n",
106 : dr->hr.http_status,
107 : ds->is->commands[ds->is->ip].label,
108 : __FILE__,
109 : __LINE__);
110 0 : json_dumpf (dr->hr.reply,
111 : stderr,
112 : 0);
113 0 : TALER_TESTING_interpreter_fail (ds->is);
114 0 : return;
115 : }
116 0 : ref = TALER_TESTING_interpreter_lookup_command (ds->is,
117 : ds->contract_ref);
118 0 : if (MHD_HTTP_OK == dr->hr.http_status)
119 : {
120 : const struct TALER_PurseMergePrivateKeyP *mp;
121 : const json_t *ct;
122 :
123 0 : ds->purse_pub = dr->details.success.purse_pub;
124 0 : if (ds->merge)
125 : {
126 0 : if (GNUNET_OK !=
127 0 : TALER_TESTING_get_trait_merge_priv (ref,
128 : &mp))
129 : {
130 0 : GNUNET_break (0);
131 0 : TALER_TESTING_interpreter_fail (ds->is);
132 0 : return;
133 : }
134 0 : ds->contract_terms =
135 0 : TALER_CRYPTO_contract_decrypt_for_merge (
136 0 : &ds->contract_priv,
137 0 : &ds->purse_pub,
138 : dr->details.success.econtract,
139 : dr->details.success.econtract_size,
140 : &ds->merge_priv);
141 0 : if (0 !=
142 0 : GNUNET_memcmp (mp,
143 : &ds->merge_priv))
144 : {
145 0 : GNUNET_break (0);
146 0 : TALER_TESTING_interpreter_fail (ds->is);
147 0 : return;
148 : }
149 : }
150 : else
151 : {
152 0 : ds->contract_terms =
153 0 : TALER_CRYPTO_contract_decrypt_for_deposit (
154 0 : &ds->contract_priv,
155 : dr->details.success.econtract,
156 : dr->details.success.econtract_size);
157 : }
158 0 : if (NULL == ds->contract_terms)
159 : {
160 0 : GNUNET_break (0);
161 0 : TALER_TESTING_interpreter_fail (ds->is);
162 0 : return;
163 : }
164 0 : if (GNUNET_OK !=
165 0 : TALER_TESTING_get_trait_contract_terms (ref,
166 : &ct))
167 : {
168 0 : GNUNET_break (0);
169 0 : TALER_TESTING_interpreter_fail (ds->is);
170 0 : return;
171 : }
172 0 : if (1 != /* 1: equal, 0: not equal */
173 0 : json_equal (ct,
174 0 : ds->contract_terms))
175 : {
176 0 : GNUNET_break (0);
177 0 : TALER_TESTING_interpreter_fail (ds->is);
178 0 : return;
179 : }
180 : }
181 0 : TALER_TESTING_interpreter_next (ds->is);
182 : }
183 :
184 :
185 : /**
186 : * Run the command.
187 : *
188 : * @param cls closure.
189 : * @param cmd the command to execute.
190 : * @param is the interpreter state.
191 : */
192 : static void
193 0 : get_run (void *cls,
194 : const struct TALER_TESTING_Command *cmd,
195 : struct TALER_TESTING_Interpreter *is)
196 : {
197 0 : struct ContractGetState *ds = cls;
198 : const struct TALER_ContractDiffiePrivateP *contract_priv;
199 : const struct TALER_TESTING_Command *ref;
200 :
201 : (void) cmd;
202 0 : ds->is = is;
203 0 : ref = TALER_TESTING_interpreter_lookup_command (ds->is,
204 : ds->contract_ref);
205 0 : GNUNET_assert (NULL != ref);
206 0 : if (GNUNET_OK !=
207 0 : TALER_TESTING_get_trait_contract_priv (ref,
208 : &contract_priv))
209 : {
210 0 : GNUNET_break (0);
211 0 : TALER_TESTING_interpreter_fail (ds->is);
212 0 : return;
213 : }
214 0 : ds->contract_priv = *contract_priv;
215 0 : ds->dh = TALER_EXCHANGE_contract_get (
216 : is->exchange,
217 : contract_priv,
218 : &get_cb,
219 : ds);
220 0 : if (NULL == ds->dh)
221 : {
222 0 : GNUNET_break (0);
223 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
224 : "Could not GET contract\n");
225 0 : TALER_TESTING_interpreter_fail (is);
226 0 : return;
227 : }
228 : }
229 :
230 :
231 : /**
232 : * Free the state of a "get" CMD, and possibly cancel a
233 : * pending operation thereof.
234 : *
235 : * @param cls closure, must be a `struct ContractGetState`.
236 : * @param cmd the command which is being cleaned up.
237 : */
238 : static void
239 0 : get_cleanup (void *cls,
240 : const struct TALER_TESTING_Command *cmd)
241 : {
242 0 : struct ContractGetState *ds = cls;
243 :
244 0 : if (NULL != ds->dh)
245 : {
246 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
247 : "Command %u (%s) did not complete\n",
248 : ds->is->ip,
249 : cmd->label);
250 0 : TALER_EXCHANGE_contract_get_cancel (ds->dh);
251 0 : ds->dh = NULL;
252 : }
253 0 : json_decref (ds->contract_terms);
254 0 : GNUNET_free (ds);
255 0 : }
256 :
257 :
258 : /**
259 : * Offer internal data from a "get" CMD, to other commands.
260 : *
261 : * @param cls closure.
262 : * @param[out] ret result.
263 : * @param trait name of the trait.
264 : * @param index index number of the object to offer.
265 : * @return #GNUNET_OK on success.
266 : */
267 : static enum GNUNET_GenericReturnValue
268 0 : get_traits (void *cls,
269 : const void **ret,
270 : const char *trait,
271 : unsigned int index)
272 : {
273 0 : struct ContractGetState *ds = cls;
274 : struct TALER_TESTING_Trait traits[] = {
275 0 : TALER_TESTING_make_trait_merge_priv (&ds->merge_priv),
276 0 : TALER_TESTING_make_trait_purse_pub (&ds->purse_pub),
277 0 : TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
278 0 : TALER_TESTING_trait_end ()
279 : };
280 :
281 : /* skip 'merge_priv' if we are in 'merge' mode */
282 0 : return TALER_TESTING_get_trait (&traits[ds->merge ? 0 : 1],
283 : ret,
284 : trait,
285 : index);
286 : }
287 :
288 :
289 : struct TALER_TESTING_Command
290 0 : TALER_TESTING_cmd_contract_get (
291 : const char *label,
292 : unsigned int expected_http_status,
293 : bool for_merge,
294 : const char *contract_ref)
295 : {
296 : struct ContractGetState *ds;
297 :
298 0 : ds = GNUNET_new (struct ContractGetState);
299 0 : ds->expected_response_code = expected_http_status;
300 0 : ds->contract_ref = contract_ref;
301 0 : ds->merge = for_merge;
302 : {
303 0 : struct TALER_TESTING_Command cmd = {
304 : .cls = ds,
305 : .label = label,
306 : .run = &get_run,
307 : .cleanup = &get_cleanup,
308 : .traits = &get_traits
309 : };
310 :
311 0 : return cmd;
312 : }
313 : }
314 :
315 :
316 : /* end of testing_api_cmd_contract_get.c */
|