Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2015-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_common.c
19 : * @brief common functions for the exchange API
20 : * @author Christian Grothoff
21 : */
22 : #include "taler/taler_json_lib.h"
23 : #include <microhttpd.h>
24 : #include <gnunet/gnunet_curl_lib.h>
25 : #include "exchange_api_common.h"
26 : #include "exchange_api_handle.h"
27 : #include "taler/taler_signatures.h"
28 :
29 :
30 : const struct TALER_EXCHANGE_SigningPublicKey *
31 4 : TALER_EXCHANGE_get_signing_key_info (
32 : const struct TALER_EXCHANGE_Keys *keys,
33 : const struct TALER_ExchangePublicKeyP *exchange_pub)
34 : {
35 4 : for (unsigned int i = 0; i<keys->num_sign_keys; i++)
36 : {
37 4 : const struct TALER_EXCHANGE_SigningPublicKey *spk
38 4 : = &keys->sign_keys[i];
39 :
40 4 : if (0 == GNUNET_memcmp (exchange_pub,
41 : &spk->key))
42 4 : return spk;
43 : }
44 0 : return NULL;
45 : }
46 :
47 :
48 : enum GNUNET_GenericReturnValue
49 0 : TALER_EXCHANGE_check_purse_create_conflict_ (
50 : const struct TALER_PurseContractSignatureP *cpurse_sig,
51 : const struct TALER_PurseContractPublicKeyP *purse_pub,
52 : const json_t *proof)
53 : {
54 : struct TALER_Amount amount;
55 : uint32_t min_age;
56 : struct GNUNET_TIME_Timestamp purse_expiration;
57 : struct TALER_PurseContractSignatureP purse_sig;
58 : struct TALER_PrivateContractHashP h_contract_terms;
59 : struct TALER_PurseMergePublicKeyP merge_pub;
60 : struct GNUNET_JSON_Specification spec[] = {
61 0 : TALER_JSON_spec_amount_any ("amount",
62 : &amount),
63 0 : GNUNET_JSON_spec_uint32 ("min_age",
64 : &min_age),
65 0 : GNUNET_JSON_spec_timestamp ("purse_expiration",
66 : &purse_expiration),
67 0 : GNUNET_JSON_spec_fixed_auto ("purse_sig",
68 : &purse_sig),
69 0 : GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
70 : &h_contract_terms),
71 0 : GNUNET_JSON_spec_fixed_auto ("merge_pub",
72 : &merge_pub),
73 0 : GNUNET_JSON_spec_end ()
74 : };
75 :
76 0 : if (GNUNET_OK !=
77 0 : GNUNET_JSON_parse (proof,
78 : spec,
79 : NULL, NULL))
80 : {
81 0 : GNUNET_break_op (0);
82 0 : return GNUNET_SYSERR;
83 : }
84 0 : if (GNUNET_OK !=
85 0 : TALER_wallet_purse_create_verify (purse_expiration,
86 : &h_contract_terms,
87 : &merge_pub,
88 : min_age,
89 : &amount,
90 : purse_pub,
91 : &purse_sig))
92 : {
93 0 : GNUNET_break_op (0);
94 0 : return GNUNET_SYSERR;
95 : }
96 0 : if (0 ==
97 0 : GNUNET_memcmp (&purse_sig,
98 : cpurse_sig))
99 : {
100 : /* Must be the SAME data, not a conflict! */
101 0 : GNUNET_break_op (0);
102 0 : return GNUNET_SYSERR;
103 : }
104 0 : return GNUNET_OK;
105 : }
106 :
107 :
108 : enum GNUNET_GenericReturnValue
109 2 : TALER_EXCHANGE_check_purse_merge_conflict_ (
110 : const struct TALER_PurseMergeSignatureP *cmerge_sig,
111 : const struct TALER_PurseMergePublicKeyP *merge_pub,
112 : const struct TALER_PurseContractPublicKeyP *purse_pub,
113 : const char *exchange_url,
114 : const json_t *proof)
115 : {
116 : struct TALER_PurseMergeSignatureP merge_sig;
117 : struct GNUNET_TIME_Timestamp merge_timestamp;
118 2 : const char *partner_url = NULL;
119 : struct TALER_ReservePublicKeyP reserve_pub;
120 : struct GNUNET_JSON_Specification spec[] = {
121 2 : GNUNET_JSON_spec_mark_optional (
122 : TALER_JSON_spec_web_url ("partner_url",
123 : &partner_url),
124 : NULL),
125 2 : GNUNET_JSON_spec_timestamp ("merge_timestamp",
126 : &merge_timestamp),
127 2 : GNUNET_JSON_spec_fixed_auto ("merge_sig",
128 : &merge_sig),
129 2 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
130 : &reserve_pub),
131 2 : GNUNET_JSON_spec_end ()
132 : };
133 : struct TALER_NormalizedPayto payto_uri;
134 :
135 2 : if (GNUNET_OK !=
136 2 : GNUNET_JSON_parse (proof,
137 : spec,
138 : NULL, NULL))
139 : {
140 0 : GNUNET_break_op (0);
141 0 : return GNUNET_SYSERR;
142 : }
143 2 : if (NULL == partner_url)
144 2 : partner_url = exchange_url;
145 2 : payto_uri = TALER_reserve_make_payto (partner_url,
146 : &reserve_pub);
147 2 : if (GNUNET_OK !=
148 2 : TALER_wallet_purse_merge_verify (
149 : payto_uri,
150 : merge_timestamp,
151 : purse_pub,
152 : merge_pub,
153 : &merge_sig))
154 : {
155 0 : GNUNET_break_op (0);
156 0 : GNUNET_free (payto_uri.normalized_payto);
157 0 : return GNUNET_SYSERR;
158 : }
159 2 : GNUNET_free (payto_uri.normalized_payto);
160 2 : if (0 ==
161 2 : GNUNET_memcmp (&merge_sig,
162 : cmerge_sig))
163 : {
164 : /* Must be the SAME data, not a conflict! */
165 0 : GNUNET_break_op (0);
166 0 : return GNUNET_SYSERR;
167 : }
168 2 : return GNUNET_OK;
169 : }
170 :
171 :
172 : enum GNUNET_GenericReturnValue
173 0 : TALER_EXCHANGE_check_purse_coin_conflict_ (
174 : const struct TALER_PurseContractPublicKeyP *purse_pub,
175 : const char *exchange_url,
176 : const json_t *proof,
177 : struct TALER_DenominationHashP *h_denom_pub,
178 : struct TALER_AgeCommitmentHashP *phac,
179 : struct TALER_CoinSpendPublicKeyP *coin_pub,
180 : struct TALER_CoinSpendSignatureP *coin_sig)
181 : {
182 0 : const char *partner_url = NULL;
183 : struct TALER_Amount amount;
184 : struct GNUNET_JSON_Specification spec[] = {
185 0 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
186 : h_denom_pub),
187 0 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
188 : phac),
189 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
190 : coin_sig),
191 0 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
192 : coin_pub),
193 0 : GNUNET_JSON_spec_mark_optional (
194 : TALER_JSON_spec_web_url ("partner_url",
195 : &partner_url),
196 : NULL),
197 0 : TALER_JSON_spec_amount_any ("amount",
198 : &amount),
199 0 : GNUNET_JSON_spec_end ()
200 : };
201 :
202 0 : if (GNUNET_OK !=
203 0 : GNUNET_JSON_parse (proof,
204 : spec,
205 : NULL, NULL))
206 : {
207 0 : GNUNET_break_op (0);
208 0 : return GNUNET_SYSERR;
209 : }
210 0 : if (NULL == partner_url)
211 0 : partner_url = exchange_url;
212 0 : if (GNUNET_OK !=
213 0 : TALER_wallet_purse_deposit_verify (
214 : partner_url,
215 : purse_pub,
216 : &amount,
217 : h_denom_pub,
218 : phac,
219 : coin_pub,
220 : coin_sig))
221 : {
222 0 : GNUNET_break_op (0);
223 0 : return GNUNET_SYSERR;
224 : }
225 0 : return GNUNET_OK;
226 : }
227 :
228 :
229 : enum GNUNET_GenericReturnValue
230 0 : TALER_EXCHANGE_check_purse_econtract_conflict_ (
231 : const struct TALER_PurseContractSignatureP *ccontract_sig,
232 : const struct TALER_PurseContractPublicKeyP *purse_pub,
233 : const json_t *proof)
234 : {
235 : struct TALER_ContractDiffiePublicP contract_pub;
236 : struct TALER_PurseContractSignatureP contract_sig;
237 : struct GNUNET_HashCode h_econtract;
238 : struct GNUNET_JSON_Specification spec[] = {
239 0 : GNUNET_JSON_spec_fixed_auto ("h_econtract",
240 : &h_econtract),
241 0 : GNUNET_JSON_spec_fixed_auto ("econtract_sig",
242 : &contract_sig),
243 0 : GNUNET_JSON_spec_fixed_auto ("contract_pub",
244 : &contract_pub),
245 0 : GNUNET_JSON_spec_end ()
246 : };
247 :
248 0 : if (GNUNET_OK !=
249 0 : GNUNET_JSON_parse (proof,
250 : spec,
251 : NULL, NULL))
252 : {
253 0 : GNUNET_break_op (0);
254 0 : return GNUNET_SYSERR;
255 : }
256 0 : if (GNUNET_OK !=
257 0 : TALER_wallet_econtract_upload_verify2 (
258 : &h_econtract,
259 : &contract_pub,
260 : purse_pub,
261 : &contract_sig))
262 : {
263 0 : GNUNET_break_op (0);
264 0 : return GNUNET_SYSERR;
265 : }
266 0 : if (0 ==
267 0 : GNUNET_memcmp (&contract_sig,
268 : ccontract_sig))
269 : {
270 : /* Must be the SAME data, not a conflict! */
271 0 : GNUNET_break_op (0);
272 0 : return GNUNET_SYSERR;
273 : }
274 0 : return GNUNET_OK;
275 : }
276 :
277 :
278 : enum GNUNET_GenericReturnValue
279 12 : TALER_EXCHANGE_parse_coin_denomination_conflict_ (
280 : const json_t *proof,
281 : struct TALER_EXCHANGE_CoinDenominationConflict *cdc)
282 : {
283 : struct GNUNET_JSON_Specification spec[] = {
284 12 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
285 : &cdc->coin_pub),
286 12 : TALER_JSON_spec_denom_pub ("prev_denom_pub",
287 : &cdc->prev_denom_pub),
288 12 : TALER_JSON_spec_denom_sig ("prev_denom_sig",
289 : &cdc->prev_denom_sig),
290 12 : GNUNET_JSON_spec_mark_optional (
291 12 : GNUNET_JSON_spec_fixed_auto ("prev_h_age_commitment",
292 : &cdc->prev_h_age_commitment),
293 : &cdc->no_prev_age_commitment),
294 12 : GNUNET_JSON_spec_end ()
295 : };
296 :
297 12 : memset (cdc,
298 : 0,
299 : sizeof (*cdc));
300 12 : if (GNUNET_OK !=
301 12 : GNUNET_JSON_parse (proof,
302 : spec,
303 : NULL, NULL))
304 : {
305 1 : GNUNET_break_op (0);
306 1 : return GNUNET_SYSERR;
307 : }
308 11 : TALER_denom_pub_hash (&cdc->prev_denom_pub,
309 : &cdc->prev_h_denom_pub);
310 11 : cdc->verified = false;
311 11 : return GNUNET_OK;
312 : }
313 :
314 :
315 : enum GNUNET_GenericReturnValue
316 11 : TALER_EXCHANGE_check_coin_denomination_conflict_ (
317 : const struct TALER_EXCHANGE_Keys *keys,
318 : const struct TALER_DenominationHashP *h_denom_pub,
319 : struct TALER_EXCHANGE_CoinDenominationConflict *cdc)
320 : {
321 : const struct TALER_EXCHANGE_DenomPublicKey *dk;
322 11 : struct TALER_CoinPublicInfo cpi = {
323 : .coin_pub = cdc->coin_pub,
324 : .denom_pub_hash = cdc->prev_h_denom_pub,
325 : .h_age_commitment = cdc->prev_h_age_commitment,
326 11 : .no_age_commitment = cdc->no_prev_age_commitment,
327 : .denom_sig = cdc->prev_denom_sig
328 : };
329 :
330 11 : cdc->verified = false;
331 11 : if (0 ==
332 11 : GNUNET_memcmp (h_denom_pub,
333 : &cdc->prev_h_denom_pub))
334 : {
335 : /* Must be a DIFFERENT denomination, not a conflict! */
336 1 : GNUNET_break_op (0);
337 1 : return GNUNET_SYSERR;
338 : }
339 10 : dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
340 10 : &cdc->prev_h_denom_pub);
341 10 : if (NULL == dk)
342 : {
343 : /* TODO[oec]: the exchange names a denomination that is not (or no
344 : longer) in our /keys, for example one that expired. We cannot
345 : verify the signature, but we also cannot tell that the proof is
346 : wrong. We accept the response as unverifiable and leave the
347 : decision to the application (see @e verified). */
348 1 : return GNUNET_NO;
349 : }
350 : /* the age commitment hash must be present exactly if the
351 : denomination is age-restricted */
352 9 : if (cdc->no_prev_age_commitment !=
353 9 : (0 == dk->key.age_mask.bits))
354 : {
355 2 : GNUNET_break_op (0);
356 2 : return GNUNET_SYSERR;
357 : }
358 7 : if (0 !=
359 7 : TALER_denom_pub_cmp (&dk->key,
360 7 : &cdc->prev_denom_pub))
361 : {
362 : /* hash matches, key does not?! */
363 0 : GNUNET_break_op (0);
364 0 : return GNUNET_SYSERR;
365 : }
366 7 : if (GNUNET_OK !=
367 7 : TALER_test_coin_valid (&cpi,
368 7 : &cdc->prev_denom_pub))
369 : {
370 2 : GNUNET_break_op (0);
371 2 : return GNUNET_SYSERR;
372 : }
373 5 : cdc->verified = true;
374 5 : return GNUNET_OK;
375 : }
376 :
377 :
378 : void
379 11 : TALER_EXCHANGE_free_coin_denomination_conflict_ (
380 : struct TALER_EXCHANGE_CoinDenominationConflict *cdc)
381 : {
382 11 : TALER_denom_pub_free (&cdc->prev_denom_pub);
383 11 : TALER_denom_sig_free (&cdc->prev_denom_sig);
384 11 : }
385 :
386 :
387 : enum GNUNET_GenericReturnValue
388 8 : TALER_EXCHANGE_parse_coin_age_commitment_conflict_ (
389 : const json_t *proof,
390 : struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac)
391 : {
392 : struct GNUNET_JSON_Specification spec[] = {
393 8 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
394 : &cac->coin_pub),
395 8 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
396 : &cac->h_denom_pub),
397 8 : GNUNET_JSON_spec_mark_optional (
398 8 : GNUNET_JSON_spec_fixed_auto ("expected_age_commitment_hash",
399 : &cac->expected_age_commitment_hash),
400 : &cac->no_expected_age_commitment),
401 8 : GNUNET_JSON_spec_string ("conflict_detail",
402 : &cac->conflict_detail),
403 8 : TALER_JSON_spec_denom_sig ("prev_denom_sig",
404 : &cac->prev_denom_sig),
405 8 : GNUNET_JSON_spec_end ()
406 : };
407 :
408 8 : memset (cac,
409 : 0,
410 : sizeof (*cac));
411 8 : if (GNUNET_OK !=
412 8 : GNUNET_JSON_parse (proof,
413 : spec,
414 : NULL, NULL))
415 : {
416 0 : GNUNET_break_op (0);
417 0 : return GNUNET_SYSERR;
418 : }
419 8 : cac->verified = false;
420 8 : return GNUNET_OK;
421 : }
422 :
423 :
424 : enum GNUNET_GenericReturnValue
425 8 : TALER_EXCHANGE_check_coin_age_commitment_conflict_ (
426 : const struct TALER_EXCHANGE_Keys *keys,
427 : const struct TALER_DenominationHashP *h_denom_pub,
428 : const struct TALER_AgeCommitmentHashP *h_age_commitment,
429 : struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac)
430 : {
431 : const struct TALER_EXCHANGE_DenomPublicKey *dk;
432 8 : struct TALER_CoinPublicInfo cpi = {
433 : .coin_pub = cac->coin_pub,
434 : .denom_pub_hash = cac->h_denom_pub,
435 : .h_age_commitment = cac->expected_age_commitment_hash,
436 8 : .no_age_commitment = cac->no_expected_age_commitment,
437 : .denom_sig = cac->prev_denom_sig
438 : };
439 :
440 8 : cac->verified = false;
441 8 : if (0 !=
442 8 : GNUNET_memcmp (h_denom_pub,
443 : &cac->h_denom_pub))
444 : {
445 : /* an age conflict is about the SAME denomination */
446 1 : GNUNET_break_op (0);
447 1 : return GNUNET_SYSERR;
448 : }
449 7 : if (cac->no_expected_age_commitment == (NULL == h_age_commitment))
450 : {
451 : /* both absent, or both present: then they must differ */
452 5 : if ( (cac->no_expected_age_commitment) ||
453 : (0 ==
454 4 : GNUNET_memcmp (h_age_commitment,
455 : &cac->expected_age_commitment_hash)) )
456 : {
457 : /* Must be a DIFFERENT age commitment, not a conflict! */
458 2 : GNUNET_break_op (0);
459 2 : return GNUNET_SYSERR;
460 : }
461 : }
462 5 : dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
463 5 : &cac->h_denom_pub);
464 5 : if (NULL == dk)
465 : {
466 : /* TODO[oec]: as for the denomination conflict, the denomination is
467 : not in our /keys, so the signature cannot be checked; accept the
468 : response as unverifiable. */
469 1 : return GNUNET_NO;
470 : }
471 4 : if (GNUNET_OK !=
472 4 : TALER_test_coin_valid (&cpi,
473 : &dk->key))
474 : {
475 1 : GNUNET_break_op (0);
476 1 : return GNUNET_SYSERR;
477 : }
478 3 : cac->verified = true;
479 3 : return GNUNET_OK;
480 : }
481 :
482 :
483 : void
484 8 : TALER_EXCHANGE_free_coin_age_commitment_conflict_ (
485 : struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac)
486 : {
487 8 : TALER_denom_sig_free (&cac->prev_denom_sig);
488 8 : cac->conflict_detail = NULL;
489 8 : }
490 :
491 :
492 : enum GNUNET_GenericReturnValue
493 3 : TALER_EXCHANGE_check_coin_conflict_ (
494 : const struct TALER_EXCHANGE_Keys *keys,
495 : enum TALER_ErrorCode ec,
496 : const json_t *proof,
497 : TALER_EXCHANGE_CoinLookupCallback_ lookup,
498 : void *lookup_cls,
499 : struct TALER_EXCHANGE_CoinConflict *cc)
500 : {
501 3 : const struct TALER_DenominationHashP *h_denom_pub = NULL;
502 3 : const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL;
503 :
504 3 : cc->ec = TALER_EC_NONE;
505 3 : switch (ec)
506 : {
507 3 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
508 : {
509 3 : struct TALER_EXCHANGE_CoinDenominationConflict *cdc
510 : = &cc->details.denomination_conflict;
511 :
512 3 : if (GNUNET_OK !=
513 3 : TALER_EXCHANGE_parse_coin_denomination_conflict_ (proof,
514 : cdc))
515 : {
516 0 : GNUNET_break_op (0);
517 0 : return GNUNET_SYSERR;
518 : }
519 3 : if ( (GNUNET_OK !=
520 3 : lookup (lookup_cls,
521 3 : &cdc->coin_pub,
522 : &h_denom_pub,
523 3 : &h_age_commitment)) ||
524 : (GNUNET_SYSERR ==
525 3 : TALER_EXCHANGE_check_coin_denomination_conflict_ (keys,
526 : h_denom_pub,
527 : cdc)) )
528 : {
529 0 : GNUNET_break_op (0);
530 0 : TALER_EXCHANGE_free_coin_denomination_conflict_ (cdc);
531 0 : return GNUNET_SYSERR;
532 : }
533 3 : cc->ec = ec;
534 3 : return GNUNET_OK;
535 : }
536 0 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
537 : {
538 0 : struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac
539 : = &cc->details.age_commitment_conflict;
540 :
541 0 : if (GNUNET_OK !=
542 0 : TALER_EXCHANGE_parse_coin_age_commitment_conflict_ (proof,
543 : cac))
544 : {
545 0 : GNUNET_break_op (0);
546 0 : return GNUNET_SYSERR;
547 : }
548 0 : if ( (GNUNET_OK !=
549 0 : lookup (lookup_cls,
550 0 : &cac->coin_pub,
551 : &h_denom_pub,
552 0 : &h_age_commitment)) ||
553 : (GNUNET_SYSERR ==
554 0 : TALER_EXCHANGE_check_coin_age_commitment_conflict_ (
555 : keys,
556 : h_denom_pub,
557 : h_age_commitment,
558 : cac)) )
559 : {
560 0 : GNUNET_break_op (0);
561 0 : TALER_EXCHANGE_free_coin_age_commitment_conflict_ (cac);
562 0 : return GNUNET_SYSERR;
563 : }
564 0 : cc->ec = ec;
565 0 : return GNUNET_OK;
566 : }
567 0 : default:
568 0 : GNUNET_break (0);
569 0 : return GNUNET_SYSERR;
570 : }
571 : }
572 :
573 :
574 : void
575 204 : TALER_EXCHANGE_free_coin_conflict_ (
576 : const struct TALER_EXCHANGE_HttpResponse *hr,
577 : struct TALER_EXCHANGE_CoinConflict *cc)
578 : {
579 204 : if (MHD_HTTP_CONFLICT != hr->http_status)
580 171 : return;
581 33 : if (hr->ec != cc->ec)
582 30 : return;
583 3 : switch (cc->ec)
584 : {
585 3 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
586 3 : TALER_EXCHANGE_free_coin_denomination_conflict_ (
587 : &cc->details.denomination_conflict);
588 3 : break;
589 0 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
590 0 : TALER_EXCHANGE_free_coin_age_commitment_conflict_ (
591 : &cc->details.age_commitment_conflict);
592 0 : break;
593 0 : default:
594 0 : break;
595 : }
596 3 : cc->ec = TALER_EC_NONE;
597 : }
598 :
599 :
600 : enum GNUNET_GenericReturnValue
601 0 : TALER_EXCHANGE_get_min_denomination_ (
602 : const struct TALER_EXCHANGE_Keys *keys,
603 : struct TALER_Amount *min)
604 : {
605 0 : bool have_min = false;
606 0 : for (unsigned int i = 0; i<keys->num_denom_keys; i++)
607 : {
608 0 : const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i];
609 :
610 0 : if (! have_min)
611 : {
612 0 : *min = dk->value;
613 0 : have_min = true;
614 0 : continue;
615 : }
616 0 : if (1 != TALER_amount_cmp (min,
617 : &dk->value))
618 0 : continue;
619 0 : *min = dk->value;
620 : }
621 0 : if (! have_min)
622 : {
623 0 : GNUNET_break (0);
624 0 : return GNUNET_SYSERR;
625 : }
626 0 : return GNUNET_OK;
627 : }
628 :
629 :
630 : enum GNUNET_GenericReturnValue
631 120 : TALER_EXCHANGE_verify_deposit_signature_ (
632 : const struct TALER_EXCHANGE_DepositContractDetail *dcd,
633 : const struct TALER_ExtensionPolicyHashP *ech,
634 : const struct TALER_MerchantWireHashP *h_wire,
635 : const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
636 : const struct TALER_EXCHANGE_DenomPublicKey *dki)
637 : {
638 120 : if (GNUNET_OK !=
639 120 : TALER_wallet_deposit_verify (&cdd->amount,
640 : &dki->fees.deposit,
641 : h_wire,
642 : &dcd->h_contract_terms,
643 : &dcd->wallet_data_hash,
644 : &cdd->h_age_commitment,
645 : ech,
646 : &cdd->h_denom_pub,
647 : dcd->wallet_timestamp,
648 : &dcd->merchant_pub,
649 : dcd->refund_deadline,
650 : &cdd->coin_pub,
651 : &cdd->coin_sig))
652 : {
653 0 : GNUNET_break_op (0);
654 0 : TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n");
655 0 : TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
656 : TALER_amount2s (&cdd->amount));
657 0 : TALER_LOG_DEBUG ("... deposit_fee was %s\n",
658 : TALER_amount2s (&dki->fees.deposit));
659 0 : return GNUNET_SYSERR;
660 : }
661 :
662 : /* check coin signature */
663 : {
664 120 : struct TALER_CoinPublicInfo coin_info = {
665 : .coin_pub = cdd->coin_pub,
666 : .denom_pub_hash = cdd->h_denom_pub,
667 : .denom_sig = cdd->denom_sig,
668 : .h_age_commitment = cdd->h_age_commitment,
669 : };
670 :
671 120 : if (GNUNET_YES !=
672 120 : TALER_test_coin_valid (&coin_info,
673 : &dki->key))
674 : {
675 0 : GNUNET_break_op (0);
676 0 : TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
677 0 : return GNUNET_SYSERR;
678 : }
679 : }
680 :
681 : /* Check coin does make a contribution */
682 120 : if (0 < TALER_amount_cmp (&dki->fees.deposit,
683 : &cdd->amount))
684 : {
685 0 : GNUNET_break_op (0);
686 0 : TALER_LOG_WARNING ("Deposit amount smaller than fee\n");
687 0 : return GNUNET_SYSERR;
688 : }
689 120 : return GNUNET_OK;
690 : }
691 :
692 :
693 : /**
694 : * Parse account restriction in @a jrest into @a rest.
695 : *
696 : * @param jresta array of account restrictions in JSON
697 : * @param[out] resta_len set to length of @a resta
698 : * @param[out] resta account restriction array to set
699 : * @return #GNUNET_OK on success
700 : */
701 : static enum GNUNET_GenericReturnValue
702 108 : parse_restrictions (const json_t *jresta,
703 : unsigned int *resta_len,
704 : struct TALER_EXCHANGE_AccountRestriction **resta)
705 : {
706 : size_t alen;
707 :
708 108 : if (! json_is_array (jresta))
709 : {
710 0 : GNUNET_break_op (0);
711 0 : return GNUNET_SYSERR;
712 : }
713 108 : alen = json_array_size (jresta);
714 108 : if (0 == alen)
715 : {
716 : /* no restrictions, perfectly OK */
717 108 : *resta = NULL;
718 108 : return GNUNET_OK;
719 : }
720 0 : *resta_len = (unsigned int) alen;
721 0 : GNUNET_assert (alen == *resta_len);
722 0 : *resta = GNUNET_new_array (*resta_len,
723 : struct TALER_EXCHANGE_AccountRestriction);
724 0 : for (unsigned int i = 0; i< *resta_len; i++)
725 : {
726 0 : const json_t *jr = json_array_get (jresta,
727 : i);
728 0 : struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i];
729 0 : const char *type = json_string_value (json_object_get (jr,
730 : "type"));
731 :
732 0 : if (NULL == type)
733 : {
734 0 : GNUNET_break (0);
735 0 : goto fail;
736 : }
737 0 : if (0 == strcmp (type,
738 : "deny"))
739 : {
740 0 : ar->type = TALER_EXCHANGE_AR_DENY;
741 0 : continue;
742 : }
743 0 : if (0 == strcmp (type,
744 : "regex"))
745 0 : {
746 : const char *regex;
747 : const char *hint;
748 : struct GNUNET_JSON_Specification spec[] = {
749 0 : GNUNET_JSON_spec_string (
750 : "payto_regex",
751 : ®ex),
752 0 : GNUNET_JSON_spec_string (
753 : "human_hint",
754 : &hint),
755 0 : GNUNET_JSON_spec_mark_optional (
756 : GNUNET_JSON_spec_json (
757 : "human_hint_i18n",
758 : &ar->details.regex.human_hint_i18n),
759 : NULL),
760 0 : GNUNET_JSON_spec_end ()
761 : };
762 :
763 0 : if (GNUNET_OK !=
764 0 : GNUNET_JSON_parse (jr,
765 : spec,
766 : NULL, NULL))
767 : {
768 : /* bogus reply */
769 0 : GNUNET_break_op (0);
770 0 : goto fail;
771 : }
772 0 : ar->type = TALER_EXCHANGE_AR_REGEX;
773 0 : ar->details.regex.posix_egrep = GNUNET_strdup (regex);
774 0 : ar->details.regex.human_hint = GNUNET_strdup (hint);
775 0 : continue;
776 : }
777 : /* unsupported type */
778 0 : GNUNET_break (0);
779 0 : goto fail;
780 : }
781 0 : return GNUNET_OK;
782 0 : fail:
783 0 : for (unsigned int i = 0; i<*resta_len; i++)
784 : {
785 0 : struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i];
786 :
787 0 : if (TALER_EXCHANGE_AR_REGEX == ar->type)
788 : {
789 0 : GNUNET_free (ar->details.regex.posix_egrep);
790 0 : GNUNET_free (ar->details.regex.human_hint);
791 0 : json_decref (ar->details.regex.human_hint_i18n);
792 : }
793 : }
794 0 : GNUNET_free (*resta);
795 0 : *resta_len = 0;
796 0 : return GNUNET_SYSERR;
797 : }
798 :
799 :
800 : enum GNUNET_GenericReturnValue
801 50 : TALER_EXCHANGE_parse_accounts (
802 : const struct TALER_MasterPublicKeyP *master_pub,
803 : const json_t *accounts,
804 : unsigned int was_length,
805 : struct TALER_EXCHANGE_WireAccount was[static was_length])
806 50 : {
807 50 : memset (was,
808 : 0,
809 : sizeof (struct TALER_EXCHANGE_WireAccount) * was_length);
810 50 : GNUNET_assert (was_length ==
811 : json_array_size (accounts));
812 50 : for (unsigned int i = 0;
813 104 : i<was_length;
814 54 : i++)
815 : {
816 54 : struct TALER_EXCHANGE_WireAccount *wa = &was[i];
817 : struct TALER_FullPayto payto_uri;
818 54 : const char *conversion_url = NULL;
819 54 : const char *open_banking_gateway = NULL;
820 54 : const char *prepared_transfer_url = NULL;
821 54 : const char *bank_label = NULL;
822 54 : int64_t priority = 0;
823 : const json_t *credit_restrictions;
824 : const json_t *debit_restrictions;
825 : struct GNUNET_JSON_Specification spec_account[] = {
826 54 : TALER_JSON_spec_full_payto_uri ("payto_uri",
827 : &payto_uri),
828 54 : GNUNET_JSON_spec_mark_optional (
829 : TALER_JSON_spec_web_url ("conversion_url",
830 : &conversion_url),
831 : NULL),
832 54 : GNUNET_JSON_spec_mark_optional (
833 : TALER_JSON_spec_web_url ("open_banking_gateway",
834 : &open_banking_gateway),
835 : NULL),
836 54 : GNUNET_JSON_spec_mark_optional (
837 : TALER_JSON_spec_web_url ("prepared_transfer_url",
838 : &prepared_transfer_url),
839 : NULL),
840 54 : GNUNET_JSON_spec_mark_optional (
841 : GNUNET_JSON_spec_int64 ("priority",
842 : &priority),
843 : NULL),
844 54 : GNUNET_JSON_spec_mark_optional (
845 : GNUNET_JSON_spec_string ("bank_label",
846 : &bank_label),
847 : NULL),
848 54 : GNUNET_JSON_spec_array_const ("credit_restrictions",
849 : &credit_restrictions),
850 54 : GNUNET_JSON_spec_array_const ("debit_restrictions",
851 : &debit_restrictions),
852 54 : GNUNET_JSON_spec_fixed_auto ("master_sig",
853 : &wa->master_sig),
854 54 : GNUNET_JSON_spec_end ()
855 : };
856 : json_t *account;
857 :
858 54 : account = json_array_get (accounts,
859 : i);
860 54 : if (GNUNET_OK !=
861 54 : GNUNET_JSON_parse (account,
862 : spec_account,
863 : NULL, NULL))
864 : {
865 : /* bogus reply */
866 0 : GNUNET_break_op (0);
867 0 : return GNUNET_SYSERR;
868 : }
869 54 : if ( (NULL != master_pub) &&
870 108 : (! ( ( (NULL == open_banking_gateway) &&
871 108 : (NULL == prepared_transfer_url) &&
872 : (GNUNET_OK ==
873 54 : TALER_exchange_wire_signature_check32 (
874 : payto_uri,
875 : conversion_url,
876 : debit_restrictions,
877 : credit_restrictions,
878 : master_pub,
879 54 : &wa->master_sig)) ) ||
880 : (GNUNET_OK ==
881 54 : TALER_exchange_wire_signature_check (
882 : payto_uri,
883 : conversion_url,
884 : open_banking_gateway,
885 : prepared_transfer_url,
886 : debit_restrictions,
887 : credit_restrictions,
888 : master_pub,
889 54 : &wa->master_sig)) ) ) )
890 : {
891 : /* bogus reply */
892 0 : GNUNET_break_op (0);
893 0 : return GNUNET_SYSERR;
894 : }
895 54 : if ( (GNUNET_OK !=
896 54 : parse_restrictions (credit_restrictions,
897 : &wa->credit_restrictions_length,
898 54 : &wa->credit_restrictions)) ||
899 : (GNUNET_OK !=
900 54 : parse_restrictions (debit_restrictions,
901 : &wa->debit_restrictions_length,
902 : &wa->debit_restrictions)) )
903 : {
904 : /* bogus reply */
905 0 : GNUNET_break_op (0);
906 0 : return GNUNET_SYSERR;
907 : }
908 : wa->fpayto_uri.full_payto
909 54 : = GNUNET_strdup (payto_uri.full_payto);
910 54 : wa->priority = priority;
911 54 : if (NULL != conversion_url)
912 0 : wa->conversion_url = GNUNET_strdup (conversion_url);
913 54 : if (NULL != open_banking_gateway)
914 0 : wa->open_banking_gateway = GNUNET_strdup (open_banking_gateway);
915 54 : if (NULL != prepared_transfer_url)
916 0 : wa->prepared_transfer_url = GNUNET_strdup (prepared_transfer_url);
917 54 : if (NULL != bank_label)
918 4 : wa->bank_label = GNUNET_strdup (bank_label);
919 : } /* end 'for all accounts */
920 50 : return GNUNET_OK;
921 : }
922 :
923 :
924 : /**
925 : * Free array of account restrictions.
926 : *
927 : * @param ar_len length of @a ar
928 : * @param[in] ar array to free contents of (but not @a ar itself)
929 : */
930 : static void
931 108 : free_restrictions (unsigned int ar_len,
932 : struct TALER_EXCHANGE_AccountRestriction ar[static ar_len])
933 108 : {
934 108 : for (unsigned int i = 0; i<ar_len; i++)
935 : {
936 0 : struct TALER_EXCHANGE_AccountRestriction *a = &ar[i];
937 0 : switch (a->type)
938 : {
939 0 : case TALER_EXCHANGE_AR_INVALID:
940 0 : GNUNET_break (0);
941 0 : break;
942 0 : case TALER_EXCHANGE_AR_DENY:
943 0 : break;
944 0 : case TALER_EXCHANGE_AR_REGEX:
945 0 : GNUNET_free (a->details.regex.posix_egrep);
946 0 : GNUNET_free (a->details.regex.human_hint);
947 0 : json_decref (a->details.regex.human_hint_i18n);
948 0 : break;
949 : }
950 : }
951 108 : }
952 :
953 :
954 : void
955 50 : TALER_EXCHANGE_free_accounts (
956 : unsigned int was_len,
957 : struct TALER_EXCHANGE_WireAccount was[static was_len])
958 50 : {
959 104 : for (unsigned int i = 0; i<was_len; i++)
960 : {
961 54 : struct TALER_EXCHANGE_WireAccount *wa = &was[i];
962 :
963 54 : GNUNET_free (wa->fpayto_uri.full_payto);
964 54 : GNUNET_free (wa->conversion_url);
965 54 : GNUNET_free (wa->open_banking_gateway);
966 54 : GNUNET_free (wa->prepared_transfer_url);
967 54 : GNUNET_free (wa->bank_label);
968 54 : free_restrictions (wa->credit_restrictions_length,
969 : wa->credit_restrictions);
970 54 : GNUNET_array_grow (wa->credit_restrictions,
971 : wa->credit_restrictions_length,
972 : 0);
973 54 : free_restrictions (wa->debit_restrictions_length,
974 : wa->debit_restrictions);
975 54 : GNUNET_array_grow (wa->debit_restrictions,
976 : wa->debit_restrictions_length,
977 : 0);
978 : }
979 50 : }
980 :
981 :
982 : enum GNUNET_GenericReturnValue
983 0 : TALER_EXCHANGE_keys_test_account_allowed (
984 : const struct TALER_EXCHANGE_Keys *keys,
985 : bool check_credit,
986 : const struct TALER_NormalizedPayto payto_uri)
987 : {
988 : /* For all accounts of the exchange */
989 0 : for (unsigned int i = 0; i<keys->accounts_len; i++)
990 : {
991 0 : const struct TALER_EXCHANGE_WireAccount *account
992 0 : = &keys->accounts[i];
993 :
994 : /* KYC auth transfers are never supported with conversion */
995 0 : if (NULL != account->conversion_url)
996 0 : continue;
997 : /* filter by source account by credit_restrictions */
998 0 : if (GNUNET_YES !=
999 0 : TALER_EXCHANGE_test_account_allowed (account,
1000 : check_credit,
1001 : payto_uri))
1002 0 : continue;
1003 : /* exchange account is allowed, add it */
1004 0 : return true;
1005 : }
1006 0 : return false;
1007 : }
1008 :
1009 :
1010 : /**
1011 : * We received an #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS response code.
1012 : * Parse the JSON response and initialize the @a uflr object.
1013 : *
1014 : * @param[out] uflr data structure to initialize
1015 : * @param j JSON response to parse
1016 : * @return #GNUNET_OK on success
1017 : */
1018 : enum GNUNET_GenericReturnValue
1019 20 : TALER_EXCHANGE_parse_451 (struct TALER_EXCHANGE_KycNeededRedirect *uflr,
1020 : const json_t *j)
1021 : {
1022 : struct GNUNET_JSON_Specification spec[] = {
1023 20 : GNUNET_JSON_spec_fixed_auto (
1024 : "h_payto",
1025 : &uflr->h_payto),
1026 20 : GNUNET_JSON_spec_uint64 (
1027 : "requirement_row",
1028 : &uflr->requirement_row),
1029 20 : GNUNET_JSON_spec_mark_optional (
1030 20 : GNUNET_JSON_spec_fixed_auto (
1031 : "account_pub",
1032 : &uflr->account_pub),
1033 : NULL),
1034 20 : GNUNET_JSON_spec_mark_optional (
1035 : GNUNET_JSON_spec_bool (
1036 : "bad_kyc_auth",
1037 : &uflr->bad_kyc_auth),
1038 : NULL),
1039 20 : GNUNET_JSON_spec_end ()
1040 : };
1041 :
1042 20 : if (GNUNET_OK !=
1043 20 : GNUNET_JSON_parse (j,
1044 : spec,
1045 : NULL,
1046 : NULL))
1047 : {
1048 0 : GNUNET_break_op (0);
1049 0 : return GNUNET_SYSERR;
1050 : }
1051 20 : return GNUNET_OK;
1052 : }
1053 :
1054 :
1055 : json_t *
1056 42 : TALER_EXCHANGE_recoup_coin_data_ (
1057 : size_t num_coins,
1058 : const struct TALER_EXCHANGE_RecoupCoin coins[static num_coins],
1059 : const struct TALER_BlindingMasterSeedP *blinding_seed,
1060 : bool for_melt,
1061 : struct TALER_CoinSpendPublicKeyP recouped_pubs[static num_coins],
1062 : struct TALER_EXCHANGE_RecoupedCoinInfo_ recouped_infos[static num_coins],
1063 : size_t *num_recouped)
1064 42 : {
1065 42 : json_t *arr = json_array ();
1066 :
1067 42 : GNUNET_assert (NULL != arr);
1068 42 : *num_recouped = 0;
1069 144 : for (size_t i = 0; i < num_coins; i++)
1070 : {
1071 102 : const struct TALER_EXCHANGE_RecoupCoin *c = &coins[i];
1072 : struct TALER_CoinSpendPrivateKeyP coin_priv;
1073 : struct TALER_CoinSpendPublicKeyP coin_pub;
1074 : union GNUNET_CRYPTO_BlindingSecretP bks;
1075 : union GNUNET_CRYPTO_BlindSessionNonce nonce;
1076 102 : const union GNUNET_CRYPTO_BlindSessionNonce *np = NULL;
1077 : json_t *entry;
1078 :
1079 102 : const struct TALER_ExchangeBlindingValues *alg_values
1080 : = c->exchange_vals;
1081 :
1082 102 : if ( (NULL == c->pk) ||
1083 102 : (NULL == c->ps) )
1084 : {
1085 0 : GNUNET_break (0);
1086 0 : json_decref (arr);
1087 0 : return NULL;
1088 : }
1089 102 : if (NULL == alg_values)
1090 : {
1091 : /* documented for RSA denominations, which have no
1092 : exchange-contributed blinding values */
1093 24 : if (GNUNET_CRYPTO_BSA_RSA != c->pk->key.bsign_pub_key->cipher)
1094 : {
1095 0 : GNUNET_break (0);
1096 0 : json_decref (arr);
1097 0 : return NULL;
1098 : }
1099 24 : alg_values = TALER_denom_ewv_rsa_singleton ();
1100 : }
1101 102 : TALER_planchet_setup_coin_priv (c->ps,
1102 : alg_values,
1103 : &coin_priv);
1104 102 : TALER_planchet_blinding_secret_create (c->ps,
1105 : alg_values,
1106 : &bks);
1107 102 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
1108 : &coin_pub.eddsa_pub);
1109 102 : if (GNUNET_CRYPTO_BSA_CS == c->pk->key.bsign_pub_key->cipher)
1110 : {
1111 51 : if (NULL == blinding_seed)
1112 : {
1113 0 : GNUNET_break (0);
1114 0 : json_decref (arr);
1115 0 : return NULL;
1116 : }
1117 51 : TALER_cs_nonce_derive_indexed (blinding_seed,
1118 : for_melt,
1119 : (uint32_t) i,
1120 : &nonce.cs_nonce);
1121 51 : np = &nonce;
1122 : }
1123 102 : if (NULL == c->denom_sig)
1124 : {
1125 : struct TALER_PlanchetDetail pd;
1126 : struct TALER_CoinPubHashP c_hash;
1127 : struct TALER_BlindedCoinHashP bch;
1128 :
1129 54 : if (GNUNET_OK !=
1130 54 : TALER_planchet_prepare (&c->pk->key,
1131 : alg_values,
1132 : &bks,
1133 : np,
1134 : &coin_priv,
1135 54 : c->h_age_commitment,
1136 : &c_hash,
1137 : &pd))
1138 : {
1139 0 : GNUNET_break (0);
1140 0 : json_decref (arr);
1141 0 : return NULL;
1142 : }
1143 54 : TALER_coin_ev_hash (&pd.blinded_planchet,
1144 54 : &c->pk->h_key,
1145 : &bch);
1146 54 : TALER_blinded_planchet_free (&pd.blinded_planchet);
1147 54 : entry = GNUNET_JSON_PACK (
1148 : GNUNET_JSON_pack_string ("type",
1149 : "hash"),
1150 : GNUNET_JSON_pack_data_auto ("h_coin_ev",
1151 : &bch));
1152 : }
1153 : else
1154 : {
1155 : struct TALER_CoinSpendSignatureP coin_sig;
1156 :
1157 48 : if (for_melt)
1158 20 : TALER_wallet_recoup_refresh_sign (&c->pk->h_key,
1159 : &bks,
1160 : &coin_priv,
1161 : &coin_sig);
1162 : else
1163 28 : TALER_wallet_recoup_sign (&c->pk->h_key,
1164 : &bks,
1165 : &coin_priv,
1166 : &coin_sig);
1167 48 : entry = GNUNET_JSON_PACK (
1168 : GNUNET_JSON_pack_string ("type",
1169 : "recoup"),
1170 : GNUNET_JSON_pack_data_auto ("coin_pub",
1171 : &coin_pub),
1172 : GNUNET_JSON_pack_data_auto ("denom_pub_h",
1173 : &c->pk->h_key),
1174 : TALER_JSON_pack_denom_sig ("denom_sig",
1175 : c->denom_sig),
1176 : GNUNET_JSON_pack_data_auto ("coin_blinding_secret",
1177 : &bks),
1178 : GNUNET_JSON_pack_allow_null (
1179 : GNUNET_JSON_pack_data_varsize ("h_age_commitment",
1180 : c->h_age_commitment,
1181 : (NULL == c->h_age_commitment)
1182 : ? 0
1183 : : sizeof (*c->h_age_commitment))),
1184 : GNUNET_JSON_pack_data_auto ("coin_sig",
1185 : &coin_sig));
1186 48 : recouped_pubs[*num_recouped] = coin_pub;
1187 48 : recouped_infos[*num_recouped].h_denom_pub = c->pk->h_key;
1188 48 : recouped_infos[*num_recouped].have_age = (NULL != c->h_age_commitment);
1189 48 : if (NULL != c->h_age_commitment)
1190 4 : recouped_infos[*num_recouped].h_age_commitment = *c->h_age_commitment;
1191 48 : (*num_recouped)++;
1192 : }
1193 102 : GNUNET_assert (0 ==
1194 : json_array_append_new (arr,
1195 : entry));
1196 : }
1197 42 : if (0 == *num_recouped)
1198 : {
1199 0 : GNUNET_break (0);
1200 0 : json_decref (arr);
1201 0 : return NULL;
1202 : }
1203 42 : return arr;
1204 : }
1205 :
1206 :
1207 : enum GNUNET_GenericReturnValue
1208 36 : TALER_EXCHANGE_parse_recoups_ (
1209 : const json_t *j_recoups,
1210 : size_t num_expected,
1211 : const struct TALER_CoinSpendPublicKeyP expected_pubs[static num_expected],
1212 : const struct TALER_Amount *total_amount,
1213 : struct TALER_RecoupedCoin **recoups,
1214 : struct GNUNET_HashCode *h_recoups)
1215 36 : {
1216 : struct TALER_RecoupedCoin *rcs;
1217 : struct TALER_Amount sum;
1218 : size_t idx;
1219 : json_t *entry;
1220 :
1221 36 : *recoups = NULL;
1222 72 : if ( (! json_is_array (j_recoups)) ||
1223 36 : (num_expected != json_array_size (j_recoups)) )
1224 : {
1225 0 : GNUNET_break_op (0);
1226 0 : return GNUNET_SYSERR;
1227 : }
1228 36 : rcs = GNUNET_new_array (num_expected,
1229 : struct TALER_RecoupedCoin);
1230 36 : GNUNET_assert (GNUNET_OK ==
1231 : TALER_amount_set_zero (total_amount->currency,
1232 : &sum));
1233 78 : json_array_foreach (j_recoups, idx, entry)
1234 : {
1235 : struct GNUNET_JSON_Specification spec[] = {
1236 42 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
1237 : &rcs[idx].coin_pub),
1238 42 : TALER_JSON_spec_amount_any ("amount",
1239 42 : &rcs[idx].amount),
1240 42 : GNUNET_JSON_spec_end ()
1241 : };
1242 :
1243 42 : if (GNUNET_OK !=
1244 42 : GNUNET_JSON_parse (entry,
1245 : spec,
1246 : NULL, NULL))
1247 : {
1248 0 : GNUNET_break_op (0);
1249 0 : GNUNET_free (rcs);
1250 0 : return GNUNET_SYSERR;
1251 : }
1252 42 : if (0 !=
1253 42 : GNUNET_memcmp (&rcs[idx].coin_pub,
1254 : &expected_pubs[idx]))
1255 : {
1256 0 : GNUNET_break_op (0);
1257 0 : GNUNET_free (rcs);
1258 0 : return GNUNET_SYSERR;
1259 : }
1260 42 : if ( (GNUNET_OK !=
1261 42 : TALER_amount_cmp_currency (&sum,
1262 84 : &rcs[idx].amount)) ||
1263 : (0 >
1264 42 : TALER_amount_add (&sum,
1265 : &sum,
1266 42 : &rcs[idx].amount)) )
1267 : {
1268 0 : GNUNET_break_op (0);
1269 0 : GNUNET_free (rcs);
1270 0 : return GNUNET_SYSERR;
1271 : }
1272 : }
1273 36 : if ( (GNUNET_OK !=
1274 36 : TALER_amount_cmp_currency (&sum,
1275 36 : total_amount)) ||
1276 : (0 !=
1277 36 : TALER_amount_cmp (&sum,
1278 : total_amount)) )
1279 : {
1280 0 : GNUNET_break_op (0);
1281 0 : GNUNET_free (rcs);
1282 0 : return GNUNET_SYSERR;
1283 : }
1284 36 : TALER_recoup_batch_hash (num_expected,
1285 : rcs,
1286 : h_recoups);
1287 36 : *recoups = rcs;
1288 36 : return GNUNET_OK;
1289 : }
1290 :
1291 :
1292 : /* end of exchange_api_common.c */
|