Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2026 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_post-batch-deposit.c
19 : * @brief Implementation of the /batch-deposit request of the exchange's HTTP API
20 : * @author Sree Harsha Totakura <sreeharsha@totakura.in>
21 : * @author Christian Grothoff
22 : */
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/taler_json_lib.h"
29 : #include "taler/taler_auditor_service.h"
30 : #include "exchange_api_common.h"
31 : #include "exchange_api_handle.h"
32 : #include "taler/taler_signatures.h"
33 : #include "exchange_api_curl_defaults.h"
34 :
35 :
36 : /**
37 : * 1:#AUDITOR_CHANCE is the probability that we report deposits
38 : * to the auditor.
39 : *
40 : * 20==5% of going to auditor. This is possibly still too high, but set
41 : * deliberately this high for testing
42 : */
43 : #define AUDITOR_CHANCE 20
44 :
45 :
46 : /**
47 : * Entry in list of ongoing interactions with an auditor.
48 : */
49 : struct TEAH_AuditorInteractionEntry
50 : {
51 : /**
52 : * DLL entry.
53 : */
54 : struct TEAH_AuditorInteractionEntry *next;
55 :
56 : /**
57 : * DLL entry.
58 : */
59 : struct TEAH_AuditorInteractionEntry *prev;
60 :
61 : /**
62 : * URL of our auditor. For logging.
63 : */
64 : const char *auditor_url;
65 :
66 : /**
67 : * Interaction state.
68 : */
69 : struct TALER_AUDITOR_DepositConfirmationHandle *dch;
70 :
71 : /**
72 : * Batch deposit this is for.
73 : */
74 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh;
75 : };
76 :
77 :
78 : /**
79 : * @brief A Batch Deposit Handle
80 : */
81 : struct TALER_EXCHANGE_PostBatchDepositHandle
82 : {
83 :
84 : /**
85 : * The keys of the exchange.
86 : */
87 : struct TALER_EXCHANGE_Keys *keys;
88 :
89 : /**
90 : * Context for our curl request(s).
91 : */
92 : struct GNUNET_CURL_Context *ctx;
93 :
94 : /**
95 : * The base URL of the exchange (used to build the endpoint URL in _start).
96 : */
97 : char *base_url;
98 :
99 : /**
100 : * The full endpoint URL for this request (built in _start).
101 : */
102 : char *url;
103 :
104 : /**
105 : * Context for #TEH_curl_easy_post(). Keeps the data that must
106 : * persist for Curl to make the upload.
107 : */
108 : struct TALER_CURL_PostContext post_ctx;
109 :
110 : /**
111 : * Handle for the request.
112 : */
113 : struct GNUNET_CURL_Job *job;
114 :
115 : /**
116 : * Function to call with the result.
117 : */
118 : TALER_EXCHANGE_PostBatchDepositCallback cb;
119 :
120 : /**
121 : * Closure for @a cb.
122 : */
123 : void *cb_cls;
124 :
125 : /**
126 : * Details about the contract.
127 : */
128 : struct TALER_EXCHANGE_DepositContractDetail dcd;
129 :
130 : /**
131 : * Array with details about the coins.
132 : */
133 : struct TALER_EXCHANGE_CoinDepositDetail *cdds;
134 :
135 : /**
136 : * Hash of the merchant's wire details.
137 : */
138 : struct TALER_MerchantWireHashP h_wire;
139 :
140 : /**
141 : * Hash over the extensions, or all zero.
142 : */
143 : struct TALER_ExtensionPolicyHashP h_policy;
144 :
145 : /**
146 : * Time when this confirmation was generated / when the exchange received
147 : * the deposit request.
148 : */
149 : struct GNUNET_TIME_Timestamp exchange_timestamp;
150 :
151 : /**
152 : * Exchange signature, set for #auditor_cb.
153 : */
154 : struct TALER_ExchangeSignatureP exchange_sig;
155 :
156 : /**
157 : * Head of DLL of interactions with this auditor.
158 : */
159 : struct TEAH_AuditorInteractionEntry *ai_head;
160 :
161 : /**
162 : * Tail of DLL of interactions with this auditor.
163 : */
164 : struct TEAH_AuditorInteractionEntry *ai_tail;
165 :
166 : /**
167 : * Result to return to the application once @e ai_head is empty.
168 : */
169 : struct TALER_EXCHANGE_PostBatchDepositResponse dr;
170 :
171 : /**
172 : * Exchange signing public key, set for #auditor_cb.
173 : */
174 : struct TALER_ExchangePublicKeyP exchange_pub;
175 :
176 : /**
177 : * Total amount deposited without fees as calculated by us.
178 : */
179 : struct TALER_Amount total_without_fee;
180 :
181 : /**
182 : * Response object to free at the end.
183 : */
184 : json_t *response;
185 :
186 : /**
187 : * The JSON body to POST (built during _create, used during _start).
188 : */
189 : json_t *deposit_obj;
190 :
191 : /**
192 : * Chance that we will inform the auditor about the deposit
193 : * is 1:n, where the value of this field is "n".
194 : */
195 : unsigned int auditor_chance;
196 :
197 : /**
198 : * Length of the @e cdds array.
199 : */
200 : unsigned int num_cdds;
201 :
202 : /**
203 : * Whether to verify the merchant signature on the contract terms.
204 : */
205 : bool verify_merchant_sig;
206 :
207 : };
208 :
209 :
210 : /**
211 : * Finish batch deposit operation by calling the callback.
212 : *
213 : * @param[in] dh handle to finished batch deposit operation
214 : */
215 : static void
216 98 : finish_dh (struct TALER_EXCHANGE_PostBatchDepositHandle *dh)
217 : {
218 98 : dh->cb (dh->cb_cls,
219 98 : &dh->dr);
220 98 : TALER_EXCHANGE_post_batch_deposit_cancel (dh);
221 98 : }
222 :
223 :
224 : /**
225 : * Function called with the result from our call to the
226 : * auditor's /deposit-confirmation handler.
227 : *
228 : * @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
229 : * @param dcr response
230 : */
231 : static void
232 0 : acc_confirmation_cb (
233 : void *cls,
234 : const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
235 : {
236 0 : struct TEAH_AuditorInteractionEntry *aie = cls;
237 0 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh = aie->dh;
238 :
239 0 : if (MHD_HTTP_OK != dcr->hr.http_status)
240 : {
241 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
242 : "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n",
243 : aie->auditor_url,
244 : dcr->hr.http_status,
245 : dcr->hr.ec);
246 : }
247 0 : GNUNET_CONTAINER_DLL_remove (dh->ai_head,
248 : dh->ai_tail,
249 : aie);
250 0 : GNUNET_free (aie);
251 0 : if (NULL == dh->ai_head)
252 0 : finish_dh (dh);
253 0 : }
254 :
255 :
256 : /**
257 : * Function called for each auditor to give us a chance to possibly
258 : * launch a deposit confirmation interaction.
259 : *
260 : * @param cls closure
261 : * @param auditor_url base URL of the auditor
262 : * @param auditor_pub public key of the auditor
263 : */
264 : static void
265 32 : auditor_cb (void *cls,
266 : const char *auditor_url,
267 : const struct TALER_AuditorPublicKeyP *auditor_pub)
268 32 : {
269 32 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls;
270 : const struct TALER_EXCHANGE_SigningPublicKey *spk;
271 : struct TEAH_AuditorInteractionEntry *aie;
272 32 : const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (
273 : dh->num_cdds)];
274 32 : const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL (
275 : dh->num_cdds)];
276 :
277 64 : for (unsigned int i = 0; i<dh->num_cdds; i++)
278 : {
279 32 : const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i];
280 :
281 32 : csigs[i] = &cdd->coin_sig;
282 32 : cpubs[i] = &cdd->coin_pub;
283 : }
284 :
285 32 : if (0 !=
286 32 : GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
287 : dh->auditor_chance))
288 : {
289 32 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
290 : "Not providing deposit confirmation to auditor\n");
291 32 : return;
292 : }
293 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
294 : "Will provide deposit confirmation to auditor `%s'\n",
295 : TALER_B2S (auditor_pub));
296 0 : spk = TALER_EXCHANGE_get_signing_key_info (dh->keys,
297 0 : &dh->exchange_pub);
298 0 : if (NULL == spk)
299 : {
300 0 : GNUNET_break_op (0);
301 0 : return;
302 : }
303 0 : aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
304 0 : aie->dh = dh;
305 0 : aie->auditor_url = auditor_url;
306 0 : aie->dch = TALER_AUDITOR_deposit_confirmation (
307 : dh->ctx,
308 : auditor_url,
309 0 : &dh->h_wire,
310 0 : &dh->h_policy,
311 0 : &dh->dcd.h_contract_terms,
312 : dh->exchange_timestamp,
313 : dh->dcd.wire_deadline,
314 : dh->dcd.refund_deadline,
315 0 : &dh->total_without_fee,
316 : dh->num_cdds,
317 : cpubs,
318 : csigs,
319 0 : &dh->dcd.merchant_pub,
320 0 : &dh->exchange_pub,
321 0 : &dh->exchange_sig,
322 0 : &dh->keys->master_pub,
323 : spk->valid_from,
324 : spk->valid_until,
325 : spk->valid_legal,
326 : &spk->master_sig,
327 : &acc_confirmation_cb,
328 : aie);
329 0 : if (NULL == aie->dch)
330 : {
331 0 : GNUNET_break (0);
332 0 : GNUNET_free (aie);
333 0 : return;
334 : }
335 0 : GNUNET_CONTAINER_DLL_insert (dh->ai_head,
336 : dh->ai_tail,
337 : aie);
338 : }
339 :
340 :
341 : /**
342 : * Function called when we're done processing the
343 : * HTTP /batch-deposit request.
344 : *
345 : * @param cls the `struct TALER_EXCHANGE_PostBatchDepositHandle`
346 : * @param response_code HTTP response code, 0 on error
347 : * @param response parsed JSON result, NULL on error
348 : */
349 : static void
350 98 : handle_deposit_finished (void *cls,
351 : long response_code,
352 : const void *response)
353 : {
354 98 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls;
355 98 : const json_t *j = response;
356 98 : struct TALER_EXCHANGE_PostBatchDepositResponse *dr = &dh->dr;
357 :
358 98 : dh->job = NULL;
359 98 : dh->response = json_incref ((json_t*) j);
360 98 : dr->hr.reply = dh->response;
361 98 : dr->hr.http_status = (unsigned int) response_code;
362 98 : switch (response_code)
363 : {
364 0 : case 0:
365 0 : dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
366 0 : break;
367 84 : case MHD_HTTP_OK:
368 : {
369 : bool prev33;
370 : struct GNUNET_JSON_Specification spec[] = {
371 84 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
372 : &dh->exchange_sig),
373 84 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
374 : &dh->exchange_pub),
375 84 : GNUNET_JSON_spec_mark_optional (
376 : TALER_JSON_spec_amount (
377 : "accumulated_total_without_fee",
378 84 : dh->total_without_fee.currency,
379 : &dr->details.ok.accumulated_total_without_fee),
380 : &prev33),
381 84 : GNUNET_JSON_spec_mark_optional (
382 : TALER_JSON_spec_web_url ("transaction_base_url",
383 : &dr->details.ok.transaction_base_url),
384 : NULL),
385 84 : GNUNET_JSON_spec_timestamp ("exchange_timestamp",
386 : &dh->exchange_timestamp),
387 84 : GNUNET_JSON_spec_end ()
388 : };
389 :
390 84 : if (GNUNET_OK !=
391 84 : GNUNET_JSON_parse (j,
392 : spec,
393 : NULL, NULL))
394 : {
395 0 : GNUNET_break_op (0);
396 0 : dr->hr.http_status = 0;
397 0 : dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
398 0 : break;
399 : }
400 84 : if (GNUNET_OK !=
401 84 : TALER_EXCHANGE_test_signing_key (dh->keys,
402 84 : &dh->exchange_pub))
403 : {
404 0 : GNUNET_break_op (0);
405 0 : dr->hr.http_status = 0;
406 0 : dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
407 0 : break;
408 : }
409 84 : if (prev33)
410 : dr->details.ok.accumulated_total_without_fee
411 0 : = dh->total_without_fee;
412 84 : if (1 ==
413 84 : TALER_amount_cmp (&dh->total_without_fee,
414 84 : &dr->details.ok.accumulated_total_without_fee))
415 : {
416 : /* Amount signed by exchange is SMALLER than what we deposited */
417 0 : GNUNET_break_op (0);
418 0 : dr->hr.http_status = 0;
419 0 : dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
420 0 : break;
421 : }
422 84 : {
423 84 : const struct TALER_CoinSpendSignatureP *csigs[
424 84 : GNUNET_NZL (dh->num_cdds)];
425 :
426 170 : for (unsigned int i = 0; i<dh->num_cdds; i++)
427 86 : csigs[i] = &dh->cdds[i].coin_sig;
428 84 : if (GNUNET_OK !=
429 84 : TALER_exchange_online_deposit_confirmation_verify (
430 84 : &dh->dcd.h_contract_terms,
431 84 : &dh->h_wire,
432 84 : &dh->h_policy,
433 : dh->exchange_timestamp,
434 : dh->dcd.wire_deadline,
435 : dh->dcd.refund_deadline,
436 84 : &dr->details.ok.accumulated_total_without_fee,
437 : dh->num_cdds,
438 : csigs,
439 84 : &dh->dcd.merchant_pub,
440 84 : &dh->exchange_pub,
441 84 : &dh->exchange_sig))
442 : {
443 0 : GNUNET_break_op (0);
444 0 : dr->hr.http_status = 0;
445 0 : dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
446 0 : break;
447 : }
448 : }
449 84 : dh->total_without_fee = dr->details.ok.accumulated_total_without_fee;
450 84 : TALER_EXCHANGE_get_auditors_for_dc_ (dh->keys,
451 : &auditor_cb,
452 : dh);
453 : }
454 84 : dr->details.ok.exchange_sig = &dh->exchange_sig;
455 84 : dr->details.ok.exchange_pub = &dh->exchange_pub;
456 84 : dr->details.ok.deposit_timestamp = dh->exchange_timestamp;
457 84 : break;
458 0 : case MHD_HTTP_BAD_REQUEST:
459 : /* This should never happen, either us or the exchange is buggy
460 : (or API version conflict); just pass JSON reply to the application */
461 0 : dr->hr.ec = TALER_JSON_get_error_code (j);
462 0 : dr->hr.hint = TALER_JSON_get_error_hint (j);
463 0 : break;
464 0 : case MHD_HTTP_FORBIDDEN:
465 0 : dr->hr.ec = TALER_JSON_get_error_code (j);
466 0 : dr->hr.hint = TALER_JSON_get_error_hint (j);
467 : /* Nothing really to verify, exchange says one of the signatures is
468 : invalid; as we checked them, this should never happen, we
469 : should pass the JSON reply to the application */
470 0 : break;
471 0 : case MHD_HTTP_NOT_FOUND:
472 0 : dr->hr.ec = TALER_JSON_get_error_code (j);
473 0 : dr->hr.hint = TALER_JSON_get_error_hint (j);
474 : /* Nothing really to verify, this should never
475 : happen, we should pass the JSON reply to the application */
476 0 : break;
477 9 : case MHD_HTTP_CONFLICT:
478 : {
479 9 : dr->hr.ec = TALER_JSON_get_error_code (j);
480 9 : dr->hr.hint = TALER_JSON_get_error_hint (j);
481 9 : switch (dr->hr.ec)
482 : {
483 4 : case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
484 : {
485 : struct GNUNET_JSON_Specification spec[] = {
486 4 : GNUNET_JSON_spec_fixed_auto (
487 : "coin_pub",
488 : &dr->details.conflict.details.insufficient_funds.coin_pub),
489 4 : GNUNET_JSON_spec_fixed_auto (
490 : "h_denom_pub",
491 : &dr->details.conflict.details.insufficient_funds.h_denom_pub),
492 4 : GNUNET_JSON_spec_end ()
493 : };
494 :
495 4 : if (GNUNET_OK !=
496 4 : GNUNET_JSON_parse (j,
497 : spec,
498 : NULL, NULL))
499 : {
500 0 : GNUNET_break_op (0);
501 0 : dr->hr.http_status = 0;
502 0 : dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
503 0 : break;
504 : }
505 : }
506 4 : break;
507 0 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
508 : {
509 : struct GNUNET_JSON_Specification spec[] = {
510 0 : GNUNET_JSON_spec_fixed_auto (
511 : "coin_pub",
512 : &dr->details.conflict.details
513 : .coin_conflicting_age_hash.coin_pub),
514 0 : GNUNET_JSON_spec_fixed_auto (
515 : "h_denom_pub",
516 : &dr->details.conflict.details
517 : .coin_conflicting_age_hash.h_denom_pub),
518 0 : GNUNET_JSON_spec_end ()
519 : };
520 :
521 0 : if (GNUNET_OK !=
522 0 : GNUNET_JSON_parse (j,
523 : spec,
524 : NULL, NULL))
525 : {
526 0 : GNUNET_break_op (0);
527 0 : dr->hr.http_status = 0;
528 0 : dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
529 0 : break;
530 : }
531 : }
532 0 : break;
533 1 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
534 : {
535 : struct GNUNET_JSON_Specification spec[] = {
536 1 : GNUNET_JSON_spec_fixed_auto (
537 : "coin_pub",
538 : &dr->details.conflict.details
539 : .coin_conflicting_denomination_key.coin_pub),
540 1 : GNUNET_JSON_spec_end ()
541 : };
542 :
543 1 : if (GNUNET_OK !=
544 1 : GNUNET_JSON_parse (j,
545 : spec,
546 : NULL, NULL))
547 : {
548 0 : GNUNET_break_op (0);
549 0 : dr->hr.http_status = 0;
550 0 : dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
551 0 : break;
552 : }
553 : }
554 1 : break;
555 4 : case TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT:
556 4 : break;
557 0 : default:
558 0 : GNUNET_break_op (0);
559 0 : break;
560 : }
561 : }
562 9 : break;
563 0 : case MHD_HTTP_GONE:
564 : /* could happen if denomination was revoked */
565 : /* Note: one might want to check /keys for revocation
566 : signature here, alas tricky in case our /keys
567 : is outdated => left to clients */
568 0 : dr->hr.ec = TALER_JSON_get_error_code (j);
569 0 : dr->hr.hint = TALER_JSON_get_error_hint (j);
570 0 : break;
571 5 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
572 5 : dr->hr.ec = TALER_JSON_get_error_code (j);
573 5 : dr->hr.hint = TALER_JSON_get_error_hint (j);
574 5 : if (GNUNET_OK !=
575 5 : TALER_EXCHANGE_parse_451 (&dr->details.unavailable_for_legal_reasons,
576 : j))
577 : {
578 0 : GNUNET_break_op (0);
579 0 : dr->hr.http_status = 0;
580 0 : dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
581 0 : break;
582 : }
583 5 : break;
584 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
585 0 : dr->hr.ec = TALER_JSON_get_error_code (j);
586 0 : dr->hr.hint = TALER_JSON_get_error_hint (j);
587 : /* Server had an internal issue; we should retry, but this API
588 : leaves this to the application */
589 0 : break;
590 0 : default:
591 : /* unexpected response code */
592 0 : dr->hr.ec = TALER_JSON_get_error_code (j);
593 0 : dr->hr.hint = TALER_JSON_get_error_hint (j);
594 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
595 : "Unexpected response code %u/%d for exchange deposit\n",
596 : (unsigned int) response_code,
597 : dr->hr.ec);
598 0 : GNUNET_break_op (0);
599 0 : break;
600 : }
601 98 : if (NULL != dh->ai_head)
602 0 : return;
603 98 : finish_dh (dh);
604 : }
605 :
606 :
607 : struct TALER_EXCHANGE_PostBatchDepositHandle *
608 98 : TALER_EXCHANGE_post_batch_deposit_create (
609 : struct GNUNET_CURL_Context *ctx,
610 : const char *url,
611 : struct TALER_EXCHANGE_Keys *keys,
612 : const struct TALER_EXCHANGE_DepositContractDetail *dcd,
613 : unsigned int num_cdds,
614 : const struct TALER_EXCHANGE_CoinDepositDetail cdds[static num_cdds],
615 : enum TALER_ErrorCode *ec)
616 98 : {
617 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh;
618 : json_t *deposits;
619 : const struct GNUNET_HashCode *wallet_data_hashp;
620 :
621 98 : if (0 == num_cdds)
622 : {
623 0 : GNUNET_break (0);
624 0 : *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
625 0 : return NULL;
626 : }
627 98 : if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline,
628 : >,
629 : dcd->wire_deadline))
630 : {
631 0 : GNUNET_break_op (0);
632 0 : *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE;
633 0 : return NULL;
634 : }
635 98 : dh = GNUNET_new (struct TALER_EXCHANGE_PostBatchDepositHandle);
636 98 : dh->auditor_chance = AUDITOR_CHANCE;
637 98 : dh->ctx = ctx;
638 98 : dh->base_url = GNUNET_strdup (url);
639 98 : dh->cdds = GNUNET_memdup (cdds,
640 : num_cdds * sizeof (*cdds));
641 98 : dh->num_cdds = num_cdds;
642 98 : dh->dcd = *dcd;
643 98 : if (NULL != dcd->policy_details)
644 0 : TALER_deposit_policy_hash (dcd->policy_details,
645 : &dh->h_policy);
646 98 : TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
647 : &dcd->wire_salt,
648 : &dh->h_wire);
649 98 : deposits = json_array ();
650 98 : GNUNET_assert (NULL != deposits);
651 98 : GNUNET_assert (GNUNET_OK ==
652 : TALER_amount_set_zero (cdds[0].amount.currency,
653 : &dh->total_without_fee));
654 198 : for (unsigned int i = 0; i<num_cdds; i++)
655 : {
656 100 : const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i];
657 : const struct TALER_EXCHANGE_DenomPublicKey *dki;
658 : const struct TALER_AgeCommitmentHashP *h_age_commitmentp;
659 : struct TALER_Amount amount_without_fee;
660 :
661 100 : dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
662 : &cdd->h_denom_pub);
663 100 : if (NULL == dki)
664 : {
665 0 : *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
666 0 : GNUNET_break_op (0);
667 0 : json_decref (deposits);
668 0 : GNUNET_free (dh->base_url);
669 0 : GNUNET_free (dh->cdds);
670 0 : GNUNET_free (dh);
671 0 : return NULL;
672 : }
673 100 : if (0 >
674 100 : TALER_amount_subtract (&amount_without_fee,
675 : &cdd->amount,
676 : &dki->fees.deposit))
677 : {
678 0 : *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
679 0 : GNUNET_break_op (0);
680 0 : json_decref (deposits);
681 0 : GNUNET_free (dh->base_url);
682 0 : GNUNET_free (dh->cdds);
683 0 : GNUNET_free (dh);
684 0 : return NULL;
685 : }
686 100 : GNUNET_assert (0 <=
687 : TALER_amount_add (&dh->total_without_fee,
688 : &dh->total_without_fee,
689 : &amount_without_fee));
690 100 : if (GNUNET_OK !=
691 100 : TALER_EXCHANGE_verify_deposit_signature_ (dcd,
692 100 : &dh->h_policy,
693 100 : &dh->h_wire,
694 : cdd,
695 : dki))
696 : {
697 0 : *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID;
698 0 : GNUNET_break_op (0);
699 0 : json_decref (deposits);
700 0 : GNUNET_free (dh->base_url);
701 0 : GNUNET_free (dh->cdds);
702 0 : GNUNET_free (dh);
703 0 : return NULL;
704 : }
705 100 : if (GNUNET_is_zero (&cdd->h_age_commitment))
706 74 : h_age_commitmentp = NULL;
707 : else
708 26 : h_age_commitmentp = &cdd->h_age_commitment;
709 100 : GNUNET_assert (
710 : 0 ==
711 : json_array_append_new (
712 : deposits,
713 : GNUNET_JSON_PACK (
714 : TALER_JSON_pack_amount ("contribution",
715 : &cdd->amount),
716 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
717 : &cdd->h_denom_pub),
718 : TALER_JSON_pack_denom_sig ("ub_sig",
719 : &cdd->denom_sig),
720 : GNUNET_JSON_pack_data_auto ("coin_pub",
721 : &cdd->coin_pub),
722 : GNUNET_JSON_pack_allow_null (
723 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
724 : h_age_commitmentp)),
725 : GNUNET_JSON_pack_data_auto ("coin_sig",
726 : &cdd->coin_sig)
727 : )));
728 : }
729 :
730 98 : if (GNUNET_is_zero (&dcd->wallet_data_hash))
731 98 : wallet_data_hashp = NULL;
732 : else
733 0 : wallet_data_hashp = &dcd->wallet_data_hash;
734 98 : dh->deposit_obj = GNUNET_JSON_PACK (
735 : TALER_JSON_pack_full_payto ("merchant_payto_uri",
736 : dcd->merchant_payto_uri),
737 : GNUNET_JSON_pack_allow_null (
738 : GNUNET_JSON_pack_string ("extra_wire_subject_metadata",
739 : dcd->extra_wire_subject_metadata)),
740 : GNUNET_JSON_pack_data_auto ("wire_salt",
741 : &dcd->wire_salt),
742 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
743 : &dcd->h_contract_terms),
744 : GNUNET_JSON_pack_array_steal ("coins",
745 : deposits),
746 : GNUNET_JSON_pack_allow_null (
747 : GNUNET_JSON_pack_data_auto ("wallet_data_hash",
748 : wallet_data_hashp)),
749 : GNUNET_JSON_pack_allow_null (
750 : GNUNET_JSON_pack_object_steal ("policy",
751 : (json_t *) dcd->policy_details)),
752 : GNUNET_JSON_pack_timestamp ("timestamp",
753 : dcd->wallet_timestamp),
754 : GNUNET_JSON_pack_data_auto ("merchant_pub",
755 : &dcd->merchant_pub),
756 : GNUNET_JSON_pack_data_auto ("merchant_sig",
757 : &dcd->merchant_sig),
758 : GNUNET_JSON_pack_allow_null (
759 : GNUNET_JSON_pack_timestamp ("refund_deadline",
760 : dcd->refund_deadline)),
761 : GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
762 : dcd->wire_deadline));
763 98 : if (NULL == dh->deposit_obj)
764 : {
765 0 : GNUNET_break (0);
766 0 : *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
767 0 : GNUNET_free (dh->base_url);
768 0 : GNUNET_free (dh->cdds);
769 0 : GNUNET_free (dh);
770 0 : return NULL;
771 : }
772 98 : dh->keys = TALER_EXCHANGE_keys_incref (keys);
773 98 : *ec = TALER_EC_NONE;
774 98 : return dh;
775 : }
776 :
777 :
778 : enum GNUNET_GenericReturnValue
779 0 : TALER_EXCHANGE_post_batch_deposit_set_options_ (
780 : struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh,
781 : unsigned int num_options,
782 : const struct TALER_EXCHANGE_PostBatchDepositOptionValue options[])
783 : {
784 0 : for (unsigned int i = 0; i < num_options; i++)
785 : {
786 0 : const struct TALER_EXCHANGE_PostBatchDepositOptionValue *opt = &options[i];
787 0 : switch (opt->option)
788 : {
789 0 : case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_END:
790 0 : return GNUNET_OK;
791 0 : case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_FORCE_DC:
792 0 : pbdh->auditor_chance = 1;
793 0 : break;
794 0 : case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_VERIFY_MERCHANT_SIG:
795 0 : pbdh->verify_merchant_sig = true;
796 0 : break;
797 : }
798 : }
799 0 : return GNUNET_OK;
800 : }
801 :
802 :
803 : enum TALER_ErrorCode
804 98 : TALER_EXCHANGE_post_batch_deposit_start (
805 : struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh,
806 : TALER_EXCHANGE_PostBatchDepositCallback cb,
807 : TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE *cb_cls)
808 : {
809 : CURL *eh;
810 :
811 98 : if (pbdh->verify_merchant_sig &&
812 : (GNUNET_OK !=
813 0 : TALER_merchant_contract_verify (
814 0 : &pbdh->dcd.h_contract_terms,
815 0 : &pbdh->dcd.merchant_pub,
816 : &pbdh->dcd.merchant_sig)) )
817 : {
818 0 : GNUNET_break_op (0);
819 0 : return TALER_EC_GENERIC_PARAMETER_MALFORMED;
820 : }
821 98 : pbdh->cb = cb;
822 98 : pbdh->cb_cls = cb_cls;
823 98 : pbdh->url = TALER_url_join (pbdh->base_url,
824 : "batch-deposit",
825 : NULL);
826 98 : if (NULL == pbdh->url)
827 : {
828 0 : GNUNET_break (0);
829 0 : return TALER_EC_GENERIC_ALLOCATION_FAILURE;
830 : }
831 98 : eh = TALER_EXCHANGE_curl_easy_get_ (pbdh->url);
832 196 : if ( (NULL == eh) ||
833 : (GNUNET_OK !=
834 98 : TALER_curl_easy_post (&pbdh->post_ctx,
835 : eh,
836 98 : pbdh->deposit_obj)) )
837 : {
838 0 : GNUNET_break (0);
839 0 : if (NULL != eh)
840 0 : curl_easy_cleanup (eh);
841 0 : return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
842 : }
843 : /* Help debug #11305 */
844 98 : GNUNET_assert (CURLE_OK ==
845 : curl_easy_setopt (eh,
846 : CURLOPT_VERBOSE,
847 : 1));
848 98 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
849 : "URL for batch-deposit: `%s'\n",
850 : pbdh->url);
851 196 : pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx,
852 : eh,
853 98 : pbdh->post_ctx.headers,
854 : &handle_deposit_finished,
855 : pbdh);
856 98 : if (NULL == pbdh->job)
857 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
858 98 : return TALER_EC_NONE;
859 : }
860 :
861 :
862 : void
863 98 : TALER_EXCHANGE_post_batch_deposit_cancel (
864 : struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh)
865 : {
866 : struct TEAH_AuditorInteractionEntry *aie;
867 :
868 98 : while (NULL != (aie = pbdh->ai_head))
869 : {
870 0 : GNUNET_assert (aie->dh == pbdh);
871 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
872 : "Not sending deposit confirmation to auditor `%s' due to cancellation\n",
873 : aie->auditor_url);
874 0 : TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
875 0 : GNUNET_CONTAINER_DLL_remove (pbdh->ai_head,
876 : pbdh->ai_tail,
877 : aie);
878 0 : GNUNET_free (aie);
879 : }
880 98 : if (NULL != pbdh->job)
881 : {
882 0 : GNUNET_CURL_job_cancel (pbdh->job);
883 0 : pbdh->job = NULL;
884 : }
885 98 : TALER_EXCHANGE_keys_decref (pbdh->keys);
886 98 : GNUNET_free (pbdh->base_url);
887 98 : GNUNET_free (pbdh->url);
888 98 : GNUNET_free (pbdh->cdds);
889 98 : TALER_curl_easy_post_finished (&pbdh->post_ctx);
890 98 : json_decref (pbdh->deposit_obj);
891 98 : json_decref (pbdh->response);
892 98 : GNUNET_free (pbdh);
893 98 : }
894 :
895 :
896 : /* end of exchange_api_post-batch-deposit.c */
|