Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file lib/exchange_api_deposits_get.c
19 : * @brief Implementation of the /deposits/ GET request
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <jansson.h>
24 : #include <microhttpd.h> /* just for HTTP status codes */
25 : #include <gnunet/gnunet_util_lib.h>
26 : #include <gnunet/gnunet_json_lib.h>
27 : #include <gnunet/gnunet_curl_lib.h>
28 : #include "taler_json_lib.h"
29 : #include "taler_exchange_service.h"
30 : #include "exchange_api_handle.h"
31 : #include "taler_signatures.h"
32 : #include "exchange_api_curl_defaults.h"
33 :
34 :
35 : /**
36 : * @brief A Deposit Get Handle
37 : */
38 : struct TALER_EXCHANGE_DepositGetHandle
39 : {
40 :
41 : /**
42 : * The keys of the this request handle will use
43 : */
44 : struct TALER_EXCHANGE_Keys *keys;
45 :
46 : /**
47 : * The url for this request.
48 : */
49 : char *url;
50 :
51 : /**
52 : * Context for #TEH_curl_easy_post(). Keeps the data that must
53 : * persist for Curl to make the upload.
54 : */
55 : struct TALER_CURL_PostContext ctx;
56 :
57 : /**
58 : * Handle for the request.
59 : */
60 : struct GNUNET_CURL_Job *job;
61 :
62 : /**
63 : * Function to call with the result.
64 : */
65 : TALER_EXCHANGE_DepositGetCallback cb;
66 :
67 : /**
68 : * Closure for @a cb.
69 : */
70 : void *cb_cls;
71 :
72 : /**
73 : * Hash over the wiring information of the merchant.
74 : */
75 : struct TALER_MerchantWireHashP h_wire;
76 :
77 : /**
78 : * Hash over the contract for which this deposit is made.
79 : */
80 : struct TALER_PrivateContractHashP h_contract_terms;
81 :
82 : /**
83 : * The coin's public key. This is the value that must have been
84 : * signed (blindly) by the Exchange.
85 : */
86 : struct TALER_CoinSpendPublicKeyP coin_pub;
87 :
88 : };
89 :
90 :
91 : /**
92 : * Function called when we're done processing the
93 : * HTTP /track/transaction request.
94 : *
95 : * @param cls the `struct TALER_EXCHANGE_DepositGetHandle`
96 : * @param response_code HTTP response code, 0 on error
97 : * @param response parsed JSON result, NULL on error
98 : */
99 : static void
100 8 : handle_deposit_wtid_finished (void *cls,
101 : long response_code,
102 : const void *response)
103 : {
104 8 : struct TALER_EXCHANGE_DepositGetHandle *dwh = cls;
105 8 : const json_t *j = response;
106 8 : struct TALER_EXCHANGE_GetDepositResponse dr = {
107 : .hr.reply = j,
108 8 : .hr.http_status = (unsigned int) response_code
109 : };
110 :
111 8 : dwh->job = NULL;
112 8 : switch (response_code)
113 : {
114 0 : case 0:
115 0 : dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
116 0 : break;
117 2 : case MHD_HTTP_OK:
118 : {
119 : struct GNUNET_JSON_Specification spec[] = {
120 2 : GNUNET_JSON_spec_fixed_auto (
121 : "wtid",
122 : &dr.details.ok.wtid),
123 2 : GNUNET_JSON_spec_timestamp (
124 : "execution_time",
125 : &dr.details.ok.execution_time),
126 2 : TALER_JSON_spec_amount_any (
127 : "coin_contribution",
128 : &dr.details.ok.coin_contribution),
129 2 : GNUNET_JSON_spec_fixed_auto (
130 : "exchange_sig",
131 : &dr.details.ok.exchange_sig),
132 2 : GNUNET_JSON_spec_fixed_auto (
133 : "exchange_pub",
134 : &dr.details.ok.exchange_pub),
135 2 : GNUNET_JSON_spec_end ()
136 : };
137 : const struct TALER_EXCHANGE_Keys *key_state;
138 :
139 2 : key_state = dwh->keys;
140 2 : GNUNET_assert (NULL != key_state);
141 2 : if (GNUNET_OK !=
142 2 : GNUNET_JSON_parse (j,
143 : spec,
144 : NULL, NULL))
145 : {
146 0 : GNUNET_break_op (0);
147 0 : dr.hr.http_status = 0;
148 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
149 0 : break;
150 : }
151 2 : if (GNUNET_OK !=
152 2 : TALER_EXCHANGE_test_signing_key (key_state,
153 : &dr.details.ok.exchange_pub))
154 : {
155 0 : GNUNET_break_op (0);
156 0 : dr.hr.http_status = 0;
157 0 : dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
158 0 : break;
159 : }
160 2 : if (GNUNET_OK !=
161 2 : TALER_exchange_online_confirm_wire_verify (
162 2 : &dwh->h_wire,
163 2 : &dwh->h_contract_terms,
164 : &dr.details.ok.wtid,
165 2 : &dwh->coin_pub,
166 : dr.details.ok.execution_time,
167 : &dr.details.ok.coin_contribution,
168 : &dr.details.ok.exchange_pub,
169 : &dr.details.ok.exchange_sig))
170 : {
171 0 : GNUNET_break_op (0);
172 0 : dr.hr.http_status = 0;
173 0 : dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
174 0 : break;
175 : }
176 2 : dwh->cb (dwh->cb_cls,
177 : &dr);
178 2 : TALER_EXCHANGE_deposits_get_cancel (dwh);
179 2 : return;
180 : }
181 4 : case MHD_HTTP_ACCEPTED:
182 : {
183 : /* Transaction known, but not executed yet */
184 4 : bool no_legi = false;
185 : struct GNUNET_JSON_Specification spec[] = {
186 4 : GNUNET_JSON_spec_timestamp (
187 : "execution_time",
188 : &dr.details.accepted.execution_time),
189 4 : GNUNET_JSON_spec_mark_optional (
190 : GNUNET_JSON_spec_fixed_auto (
191 : "account_pub",
192 : &dr.details.accepted.account_pub),
193 : NULL),
194 4 : GNUNET_JSON_spec_mark_optional (
195 : GNUNET_JSON_spec_uint64 (
196 : "requirement_row",
197 : &dr.details.accepted.requirement_row),
198 : &no_legi),
199 4 : GNUNET_JSON_spec_bool (
200 : "kyc_ok",
201 : &dr.details.accepted.kyc_ok),
202 4 : GNUNET_JSON_spec_end ()
203 : };
204 :
205 4 : if (GNUNET_OK !=
206 4 : GNUNET_JSON_parse (j,
207 : spec,
208 : NULL, NULL))
209 : {
210 0 : GNUNET_break_op (0);
211 0 : dr.hr.http_status = 0;
212 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
213 0 : break;
214 : }
215 4 : if (no_legi)
216 3 : dr.details.accepted.requirement_row = 0;
217 4 : dwh->cb (dwh->cb_cls,
218 : &dr);
219 4 : TALER_EXCHANGE_deposits_get_cancel (dwh);
220 4 : return;
221 : }
222 0 : case MHD_HTTP_BAD_REQUEST:
223 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
224 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
225 : /* This should never happen, either us or the exchange is buggy
226 : (or API version conflict); just pass JSON reply to the application */
227 0 : break;
228 0 : case MHD_HTTP_FORBIDDEN:
229 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
230 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
231 : /* Nothing really to verify, exchange says one of the signatures is
232 : invalid; as we checked them, this should never happen, we
233 : should pass the JSON reply to the application */
234 0 : break;
235 2 : case MHD_HTTP_NOT_FOUND:
236 2 : dr.hr.ec = TALER_JSON_get_error_code (j);
237 2 : dr.hr.hint = TALER_JSON_get_error_hint (j);
238 : /* Exchange does not know about transaction;
239 : we should pass the reply to the application */
240 2 : break;
241 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
242 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
243 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
244 : /* Server had an internal issue; we should retry, but this API
245 : leaves this to the application */
246 0 : break;
247 0 : default:
248 : /* unexpected response code */
249 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
250 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
251 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
252 : "Unexpected response code %u/%d for exchange GET deposits\n",
253 : (unsigned int) response_code,
254 : (int) dr.hr.ec);
255 0 : GNUNET_break_op (0);
256 0 : break;
257 : }
258 2 : dwh->cb (dwh->cb_cls,
259 : &dr);
260 2 : TALER_EXCHANGE_deposits_get_cancel (dwh);
261 : }
262 :
263 :
264 : struct TALER_EXCHANGE_DepositGetHandle *
265 8 : TALER_EXCHANGE_deposits_get (
266 : struct GNUNET_CURL_Context *ctx,
267 : const char *url,
268 : struct TALER_EXCHANGE_Keys *keys,
269 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
270 : const struct TALER_MerchantWireHashP *h_wire,
271 : const struct TALER_PrivateContractHashP *h_contract_terms,
272 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
273 : struct GNUNET_TIME_Relative timeout,
274 : TALER_EXCHANGE_DepositGetCallback cb,
275 : void *cb_cls)
276 : {
277 : struct TALER_MerchantPublicKeyP merchant;
278 : struct TALER_MerchantSignatureP merchant_sig;
279 : struct TALER_EXCHANGE_DepositGetHandle *dwh;
280 : CURL *eh;
281 : char arg_str[(sizeof (struct TALER_CoinSpendPublicKeyP)
282 : + sizeof (struct TALER_MerchantWireHashP)
283 : + sizeof (struct TALER_MerchantPublicKeyP)
284 : + sizeof (struct TALER_PrivateContractHashP)
285 : + sizeof (struct TALER_MerchantSignatureP)) * 2 + 48];
286 8 : unsigned int tms
287 8 : = (unsigned int) timeout.rel_value_us
288 8 : / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
289 :
290 8 : GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
291 : &merchant.eddsa_pub);
292 8 : TALER_merchant_deposit_sign (h_contract_terms,
293 : h_wire,
294 : coin_pub,
295 : merchant_priv,
296 : &merchant_sig);
297 : {
298 : char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
299 : char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2];
300 : char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
301 : char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2];
302 : char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2];
303 : char timeout_str[24];
304 : char *end;
305 :
306 8 : end = GNUNET_STRINGS_data_to_string (h_wire,
307 : sizeof (*h_wire),
308 : whash_str,
309 : sizeof (whash_str));
310 8 : *end = '\0';
311 8 : end = GNUNET_STRINGS_data_to_string (&merchant,
312 : sizeof (merchant),
313 : mpub_str,
314 : sizeof (mpub_str));
315 8 : *end = '\0';
316 8 : end = GNUNET_STRINGS_data_to_string (h_contract_terms,
317 : sizeof (*h_contract_terms),
318 : chash_str,
319 : sizeof (chash_str));
320 8 : *end = '\0';
321 8 : end = GNUNET_STRINGS_data_to_string (coin_pub,
322 : sizeof (*coin_pub),
323 : cpub_str,
324 : sizeof (cpub_str));
325 8 : *end = '\0';
326 8 : end = GNUNET_STRINGS_data_to_string (&merchant_sig,
327 : sizeof (merchant_sig),
328 : msig_str,
329 : sizeof (msig_str));
330 8 : *end = '\0';
331 8 : if (GNUNET_TIME_relative_is_zero (timeout))
332 : {
333 8 : timeout_str[0] = '\0';
334 : }
335 : else
336 : {
337 0 : GNUNET_snprintf (
338 : timeout_str,
339 : sizeof (timeout_str),
340 : "%u",
341 : tms);
342 : }
343 :
344 8 : GNUNET_snprintf (arg_str,
345 : sizeof (arg_str),
346 : "deposits/%s/%s/%s/%s?merchant_sig=%s%s%s",
347 : whash_str,
348 : mpub_str,
349 : chash_str,
350 : cpub_str,
351 : msig_str,
352 : 0 == tms
353 : ? ""
354 : : "&timeout_ms=",
355 : timeout_str);
356 : }
357 :
358 8 : dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle);
359 8 : dwh->cb = cb;
360 8 : dwh->cb_cls = cb_cls;
361 8 : dwh->url = TALER_url_join (url,
362 : arg_str,
363 : NULL);
364 8 : if (NULL == dwh->url)
365 : {
366 0 : GNUNET_free (dwh);
367 0 : return NULL;
368 : }
369 8 : dwh->h_wire = *h_wire;
370 8 : dwh->h_contract_terms = *h_contract_terms;
371 8 : dwh->coin_pub = *coin_pub;
372 8 : eh = TALER_EXCHANGE_curl_easy_get_ (dwh->url);
373 8 : if (NULL == eh)
374 : {
375 0 : GNUNET_break (0);
376 0 : GNUNET_free (dwh->url);
377 0 : GNUNET_free (dwh);
378 0 : return NULL;
379 : }
380 8 : if (0 != tms)
381 : {
382 0 : GNUNET_break (CURLE_OK ==
383 : curl_easy_setopt (eh,
384 : CURLOPT_TIMEOUT_MS,
385 : (long) (tms + 100L)));
386 : }
387 8 : dwh->job = GNUNET_CURL_job_add (ctx,
388 : eh,
389 : &handle_deposit_wtid_finished,
390 : dwh);
391 8 : dwh->keys = TALER_EXCHANGE_keys_incref (keys);
392 8 : return dwh;
393 : }
394 :
395 :
396 : void
397 8 : TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh)
398 : {
399 8 : if (NULL != dwh->job)
400 : {
401 0 : GNUNET_CURL_job_cancel (dwh->job);
402 0 : dwh->job = NULL;
403 : }
404 8 : GNUNET_free (dwh->url);
405 8 : TALER_curl_easy_post_finished (&dwh->ctx);
406 8 : TALER_EXCHANGE_keys_decref (dwh->keys);
407 8 : GNUNET_free (dwh);
408 8 : }
409 :
410 :
411 : /* end of exchange_api_deposits_get.c */
|