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_deposit.c
19 : * @brief Implementation of the /deposit request of the exchange's HTTP API
20 : * @author Sree Harsha Totakura <sreeharsha@totakura.in>
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <jansson.h>
25 : #include <microhttpd.h> /* just for HTTP status codes */
26 : #include <gnunet/gnunet_util_lib.h>
27 : #include <gnunet/gnunet_json_lib.h>
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler_json_lib.h"
30 : #include "taler_auditor_service.h"
31 : #include "taler_exchange_service.h"
32 : #include "exchange_api_common.h"
33 : #include "exchange_api_handle.h"
34 : #include "taler_signatures.h"
35 : #include "exchange_api_curl_defaults.h"
36 :
37 :
38 : /**
39 : * 1:#AUDITOR_CHANCE is the probability that we report deposits
40 : * to the auditor.
41 : *
42 : * 20==5% of going to auditor. This is possibly still too high, but set
43 : * deliberately this high for testing
44 : */
45 : #define AUDITOR_CHANCE 20
46 :
47 : /**
48 : * @brief A Deposit Handle
49 : */
50 : struct TALER_EXCHANGE_DepositHandle
51 : {
52 :
53 : /**
54 : * The connection to exchange this request handle will use
55 : */
56 : struct TALER_EXCHANGE_Handle *exchange;
57 :
58 : /**
59 : * The url for this request.
60 : */
61 : char *url;
62 :
63 : /**
64 : * Context for #TEH_curl_easy_post(). Keeps the data that must
65 : * persist for Curl to make the upload.
66 : */
67 : struct TALER_CURL_PostContext ctx;
68 :
69 : /**
70 : * Handle for the request.
71 : */
72 : struct GNUNET_CURL_Job *job;
73 :
74 : /**
75 : * Function to call with the result.
76 : */
77 : TALER_EXCHANGE_DepositResultCallback cb;
78 :
79 : /**
80 : * Closure for @a cb.
81 : */
82 : void *cb_cls;
83 :
84 : /**
85 : * Details about the contract.
86 : */
87 : struct TALER_EXCHANGE_DepositContractDetail dcd;
88 :
89 : /**
90 : * Details about the coin.
91 : */
92 : struct TALER_EXCHANGE_CoinDepositDetail cdd;
93 :
94 : /**
95 : * Hash of the merchant's wire details.
96 : */
97 : struct TALER_MerchantWireHashP h_wire;
98 :
99 : /**
100 : * Hash over the extensions, or all zero.
101 : */
102 : struct TALER_ExtensionContractHashP h_extensions;
103 :
104 : /**
105 : * Time when this confirmation was generated / when the exchange received
106 : * the deposit request.
107 : */
108 : struct GNUNET_TIME_Timestamp exchange_timestamp;
109 :
110 : /**
111 : * Exchange signature, set for #auditor_cb.
112 : */
113 : struct TALER_ExchangeSignatureP exchange_sig;
114 :
115 : /**
116 : * Exchange signing public key, set for #auditor_cb.
117 : */
118 : struct TALER_ExchangePublicKeyP exchange_pub;
119 :
120 : /**
121 : * Chance that we will inform the auditor about the deposit
122 : * is 1:n, where the value of this field is "n".
123 : */
124 : unsigned int auditor_chance;
125 :
126 : };
127 :
128 :
129 : /**
130 : * Function called for each auditor to give us a chance to possibly
131 : * launch a deposit confirmation interaction.
132 : *
133 : * @param cls closure
134 : * @param ah handle to the auditor
135 : * @param auditor_pub public key of the auditor
136 : * @return NULL if no deposit confirmation interaction was launched
137 : */
138 : static struct TEAH_AuditorInteractionEntry *
139 0 : auditor_cb (void *cls,
140 : struct TALER_AUDITOR_Handle *ah,
141 : const struct TALER_AuditorPublicKeyP *auditor_pub)
142 : {
143 0 : struct TALER_EXCHANGE_DepositHandle *dh = cls;
144 : const struct TALER_EXCHANGE_Keys *key_state;
145 : const struct TALER_EXCHANGE_SigningPublicKey *spk;
146 : struct TEAH_AuditorInteractionEntry *aie;
147 : struct TALER_Amount amount_without_fee;
148 : const struct TALER_EXCHANGE_DenomPublicKey *dki;
149 :
150 0 : if (0 !=
151 0 : GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
152 : dh->auditor_chance))
153 : {
154 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
155 : "Not providing deposit confirmation to auditor\n");
156 0 : return NULL;
157 : }
158 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
159 : "Will provide deposit confirmation to auditor `%s'\n",
160 : TALER_B2S (auditor_pub));
161 0 : key_state = TALER_EXCHANGE_get_keys (dh->exchange);
162 0 : dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
163 0 : &dh->cdd.h_denom_pub);
164 0 : GNUNET_assert (NULL != dki);
165 0 : spk = TALER_EXCHANGE_get_signing_key_info (key_state,
166 0 : &dh->exchange_pub);
167 0 : if (NULL == spk)
168 : {
169 0 : GNUNET_break_op (0);
170 0 : return NULL;
171 : }
172 0 : GNUNET_assert (0 <=
173 : TALER_amount_subtract (&amount_without_fee,
174 : &dh->cdd.amount,
175 : &dki->fees.deposit));
176 0 : aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
177 0 : aie->dch = TALER_AUDITOR_deposit_confirmation (
178 : ah,
179 0 : &dh->h_wire,
180 0 : &dh->h_extensions,
181 0 : &dh->dcd.h_contract_terms,
182 : dh->exchange_timestamp,
183 : dh->dcd.wire_deadline,
184 : dh->dcd.refund_deadline,
185 : &amount_without_fee,
186 0 : &dh->cdd.coin_pub,
187 0 : &dh->dcd.merchant_pub,
188 0 : &dh->exchange_pub,
189 0 : &dh->exchange_sig,
190 : &key_state->master_pub,
191 : spk->valid_from,
192 : spk->valid_until,
193 : spk->valid_legal,
194 : &spk->master_sig,
195 : &TEAH_acc_confirmation_cb,
196 : aie);
197 0 : return aie;
198 : }
199 :
200 :
201 : /**
202 : * Function called when we're done processing the
203 : * HTTP /deposit request.
204 : *
205 : * @param cls the `struct TALER_EXCHANGE_DepositHandle`
206 : * @param response_code HTTP response code, 0 on error
207 : * @param response parsed JSON result, NULL on error
208 : */
209 : static void
210 0 : handle_deposit_finished (void *cls,
211 : long response_code,
212 : const void *response)
213 : {
214 0 : struct TALER_EXCHANGE_DepositHandle *dh = cls;
215 0 : const json_t *j = response;
216 0 : struct TALER_EXCHANGE_DepositResult dr = {
217 : .hr.reply = j,
218 0 : .hr.http_status = (unsigned int) response_code
219 : };
220 : const struct TALER_EXCHANGE_Keys *keys;
221 :
222 0 : dh->job = NULL;
223 0 : keys = TALER_EXCHANGE_get_keys (dh->exchange);
224 0 : switch (response_code)
225 : {
226 0 : case 0:
227 0 : dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
228 0 : break;
229 0 : case MHD_HTTP_OK:
230 : {
231 : const struct TALER_EXCHANGE_Keys *key_state;
232 : struct GNUNET_JSON_Specification spec[] = {
233 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
234 : &dh->exchange_sig),
235 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
236 : &dh->exchange_pub),
237 0 : GNUNET_JSON_spec_mark_optional (
238 : GNUNET_JSON_spec_string ("transaction_base_url",
239 : &dr.details.success.transaction_base_url),
240 : NULL),
241 0 : GNUNET_JSON_spec_timestamp ("exchange_timestamp",
242 : &dh->exchange_timestamp),
243 0 : GNUNET_JSON_spec_end ()
244 : };
245 : struct TALER_Amount amount_without_fee;
246 : const struct TALER_EXCHANGE_DenomPublicKey *dki;
247 :
248 0 : if (GNUNET_OK !=
249 0 : GNUNET_JSON_parse (j,
250 : spec,
251 : NULL, NULL))
252 : {
253 0 : GNUNET_break_op (0);
254 0 : dr.hr.http_status = 0;
255 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
256 0 : break;
257 : }
258 0 : key_state = TALER_EXCHANGE_get_keys (dh->exchange);
259 0 : dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
260 0 : &dh->cdd.h_denom_pub);
261 0 : GNUNET_assert (NULL != dki);
262 0 : if (GNUNET_OK !=
263 0 : TALER_EXCHANGE_test_signing_key (key_state,
264 0 : &dh->exchange_pub))
265 : {
266 0 : GNUNET_break_op (0);
267 0 : dr.hr.http_status = 0;
268 0 : dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
269 0 : break;
270 : }
271 0 : GNUNET_assert (0 <=
272 : TALER_amount_subtract (&amount_without_fee,
273 : &dh->cdd.amount,
274 : &dki->fees.deposit));
275 :
276 0 : if (GNUNET_OK !=
277 0 : TALER_exchange_online_deposit_confirmation_verify (
278 0 : &dh->dcd.h_contract_terms,
279 0 : &dh->h_wire,
280 0 : &dh->h_extensions,
281 : dh->exchange_timestamp,
282 : dh->dcd.wire_deadline,
283 : dh->dcd.refund_deadline,
284 : &amount_without_fee,
285 0 : &dh->cdd.coin_pub,
286 0 : &dh->dcd.merchant_pub,
287 0 : &dh->exchange_pub,
288 0 : &dh->exchange_sig))
289 : {
290 0 : GNUNET_break_op (0);
291 0 : dr.hr.http_status = 0;
292 0 : dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
293 0 : break;
294 : }
295 :
296 0 : TEAH_get_auditors_for_dc (dh->exchange,
297 : &auditor_cb,
298 : dh);
299 : }
300 0 : dr.details.success.exchange_sig = &dh->exchange_sig;
301 0 : dr.details.success.exchange_pub = &dh->exchange_pub;
302 0 : dr.details.success.deposit_timestamp = dh->exchange_timestamp;
303 0 : break;
304 0 : case MHD_HTTP_BAD_REQUEST:
305 : /* This should never happen, either us or the exchange is buggy
306 : (or API version conflict); just pass JSON reply to the application */
307 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
308 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
309 0 : break;
310 0 : case MHD_HTTP_FORBIDDEN:
311 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
312 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
313 : /* Nothing really to verify, exchange says one of the signatures is
314 : invalid; as we checked them, this should never happen, we
315 : should pass the JSON reply to the application */
316 0 : break;
317 0 : case MHD_HTTP_NOT_FOUND:
318 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
319 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
320 : /* Nothing really to verify, this should never
321 : happen, we should pass the JSON reply to the application */
322 0 : break;
323 0 : case MHD_HTTP_CONFLICT:
324 : {
325 : const struct TALER_EXCHANGE_Keys *key_state;
326 : const struct TALER_EXCHANGE_DenomPublicKey *dki;
327 :
328 0 : key_state = TALER_EXCHANGE_get_keys (dh->exchange);
329 0 : dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
330 0 : &dh->cdd.h_denom_pub);
331 0 : GNUNET_assert (NULL != dki);
332 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
333 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
334 0 : if (GNUNET_OK !=
335 0 : TALER_EXCHANGE_check_coin_conflict_ (
336 : keys,
337 : j,
338 : dki,
339 0 : &dh->cdd.coin_pub,
340 0 : &dh->cdd.coin_sig,
341 0 : &dh->cdd.amount))
342 : {
343 0 : GNUNET_break_op (0);
344 0 : dr.hr.http_status = 0;
345 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
346 0 : break;
347 : }
348 : }
349 0 : break;
350 0 : case MHD_HTTP_GONE:
351 : /* could happen if denomination was revoked */
352 : /* Note: one might want to check /keys for revocation
353 : signature here, alas tricky in case our /keys
354 : is outdated => left to clients */
355 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
356 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
357 0 : break;
358 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
359 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
360 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
361 : /* Server had an internal issue; we should retry, but this API
362 : leaves this to the application */
363 0 : break;
364 0 : default:
365 : /* unexpected response code */
366 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
367 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
368 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
369 : "Unexpected response code %u/%d for exchange deposit\n",
370 : (unsigned int) response_code,
371 : dr.hr.ec);
372 0 : GNUNET_break_op (0);
373 0 : break;
374 : }
375 0 : dh->cb (dh->cb_cls,
376 : &dr);
377 0 : TALER_EXCHANGE_deposit_cancel (dh);
378 0 : }
379 :
380 :
381 : struct TALER_EXCHANGE_DepositHandle *
382 0 : TALER_EXCHANGE_deposit (
383 : struct TALER_EXCHANGE_Handle *exchange,
384 : const struct TALER_EXCHANGE_DepositContractDetail *dcd,
385 : const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
386 : TALER_EXCHANGE_DepositResultCallback cb,
387 : void *cb_cls,
388 : enum TALER_ErrorCode *ec)
389 : {
390 : const struct TALER_EXCHANGE_Keys *key_state;
391 : struct TALER_EXCHANGE_DepositHandle *dh;
392 : struct GNUNET_CURL_Context *ctx;
393 : json_t *deposit_obj;
394 : CURL *eh;
395 : const struct TALER_EXCHANGE_DenomPublicKey *dki;
396 : struct TALER_Amount amount_without_fee;
397 : char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
398 :
399 0 : GNUNET_assert (GNUNET_YES ==
400 : TEAH_handle_is_ready (exchange));
401 0 : if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline,
402 : >,
403 : dcd->wire_deadline))
404 : {
405 0 : GNUNET_break_op (0);
406 0 : *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE;
407 0 : return NULL;
408 : }
409 : {
410 : char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
411 : char *end;
412 :
413 0 : end = GNUNET_STRINGS_data_to_string (
414 0 : &cdd->coin_pub,
415 : sizeof (struct TALER_CoinSpendPublicKeyP),
416 : pub_str,
417 : sizeof (pub_str));
418 0 : *end = '\0';
419 0 : GNUNET_snprintf (arg_str,
420 : sizeof (arg_str),
421 : "/coins/%s/deposit",
422 : pub_str);
423 : }
424 0 : key_state = TALER_EXCHANGE_get_keys (exchange);
425 0 : dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
426 : &cdd->h_denom_pub);
427 0 : if (NULL == dki)
428 : {
429 0 : *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
430 0 : GNUNET_break_op (0);
431 0 : return NULL;
432 : }
433 0 : if (0 >
434 0 : TALER_amount_subtract (&amount_without_fee,
435 : &cdd->amount,
436 : &dki->fees.deposit))
437 : {
438 0 : *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
439 0 : GNUNET_break_op (0);
440 0 : return NULL;
441 : }
442 0 : dh = GNUNET_new (struct TALER_EXCHANGE_DepositHandle);
443 0 : dh->auditor_chance = AUDITOR_CHANCE;
444 0 : dh->exchange = exchange;
445 0 : dh->cb = cb;
446 0 : dh->cb_cls = cb_cls;
447 0 : dh->cdd = *cdd;
448 0 : dh->dcd = *dcd;
449 0 : if (NULL != dcd->extension_details)
450 0 : TALER_deposit_extension_hash (dcd->extension_details,
451 : &dh->h_extensions);
452 0 : TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
453 : &dcd->wire_salt,
454 : &dh->h_wire);
455 0 : if (GNUNET_OK !=
456 0 : TALER_EXCHANGE_verify_deposit_signature_ (dcd,
457 0 : &dh->h_extensions,
458 0 : &dh->h_wire,
459 : cdd,
460 : dki))
461 : {
462 0 : *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID;
463 0 : GNUNET_break_op (0);
464 0 : GNUNET_free (dh);
465 0 : return NULL;
466 : }
467 0 : dh->url = TEAH_path_to_url (exchange,
468 : arg_str);
469 0 : if (NULL == dh->url)
470 : {
471 0 : GNUNET_break (0);
472 0 : *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
473 0 : GNUNET_free (dh->url);
474 0 : GNUNET_free (dh);
475 0 : return NULL;
476 : }
477 :
478 0 : deposit_obj = GNUNET_JSON_PACK (
479 : TALER_JSON_pack_amount ("contribution",
480 : &cdd->amount),
481 : GNUNET_JSON_pack_string ("merchant_payto_uri",
482 : dcd->merchant_payto_uri),
483 : GNUNET_JSON_pack_data_auto ("wire_salt",
484 : &dcd->wire_salt),
485 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
486 : &dcd->h_contract_terms),
487 : GNUNET_JSON_pack_allow_null (
488 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
489 : &cdd->h_age_commitment)),
490 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
491 : &cdd->h_denom_pub),
492 : TALER_JSON_pack_denom_sig ("ub_sig",
493 : &cdd->denom_sig),
494 : GNUNET_JSON_pack_timestamp ("timestamp",
495 : dcd->timestamp),
496 : GNUNET_JSON_pack_data_auto ("merchant_pub",
497 : &dcd->merchant_pub),
498 : GNUNET_JSON_pack_allow_null (
499 : GNUNET_JSON_pack_timestamp ("refund_deadline",
500 : dcd->refund_deadline)),
501 : GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
502 : dcd->wire_deadline),
503 : GNUNET_JSON_pack_data_auto ("coin_sig",
504 : &cdd->coin_sig));
505 0 : GNUNET_assert (NULL != deposit_obj);
506 0 : eh = TALER_EXCHANGE_curl_easy_get_ (dh->url);
507 0 : if ( (NULL == eh) ||
508 : (GNUNET_OK !=
509 0 : TALER_curl_easy_post (&dh->ctx,
510 : eh,
511 : deposit_obj)) )
512 : {
513 0 : *ec = TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
514 0 : GNUNET_break (0);
515 0 : if (NULL != eh)
516 0 : curl_easy_cleanup (eh);
517 0 : json_decref (deposit_obj);
518 0 : GNUNET_free (dh->url);
519 0 : GNUNET_free (dh);
520 0 : return NULL;
521 : }
522 0 : json_decref (deposit_obj);
523 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 : "URL for deposit: `%s'\n",
525 : dh->url);
526 0 : ctx = TEAH_handle_to_context (exchange);
527 0 : dh->job = GNUNET_CURL_job_add2 (ctx,
528 : eh,
529 0 : dh->ctx.headers,
530 : &handle_deposit_finished,
531 : dh);
532 0 : return dh;
533 : }
534 :
535 :
536 : void
537 0 : TALER_EXCHANGE_deposit_force_dc (struct TALER_EXCHANGE_DepositHandle *deposit)
538 : {
539 0 : deposit->auditor_chance = 1;
540 0 : }
541 :
542 :
543 : void
544 0 : TALER_EXCHANGE_deposit_cancel (struct TALER_EXCHANGE_DepositHandle *deposit)
545 : {
546 0 : if (NULL != deposit->job)
547 : {
548 0 : GNUNET_CURL_job_cancel (deposit->job);
549 0 : deposit->job = NULL;
550 : }
551 0 : GNUNET_free (deposit->url);
552 0 : TALER_curl_easy_post_finished (&deposit->ctx);
553 0 : GNUNET_free (deposit);
554 0 : }
555 :
556 :
557 : /* end of exchange_api_deposit.c */
|