Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2021 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 connection to exchange this request handle will use
43 : */
44 : struct TALER_EXCHANGE_Handle *exchange;
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 0 : handle_deposit_wtid_finished (void *cls,
101 : long response_code,
102 : const void *response)
103 : {
104 0 : struct TALER_EXCHANGE_DepositGetHandle *dwh = cls;
105 0 : const json_t *j = response;
106 0 : struct TALER_EXCHANGE_GetDepositResponse dr = {
107 : .hr.reply = j,
108 0 : .hr.http_status = (unsigned int) response_code
109 : };
110 :
111 0 : dwh->job = NULL;
112 0 : switch (response_code)
113 : {
114 0 : case 0:
115 0 : dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
116 0 : break;
117 0 : case MHD_HTTP_OK:
118 : {
119 : struct GNUNET_JSON_Specification spec[] = {
120 0 : GNUNET_JSON_spec_fixed_auto ("wtid",
121 : &dr.details.success.wtid),
122 0 : GNUNET_JSON_spec_timestamp ("execution_time",
123 : &dr.details.success.execution_time),
124 0 : TALER_JSON_spec_amount_any ("coin_contribution",
125 : &dr.details.success.coin_contribution),
126 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
127 : &dr.details.success.exchange_sig),
128 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
129 : &dr.details.success.exchange_pub),
130 0 : GNUNET_JSON_spec_end ()
131 : };
132 : const struct TALER_EXCHANGE_Keys *key_state;
133 :
134 0 : key_state = TALER_EXCHANGE_get_keys (dwh->exchange);
135 0 : GNUNET_assert (NULL != key_state);
136 0 : if (GNUNET_OK !=
137 0 : GNUNET_JSON_parse (j,
138 : spec,
139 : NULL, NULL))
140 : {
141 0 : GNUNET_break_op (0);
142 0 : dr.hr.http_status = 0;
143 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
144 0 : break;
145 : }
146 0 : if (GNUNET_OK !=
147 0 : TALER_EXCHANGE_test_signing_key (key_state,
148 : &dr.details.success.exchange_pub))
149 : {
150 0 : GNUNET_break_op (0);
151 0 : dr.hr.http_status = 0;
152 0 : dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
153 0 : break;
154 : }
155 0 : if (GNUNET_OK !=
156 0 : TALER_exchange_online_confirm_wire_verify (
157 0 : &dwh->h_wire,
158 0 : &dwh->h_contract_terms,
159 : &dr.details.success.wtid,
160 0 : &dwh->coin_pub,
161 : dr.details.success.execution_time,
162 : &dr.details.success.coin_contribution,
163 : &dr.details.success.exchange_pub,
164 : &dr.details.success.exchange_sig))
165 : {
166 0 : GNUNET_break_op (0);
167 0 : dr.hr.http_status = 0;
168 0 : dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
169 0 : break;
170 : }
171 0 : dwh->cb (dwh->cb_cls,
172 : &dr);
173 0 : TALER_EXCHANGE_deposits_get_cancel (dwh);
174 0 : return;
175 : }
176 0 : case MHD_HTTP_ACCEPTED:
177 : {
178 : /* Transaction known, but not executed yet */
179 0 : bool no_legi = false;
180 : struct GNUNET_JSON_Specification spec[] = {
181 0 : GNUNET_JSON_spec_timestamp ("execution_time",
182 : &dr.details.accepted.execution_time),
183 0 : GNUNET_JSON_spec_mark_optional (
184 : GNUNET_JSON_spec_uint64 ("requirement_row",
185 : &dr.details.accepted.requirement_row),
186 : &no_legi),
187 0 : GNUNET_JSON_spec_bool ("kyc_ok",
188 : &dr.details.accepted.kyc_ok),
189 0 : GNUNET_JSON_spec_end ()
190 : };
191 :
192 0 : if (GNUNET_OK !=
193 0 : GNUNET_JSON_parse (j,
194 : spec,
195 : NULL, NULL))
196 : {
197 0 : GNUNET_break_op (0);
198 0 : dr.hr.http_status = 0;
199 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
200 0 : break;
201 : }
202 0 : if (no_legi)
203 0 : dr.details.accepted.requirement_row = 0;
204 0 : dwh->cb (dwh->cb_cls,
205 : &dr);
206 0 : TALER_EXCHANGE_deposits_get_cancel (dwh);
207 0 : return;
208 : }
209 0 : case MHD_HTTP_BAD_REQUEST:
210 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
211 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
212 : /* This should never happen, either us or the exchange is buggy
213 : (or API version conflict); just pass JSON reply to the application */
214 0 : break;
215 0 : case MHD_HTTP_FORBIDDEN:
216 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
217 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
218 : /* Nothing really to verify, exchange says one of the signatures is
219 : invalid; as we checked them, this should never happen, we
220 : should pass the JSON reply to the application */
221 0 : break;
222 0 : case MHD_HTTP_NOT_FOUND:
223 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
224 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
225 : /* Exchange does not know about transaction;
226 : we should pass the reply to the application */
227 0 : break;
228 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
229 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
230 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
231 : /* Server had an internal issue; we should retry, but this API
232 : leaves this to the application */
233 0 : break;
234 0 : default:
235 : /* unexpected response code */
236 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
237 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
238 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
239 : "Unexpected response code %u/%d for exchange GET deposits\n",
240 : (unsigned int) response_code,
241 : (int) dr.hr.ec);
242 0 : GNUNET_break_op (0);
243 0 : break;
244 : }
245 0 : dwh->cb (dwh->cb_cls,
246 : &dr);
247 0 : TALER_EXCHANGE_deposits_get_cancel (dwh);
248 : }
249 :
250 :
251 : struct TALER_EXCHANGE_DepositGetHandle *
252 0 : TALER_EXCHANGE_deposits_get (
253 : struct TALER_EXCHANGE_Handle *exchange,
254 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
255 : const struct TALER_MerchantWireHashP *h_wire,
256 : const struct TALER_PrivateContractHashP *h_contract_terms,
257 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
258 : TALER_EXCHANGE_DepositGetCallback cb,
259 : void *cb_cls)
260 : {
261 : struct TALER_MerchantPublicKeyP merchant;
262 : struct TALER_MerchantSignatureP merchant_sig;
263 : struct TALER_EXCHANGE_DepositGetHandle *dwh;
264 : struct GNUNET_CURL_Context *ctx;
265 : CURL *eh;
266 : char arg_str[(sizeof (struct TALER_CoinSpendPublicKeyP)
267 : + sizeof (struct TALER_MerchantWireHashP)
268 : + sizeof (struct TALER_MerchantPublicKeyP)
269 : + sizeof (struct TALER_PrivateContractHashP)
270 : + sizeof (struct TALER_MerchantSignatureP)) * 2 + 48];
271 :
272 0 : if (GNUNET_YES !=
273 0 : TEAH_handle_is_ready (exchange))
274 : {
275 0 : GNUNET_break (0);
276 0 : return NULL;
277 : }
278 0 : GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
279 : &merchant.eddsa_pub);
280 0 : TALER_merchant_deposit_sign (h_contract_terms,
281 : h_wire,
282 : coin_pub,
283 : merchant_priv,
284 : &merchant_sig);
285 : {
286 : char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
287 : char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2];
288 : char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
289 : char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2];
290 : char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2];
291 : char *end;
292 :
293 0 : end = GNUNET_STRINGS_data_to_string (h_wire,
294 : sizeof (*h_wire),
295 : whash_str,
296 : sizeof (whash_str));
297 0 : *end = '\0';
298 0 : end = GNUNET_STRINGS_data_to_string (&merchant,
299 : sizeof (merchant),
300 : mpub_str,
301 : sizeof (mpub_str));
302 0 : *end = '\0';
303 0 : end = GNUNET_STRINGS_data_to_string (h_contract_terms,
304 : sizeof (*h_contract_terms),
305 : chash_str,
306 : sizeof (chash_str));
307 0 : *end = '\0';
308 0 : end = GNUNET_STRINGS_data_to_string (coin_pub,
309 : sizeof (*coin_pub),
310 : cpub_str,
311 : sizeof (cpub_str));
312 0 : *end = '\0';
313 0 : end = GNUNET_STRINGS_data_to_string (&merchant_sig,
314 : sizeof (merchant_sig),
315 : msig_str,
316 : sizeof (msig_str));
317 0 : *end = '\0';
318 :
319 0 : GNUNET_snprintf (arg_str,
320 : sizeof (arg_str),
321 : "/deposits/%s/%s/%s/%s?merchant_sig=%s",
322 : whash_str,
323 : mpub_str,
324 : chash_str,
325 : cpub_str,
326 : msig_str);
327 : }
328 :
329 0 : dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle);
330 0 : dwh->exchange = exchange;
331 0 : dwh->cb = cb;
332 0 : dwh->cb_cls = cb_cls;
333 0 : dwh->url = TEAH_path_to_url (exchange,
334 : arg_str);
335 0 : if (NULL == dwh->url)
336 : {
337 0 : GNUNET_free (dwh);
338 0 : return NULL;
339 : }
340 0 : dwh->h_wire = *h_wire;
341 0 : dwh->h_contract_terms = *h_contract_terms;
342 0 : dwh->coin_pub = *coin_pub;
343 :
344 0 : eh = TALER_EXCHANGE_curl_easy_get_ (dwh->url);
345 0 : if (NULL == eh)
346 : {
347 0 : GNUNET_break (0);
348 0 : GNUNET_free (dwh->url);
349 0 : GNUNET_free (dwh);
350 0 : return NULL;
351 : }
352 0 : ctx = TEAH_handle_to_context (exchange);
353 0 : dwh->job = GNUNET_CURL_job_add (ctx,
354 : eh,
355 : &handle_deposit_wtid_finished,
356 : dwh);
357 0 : return dwh;
358 : }
359 :
360 :
361 : void
362 0 : TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh)
363 : {
364 0 : if (NULL != dwh->job)
365 : {
366 0 : GNUNET_CURL_job_cancel (dwh->job);
367 0 : dwh->job = NULL;
368 : }
369 0 : GNUNET_free (dwh->url);
370 0 : TALER_curl_easy_post_finished (&dwh->ctx);
371 0 : GNUNET_free (dwh);
372 0 : }
373 :
374 :
375 : /* end of exchange_api_deposits_get.c */
|