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_get-coins-COIN_PUB-history.c
19 : * @brief Implementation of the GET /coins/$COIN_PUB/history request
20 : * @author Christian Grothoff
21 : */
22 : #include <jansson.h>
23 : #include <microhttpd.h> /* just for HTTP status codes */
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler/taler_json_lib.h"
28 : #include "exchange_api_handle.h"
29 : #include "taler/taler_signatures.h"
30 : #include "exchange_api_curl_defaults.h"
31 :
32 :
33 : /**
34 : * @brief A GET /coins/$COIN_PUB/history Handle
35 : */
36 : struct TALER_EXCHANGE_GetCoinsHistoryHandle
37 : {
38 :
39 : /**
40 : * Base URL of the exchange.
41 : */
42 : char *base_url;
43 :
44 : /**
45 : * The url for this request.
46 : */
47 : char *url;
48 :
49 : /**
50 : * Handle for the request.
51 : */
52 : struct GNUNET_CURL_Job *job;
53 :
54 : /**
55 : * Function to call with the result.
56 : */
57 : TALER_EXCHANGE_GetCoinsHistoryCallback cb;
58 :
59 : /**
60 : * Closure for @e cb.
61 : */
62 : TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE *cb_cls;
63 :
64 : /**
65 : * CURL context to use.
66 : */
67 : struct GNUNET_CURL_Context *ctx;
68 :
69 : /**
70 : * Private key of the coin (for signing in _start).
71 : */
72 : struct TALER_CoinSpendPrivateKeyP coin_priv;
73 :
74 : /**
75 : * Public key of the coin we are querying.
76 : */
77 : struct TALER_CoinSpendPublicKeyP coin_pub;
78 :
79 : /**
80 : * Options set for this request.
81 : */
82 : struct
83 : {
84 : /**
85 : * Only return entries with history_offset > this value.
86 : * Default: 0 (return all entries).
87 : */
88 : uint64_t start_off;
89 : } options;
90 :
91 : };
92 :
93 :
94 : /**
95 : * Context for coin helpers.
96 : */
97 : struct CoinHistoryParseContext
98 : {
99 :
100 : /**
101 : * Keys of the exchange.
102 : */
103 : struct TALER_EXCHANGE_Keys *keys;
104 :
105 : /**
106 : * Denomination of the coin.
107 : */
108 : const struct TALER_EXCHANGE_DenomPublicKey *dk;
109 :
110 : /**
111 : * Our coin public key.
112 : */
113 : const struct TALER_CoinSpendPublicKeyP *coin_pub;
114 :
115 : /**
116 : * Where to sum up total refunds.
117 : */
118 : struct TALER_Amount *total_in;
119 :
120 : /**
121 : * Total amount encountered.
122 : */
123 : struct TALER_Amount *total_out;
124 :
125 : };
126 :
127 :
128 : /**
129 : * Signature of functions that operate on one of
130 : * the coin's history entries.
131 : *
132 : * @param[in,out] pc overall context
133 : * @param[out] rh where to write the history entry
134 : * @param amount main amount of this operation
135 : * @param transaction JSON details for the operation
136 : * @return #GNUNET_SYSERR on error,
137 : * #GNUNET_OK to add, #GNUNET_NO to subtract
138 : */
139 : typedef enum GNUNET_GenericReturnValue
140 : (*CoinCheckHelper)(struct CoinHistoryParseContext *pc,
141 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
142 : const struct TALER_Amount *amount,
143 : json_t *transaction);
144 :
145 :
146 : /**
147 : * Handle deposit entry in the coin's history.
148 : *
149 : * @param[in,out] pc overall context
150 : * @param[out] rh history entry to initialize
151 : * @param amount main amount of this operation
152 : * @param transaction JSON details for the operation
153 : * @return #GNUNET_SYSERR on error,
154 : * #GNUNET_OK to add, #GNUNET_NO to subtract
155 : */
156 : static enum GNUNET_GenericReturnValue
157 2 : help_deposit (struct CoinHistoryParseContext *pc,
158 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
159 : const struct TALER_Amount *amount,
160 : json_t *transaction)
161 : {
162 : struct GNUNET_JSON_Specification spec[] = {
163 2 : TALER_JSON_spec_amount_any ("deposit_fee",
164 : &rh->details.deposit.deposit_fee),
165 2 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
166 : &rh->details.deposit.merchant_pub),
167 2 : GNUNET_JSON_spec_timestamp ("timestamp",
168 : &rh->details.deposit.wallet_timestamp),
169 2 : GNUNET_JSON_spec_mark_optional (
170 : GNUNET_JSON_spec_timestamp ("refund_deadline",
171 : &rh->details.deposit.refund_deadline),
172 : NULL),
173 2 : GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
174 : &rh->details.deposit.h_contract_terms),
175 2 : GNUNET_JSON_spec_fixed_auto ("h_wire",
176 : &rh->details.deposit.h_wire),
177 2 : GNUNET_JSON_spec_mark_optional (
178 2 : GNUNET_JSON_spec_fixed_auto ("h_policy",
179 : &rh->details.deposit.h_policy),
180 : &rh->details.deposit.no_h_policy),
181 2 : GNUNET_JSON_spec_mark_optional (
182 2 : GNUNET_JSON_spec_fixed_auto ("wallet_data_hash",
183 : &rh->details.deposit.wallet_data_hash),
184 : &rh->details.deposit.no_wallet_data_hash),
185 2 : GNUNET_JSON_spec_mark_optional (
186 2 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
187 : &rh->details.deposit.hac),
188 : &rh->details.deposit.no_hac),
189 2 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
190 : &rh->details.deposit.sig),
191 2 : GNUNET_JSON_spec_end ()
192 : };
193 :
194 2 : rh->details.deposit.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS;
195 2 : if (GNUNET_OK !=
196 2 : GNUNET_JSON_parse (transaction,
197 : spec,
198 : NULL, NULL))
199 : {
200 0 : GNUNET_break_op (0);
201 0 : return GNUNET_SYSERR;
202 : }
203 2 : if (GNUNET_OK !=
204 6 : TALER_wallet_deposit_verify (
205 : amount,
206 2 : &rh->details.deposit.deposit_fee,
207 2 : &rh->details.deposit.h_wire,
208 2 : &rh->details.deposit.h_contract_terms,
209 2 : rh->details.deposit.no_wallet_data_hash
210 : ? NULL
211 : : &rh->details.deposit.wallet_data_hash,
212 2 : rh->details.deposit.no_hac
213 : ? NULL
214 : : &rh->details.deposit.hac,
215 2 : rh->details.deposit.no_h_policy
216 : ? NULL
217 : : &rh->details.deposit.h_policy,
218 2 : &pc->dk->h_key,
219 : rh->details.deposit.wallet_timestamp,
220 2 : &rh->details.deposit.merchant_pub,
221 : rh->details.deposit.refund_deadline,
222 : pc->coin_pub,
223 2 : &rh->details.deposit.sig))
224 : {
225 0 : GNUNET_break_op (0);
226 0 : return GNUNET_SYSERR;
227 : }
228 : /* check that deposit fee matches our expectations from /keys! */
229 2 : if ( (GNUNET_YES !=
230 2 : TALER_amount_cmp_currency (&rh->details.deposit.deposit_fee,
231 4 : &pc->dk->fees.deposit)) ||
232 : (0 !=
233 2 : TALER_amount_cmp (&rh->details.deposit.deposit_fee,
234 2 : &pc->dk->fees.deposit)) )
235 : {
236 0 : GNUNET_break_op (0);
237 0 : return GNUNET_SYSERR;
238 : }
239 2 : return GNUNET_YES;
240 : }
241 :
242 :
243 : /**
244 : * Handle melt entry in the coin's history.
245 : *
246 : * @param[in,out] pc overall context
247 : * @param[out] rh history entry to initialize
248 : * @param amount main amount of this operation
249 : * @param transaction JSON details for the operation
250 : * @return #GNUNET_SYSERR on error,
251 : * #GNUNET_OK to add, #GNUNET_NO to subtract
252 : */
253 : static enum GNUNET_GenericReturnValue
254 0 : help_melt (struct CoinHistoryParseContext *pc,
255 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
256 : const struct TALER_Amount *amount,
257 : json_t *transaction)
258 : {
259 : struct GNUNET_JSON_Specification spec[] = {
260 0 : TALER_JSON_spec_amount_any ("melt_fee",
261 : &rh->details.melt.melt_fee),
262 0 : GNUNET_JSON_spec_fixed_auto ("rc",
263 : &rh->details.melt.rc),
264 0 : GNUNET_JSON_spec_mark_optional (
265 0 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
266 : &rh->details.melt.h_age_commitment),
267 : &rh->details.melt.no_hac),
268 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
269 : &rh->details.melt.sig),
270 0 : GNUNET_JSON_spec_end ()
271 : };
272 :
273 0 : if (GNUNET_OK !=
274 0 : GNUNET_JSON_parse (transaction,
275 : spec,
276 : NULL, NULL))
277 : {
278 0 : GNUNET_break_op (0);
279 0 : return GNUNET_SYSERR;
280 : }
281 :
282 : /* check that melt fee matches our expectations from /keys! */
283 0 : if ( (GNUNET_YES !=
284 0 : TALER_amount_cmp_currency (&rh->details.melt.melt_fee,
285 0 : &pc->dk->fees.refresh)) ||
286 : (0 !=
287 0 : TALER_amount_cmp (&rh->details.melt.melt_fee,
288 0 : &pc->dk->fees.refresh)) )
289 : {
290 0 : GNUNET_break_op (0);
291 0 : return GNUNET_SYSERR;
292 : }
293 0 : if (GNUNET_OK !=
294 0 : TALER_wallet_melt_verify (
295 : amount,
296 0 : &rh->details.melt.melt_fee,
297 0 : &rh->details.melt.rc,
298 0 : &pc->dk->h_key,
299 0 : rh->details.melt.no_hac
300 : ? NULL
301 : : &rh->details.melt.h_age_commitment,
302 : pc->coin_pub,
303 0 : &rh->details.melt.sig))
304 : {
305 0 : GNUNET_break_op (0);
306 0 : return GNUNET_SYSERR;
307 : }
308 0 : return GNUNET_YES;
309 : }
310 :
311 :
312 : /**
313 : * Handle refund entry in the coin's history.
314 : *
315 : * @param[in,out] pc overall context
316 : * @param[out] rh history entry to initialize
317 : * @param amount main amount of this operation
318 : * @param transaction JSON details for the operation
319 : * @return #GNUNET_SYSERR on error,
320 : * #GNUNET_OK to add, #GNUNET_NO to subtract
321 : */
322 : static enum GNUNET_GenericReturnValue
323 0 : help_refund (struct CoinHistoryParseContext *pc,
324 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
325 : const struct TALER_Amount *amount,
326 : json_t *transaction)
327 : {
328 : struct GNUNET_JSON_Specification spec[] = {
329 0 : TALER_JSON_spec_amount_any ("refund_fee",
330 : &rh->details.refund.refund_fee),
331 0 : GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
332 : &rh->details.refund.h_contract_terms),
333 0 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
334 : &rh->details.refund.merchant_pub),
335 0 : GNUNET_JSON_spec_uint64 ("rtransaction_id",
336 : &rh->details.refund.rtransaction_id),
337 0 : GNUNET_JSON_spec_fixed_auto ("merchant_sig",
338 : &rh->details.refund.sig),
339 0 : GNUNET_JSON_spec_end ()
340 : };
341 :
342 0 : if (GNUNET_OK !=
343 0 : GNUNET_JSON_parse (transaction,
344 : spec,
345 : NULL, NULL))
346 : {
347 0 : GNUNET_break_op (0);
348 0 : return GNUNET_SYSERR;
349 : }
350 0 : if (0 >
351 0 : TALER_amount_add (&rh->details.refund.sig_amount,
352 0 : &rh->details.refund.refund_fee,
353 : amount))
354 : {
355 0 : GNUNET_break_op (0);
356 0 : return GNUNET_SYSERR;
357 : }
358 0 : if (GNUNET_OK !=
359 0 : TALER_merchant_refund_verify (pc->coin_pub,
360 0 : &rh->details.refund.h_contract_terms,
361 : rh->details.refund.rtransaction_id,
362 0 : &rh->details.refund.sig_amount,
363 0 : &rh->details.refund.merchant_pub,
364 0 : &rh->details.refund.sig))
365 : {
366 0 : GNUNET_break_op (0);
367 0 : return GNUNET_SYSERR;
368 : }
369 : /* check that refund fee matches our expectations from /keys! */
370 0 : if ( (GNUNET_YES !=
371 0 : TALER_amount_cmp_currency (&rh->details.refund.refund_fee,
372 0 : &pc->dk->fees.refund)) ||
373 : (0 !=
374 0 : TALER_amount_cmp (&rh->details.refund.refund_fee,
375 0 : &pc->dk->fees.refund)) )
376 : {
377 0 : GNUNET_break_op (0);
378 0 : return GNUNET_SYSERR;
379 : }
380 0 : return GNUNET_NO;
381 : }
382 :
383 :
384 : /**
385 : * Handle recoup entry in the coin's history.
386 : *
387 : * @param[in,out] pc overall context
388 : * @param[out] rh history entry to initialize
389 : * @param amount main amount of this operation
390 : * @param transaction JSON details for the operation
391 : * @return #GNUNET_SYSERR on error,
392 : * #GNUNET_OK to add, #GNUNET_NO to subtract
393 : */
394 : static enum GNUNET_GenericReturnValue
395 0 : help_recoup (struct CoinHistoryParseContext *pc,
396 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
397 : const struct TALER_Amount *amount,
398 : json_t *transaction)
399 : {
400 : struct GNUNET_JSON_Specification spec[] = {
401 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
402 : &rh->details.recoup.exchange_sig),
403 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
404 : &rh->details.recoup.exchange_pub),
405 0 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
406 : &rh->details.recoup.reserve_pub),
407 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
408 : &rh->details.recoup.coin_sig),
409 0 : GNUNET_JSON_spec_fixed_auto ("coin_blind",
410 : &rh->details.recoup.coin_bks),
411 0 : GNUNET_JSON_spec_timestamp ("timestamp",
412 : &rh->details.recoup.timestamp),
413 0 : GNUNET_JSON_spec_end ()
414 : };
415 :
416 0 : if (GNUNET_OK !=
417 0 : GNUNET_JSON_parse (transaction,
418 : spec,
419 : NULL, NULL))
420 : {
421 0 : GNUNET_break_op (0);
422 0 : return GNUNET_SYSERR;
423 : }
424 0 : if (GNUNET_OK !=
425 0 : TALER_exchange_online_confirm_recoup_verify (
426 : rh->details.recoup.timestamp,
427 : amount,
428 : pc->coin_pub,
429 0 : &rh->details.recoup.reserve_pub,
430 0 : &rh->details.recoup.exchange_pub,
431 0 : &rh->details.recoup.exchange_sig))
432 : {
433 0 : GNUNET_break_op (0);
434 0 : return GNUNET_SYSERR;
435 : }
436 0 : if (GNUNET_OK !=
437 0 : TALER_wallet_recoup_verify (&pc->dk->h_key,
438 0 : &rh->details.recoup.coin_bks,
439 : pc->coin_pub,
440 0 : &rh->details.recoup.coin_sig))
441 : {
442 0 : GNUNET_break_op (0);
443 0 : return GNUNET_SYSERR;
444 : }
445 0 : return GNUNET_YES;
446 : }
447 :
448 :
449 : /**
450 : * Handle recoup-refresh entry in the coin's history.
451 : * This is the coin that was subjected to a recoup,
452 : * the value being credited to the old coin.
453 : *
454 : * @param[in,out] pc overall context
455 : * @param[out] rh history entry to initialize
456 : * @param amount main amount of this operation
457 : * @param transaction JSON details for the operation
458 : * @return #GNUNET_SYSERR on error,
459 : * #GNUNET_OK to add, #GNUNET_NO to subtract
460 : */
461 : static enum GNUNET_GenericReturnValue
462 0 : help_recoup_refresh (struct CoinHistoryParseContext *pc,
463 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
464 : const struct TALER_Amount *amount,
465 : json_t *transaction)
466 : {
467 : struct GNUNET_JSON_Specification spec[] = {
468 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
469 : &rh->details.recoup_refresh.exchange_sig),
470 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
471 : &rh->details.recoup_refresh.exchange_pub),
472 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
473 : &rh->details.recoup_refresh.coin_sig),
474 0 : GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
475 : &rh->details.recoup_refresh.old_coin_pub),
476 0 : GNUNET_JSON_spec_fixed_auto ("coin_blind",
477 : &rh->details.recoup_refresh.coin_bks),
478 0 : GNUNET_JSON_spec_timestamp ("timestamp",
479 : &rh->details.recoup_refresh.timestamp),
480 0 : GNUNET_JSON_spec_end ()
481 : };
482 :
483 0 : if (GNUNET_OK !=
484 0 : GNUNET_JSON_parse (transaction,
485 : spec,
486 : NULL, NULL))
487 : {
488 0 : GNUNET_break_op (0);
489 0 : return GNUNET_SYSERR;
490 : }
491 0 : if (GNUNET_OK !=
492 0 : TALER_exchange_online_confirm_recoup_refresh_verify (
493 : rh->details.recoup_refresh.timestamp,
494 : amount,
495 : pc->coin_pub,
496 0 : &rh->details.recoup_refresh.old_coin_pub,
497 0 : &rh->details.recoup_refresh.exchange_pub,
498 0 : &rh->details.recoup_refresh.exchange_sig))
499 : {
500 0 : GNUNET_break_op (0);
501 0 : return GNUNET_SYSERR;
502 : }
503 0 : if (GNUNET_OK !=
504 0 : TALER_wallet_recoup_verify (&pc->dk->h_key,
505 0 : &rh->details.recoup_refresh.coin_bks,
506 : pc->coin_pub,
507 0 : &rh->details.recoup_refresh.coin_sig))
508 : {
509 0 : GNUNET_break_op (0);
510 0 : return GNUNET_SYSERR;
511 : }
512 0 : return GNUNET_YES;
513 : }
514 :
515 :
516 : /**
517 : * Handle old coin recoup entry in the coin's history.
518 : * This is the coin that was credited in a recoup,
519 : * the value being credited to this coin.
520 : *
521 : * @param[in,out] pc overall context
522 : * @param[out] rh history entry to initialize
523 : * @param amount main amount of this operation
524 : * @param transaction JSON details for the operation
525 : * @return #GNUNET_SYSERR on error,
526 : * #GNUNET_OK to add, #GNUNET_NO to subtract
527 : */
528 : static enum GNUNET_GenericReturnValue
529 0 : help_old_coin_recoup (struct CoinHistoryParseContext *pc,
530 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
531 : const struct TALER_Amount *amount,
532 : json_t *transaction)
533 : {
534 : struct GNUNET_JSON_Specification spec[] = {
535 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
536 : &rh->details.old_coin_recoup.exchange_sig),
537 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
538 : &rh->details.old_coin_recoup.exchange_pub),
539 0 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
540 : &rh->details.old_coin_recoup.new_coin_pub),
541 0 : GNUNET_JSON_spec_timestamp ("timestamp",
542 : &rh->details.old_coin_recoup.timestamp),
543 0 : GNUNET_JSON_spec_end ()
544 : };
545 :
546 0 : if (GNUNET_OK !=
547 0 : GNUNET_JSON_parse (transaction,
548 : spec,
549 : NULL, NULL))
550 : {
551 0 : GNUNET_break_op (0);
552 0 : return GNUNET_SYSERR;
553 : }
554 0 : if (GNUNET_OK !=
555 0 : TALER_exchange_online_confirm_recoup_refresh_verify (
556 : rh->details.old_coin_recoup.timestamp,
557 : amount,
558 0 : &rh->details.old_coin_recoup.new_coin_pub,
559 : pc->coin_pub,
560 0 : &rh->details.old_coin_recoup.exchange_pub,
561 0 : &rh->details.old_coin_recoup.exchange_sig))
562 : {
563 0 : GNUNET_break_op (0);
564 0 : return GNUNET_SYSERR;
565 : }
566 0 : return GNUNET_NO;
567 : }
568 :
569 :
570 : /**
571 : * Handle purse deposit entry in the coin's history.
572 : *
573 : * @param[in,out] pc overall context
574 : * @param[out] rh history entry to initialize
575 : * @param amount main amount of this operation
576 : * @param transaction JSON details for the operation
577 : * @return #GNUNET_SYSERR on error,
578 : * #GNUNET_OK to add, #GNUNET_NO to subtract
579 : */
580 : static enum GNUNET_GenericReturnValue
581 3 : help_purse_deposit (struct CoinHistoryParseContext *pc,
582 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
583 : const struct TALER_Amount *amount,
584 : json_t *transaction)
585 : {
586 : struct GNUNET_JSON_Specification spec[] = {
587 3 : TALER_JSON_spec_web_url ("exchange_base_url",
588 : &rh->details.purse_deposit.exchange_base_url),
589 3 : GNUNET_JSON_spec_mark_optional (
590 3 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
591 : &rh->details.purse_deposit.phac),
592 : NULL),
593 3 : GNUNET_JSON_spec_fixed_auto ("purse_pub",
594 : &rh->details.purse_deposit.purse_pub),
595 3 : GNUNET_JSON_spec_bool ("refunded",
596 : &rh->details.purse_deposit.refunded),
597 3 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
598 : &rh->details.purse_deposit.coin_sig),
599 3 : GNUNET_JSON_spec_end ()
600 : };
601 :
602 3 : if (GNUNET_OK !=
603 3 : GNUNET_JSON_parse (transaction,
604 : spec,
605 : NULL, NULL))
606 : {
607 0 : GNUNET_break_op (0);
608 0 : return GNUNET_SYSERR;
609 : }
610 3 : if (GNUNET_OK !=
611 3 : TALER_wallet_purse_deposit_verify (
612 : rh->details.purse_deposit.exchange_base_url,
613 3 : &rh->details.purse_deposit.purse_pub,
614 : amount,
615 3 : &pc->dk->h_key,
616 3 : &rh->details.purse_deposit.phac,
617 : pc->coin_pub,
618 3 : &rh->details.purse_deposit.coin_sig))
619 : {
620 0 : GNUNET_break_op (0);
621 0 : return GNUNET_SYSERR;
622 : }
623 3 : if (rh->details.purse_deposit.refunded)
624 : {
625 : /* We wave the deposit fee. */
626 0 : if (0 >
627 0 : TALER_amount_add (pc->total_in,
628 0 : pc->total_in,
629 0 : &pc->dk->fees.deposit))
630 : {
631 : /* overflow in refund history? inconceivable! Bad exchange! */
632 0 : GNUNET_break_op (0);
633 0 : return GNUNET_SYSERR;
634 : }
635 : }
636 3 : return GNUNET_YES;
637 : }
638 :
639 :
640 : /**
641 : * Handle purse refund entry in the coin's history.
642 : *
643 : * @param[in,out] pc overall context
644 : * @param[out] rh history entry to initialize
645 : * @param amount main amount of this operation
646 : * @param transaction JSON details for the operation
647 : * @return #GNUNET_SYSERR on error,
648 : * #GNUNET_OK to add, #GNUNET_NO to subtract
649 : */
650 : static enum GNUNET_GenericReturnValue
651 0 : help_purse_refund (struct CoinHistoryParseContext *pc,
652 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
653 : const struct TALER_Amount *amount,
654 : json_t *transaction)
655 : {
656 : struct GNUNET_JSON_Specification spec[] = {
657 0 : TALER_JSON_spec_amount_any ("refund_fee",
658 : &rh->details.purse_refund.refund_fee),
659 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
660 : &rh->details.purse_refund.exchange_sig),
661 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
662 : &rh->details.purse_refund.exchange_pub),
663 0 : GNUNET_JSON_spec_fixed_auto ("purse_pub",
664 : &rh->details.purse_refund.purse_pub),
665 0 : GNUNET_JSON_spec_end ()
666 : };
667 :
668 0 : if (GNUNET_OK !=
669 0 : GNUNET_JSON_parse (transaction,
670 : spec,
671 : NULL, NULL))
672 : {
673 0 : GNUNET_break_op (0);
674 0 : return GNUNET_SYSERR;
675 : }
676 0 : if (GNUNET_OK !=
677 0 : TALER_exchange_online_purse_refund_verify (
678 : amount,
679 0 : &rh->details.purse_refund.refund_fee,
680 : pc->coin_pub,
681 0 : &rh->details.purse_refund.purse_pub,
682 0 : &rh->details.purse_refund.exchange_pub,
683 0 : &rh->details.purse_refund.exchange_sig))
684 : {
685 0 : GNUNET_break_op (0);
686 0 : return GNUNET_SYSERR;
687 : }
688 0 : if ( (GNUNET_YES !=
689 0 : TALER_amount_cmp_currency (&rh->details.purse_refund.refund_fee,
690 0 : &pc->dk->fees.refund)) ||
691 : (0 !=
692 0 : TALER_amount_cmp (&rh->details.purse_refund.refund_fee,
693 0 : &pc->dk->fees.refund)) )
694 : {
695 0 : GNUNET_break_op (0);
696 0 : return GNUNET_SYSERR;
697 : }
698 0 : return GNUNET_NO;
699 : }
700 :
701 :
702 : /**
703 : * Handle reserve deposit entry in the coin's history.
704 : *
705 : * @param[in,out] pc overall context
706 : * @param[out] rh history entry to initialize
707 : * @param amount main amount of this operation
708 : * @param transaction JSON details for the operation
709 : * @return #GNUNET_SYSERR on error,
710 : * #GNUNET_OK to add, #GNUNET_NO to subtract
711 : */
712 : static enum GNUNET_GenericReturnValue
713 0 : help_reserve_open_deposit (struct CoinHistoryParseContext *pc,
714 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
715 : const struct TALER_Amount *amount,
716 : json_t *transaction)
717 : {
718 : struct GNUNET_JSON_Specification spec[] = {
719 0 : GNUNET_JSON_spec_fixed_auto (
720 : "reserve_sig",
721 : &rh->details.reserve_open_deposit.reserve_sig),
722 0 : GNUNET_JSON_spec_fixed_auto (
723 : "coin_sig",
724 : &rh->details.reserve_open_deposit.coin_sig),
725 0 : TALER_JSON_spec_amount_any (
726 : "coin_contribution",
727 : &rh->details.reserve_open_deposit.coin_contribution),
728 0 : GNUNET_JSON_spec_mark_optional (
729 0 : GNUNET_JSON_spec_fixed_auto (
730 : "h_age_commitment",
731 : &rh->details.reserve_open_deposit.h_age_commitment),
732 : &rh->details.reserve_open_deposit.no_hac),
733 0 : GNUNET_JSON_spec_end ()
734 : };
735 :
736 0 : if (GNUNET_OK !=
737 0 : GNUNET_JSON_parse (transaction,
738 : spec,
739 : NULL, NULL))
740 : {
741 0 : GNUNET_break_op (0);
742 0 : return GNUNET_SYSERR;
743 : }
744 0 : if (GNUNET_OK !=
745 0 : TALER_wallet_reserve_open_deposit_verify (
746 : amount,
747 0 : &pc->dk->h_key,
748 0 : rh->details.reserve_open_deposit.no_hac
749 : ? NULL
750 : : &rh->details.reserve_open_deposit.h_age_commitment,
751 0 : &rh->details.reserve_open_deposit.reserve_sig,
752 : pc->coin_pub,
753 0 : &rh->details.reserve_open_deposit.coin_sig))
754 : {
755 0 : GNUNET_break_op (0);
756 0 : return GNUNET_SYSERR;
757 : }
758 0 : return GNUNET_YES;
759 : }
760 :
761 :
762 : enum GNUNET_GenericReturnValue
763 4 : TALER_EXCHANGE_parse_coin_history (
764 : const struct TALER_EXCHANGE_Keys *keys,
765 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
766 : const json_t *history,
767 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
768 : struct TALER_Amount *total_in,
769 : struct TALER_Amount *total_out,
770 : unsigned int rlen,
771 : struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen])
772 4 : {
773 : const struct
774 : {
775 : const char *type;
776 : CoinCheckHelper helper;
777 : enum TALER_EXCHANGE_CoinTransactionType ctt;
778 4 : } map[] = {
779 : { "DEPOSIT",
780 : &help_deposit,
781 : TALER_EXCHANGE_CTT_DEPOSIT },
782 : { "MELT",
783 : &help_melt,
784 : TALER_EXCHANGE_CTT_MELT },
785 : { "REFUND",
786 : &help_refund,
787 : TALER_EXCHANGE_CTT_REFUND },
788 : { "RECOUP-WITHDRAW",
789 : &help_recoup,
790 : TALER_EXCHANGE_CTT_RECOUP },
791 : { "RECOUP-REFRESH",
792 : &help_recoup_refresh,
793 : TALER_EXCHANGE_CTT_RECOUP_REFRESH },
794 : { "RECOUP-REFRESH-RECEIVER",
795 : &help_old_coin_recoup,
796 : TALER_EXCHANGE_CTT_OLD_COIN_RECOUP },
797 : { "PURSE-DEPOSIT",
798 : &help_purse_deposit,
799 : TALER_EXCHANGE_CTT_PURSE_DEPOSIT },
800 : { "PURSE-REFUND",
801 : &help_purse_refund,
802 : TALER_EXCHANGE_CTT_PURSE_REFUND },
803 : { "RESERVE-OPEN-DEPOSIT",
804 : &help_reserve_open_deposit,
805 : TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT },
806 : { NULL, NULL, TALER_EXCHANGE_CTT_NONE }
807 : };
808 4 : struct CoinHistoryParseContext pc = {
809 : .dk = dk,
810 : .coin_pub = coin_pub,
811 : .total_out = total_out,
812 : .total_in = total_in
813 : };
814 : size_t len;
815 :
816 4 : if (NULL == history)
817 : {
818 0 : GNUNET_break_op (0);
819 0 : return GNUNET_SYSERR;
820 : }
821 4 : len = json_array_size (history);
822 4 : if (0 == len)
823 : {
824 0 : GNUNET_break_op (0);
825 0 : return GNUNET_SYSERR;
826 : }
827 4 : *total_in = dk->value;
828 4 : GNUNET_assert (GNUNET_OK ==
829 : TALER_amount_set_zero (total_in->currency,
830 : total_out));
831 9 : for (size_t off = 0; off < len; off++)
832 : {
833 5 : struct TALER_EXCHANGE_CoinHistoryEntry *rh = &rhistory[off];
834 5 : json_t *transaction = json_array_get (history,
835 : off);
836 : enum GNUNET_GenericReturnValue add;
837 : const char *type;
838 : struct GNUNET_JSON_Specification spec_glob[] = {
839 5 : TALER_JSON_spec_amount_any ("amount",
840 : &rh->amount),
841 5 : GNUNET_JSON_spec_string ("type",
842 : &type),
843 5 : GNUNET_JSON_spec_uint64 ("history_offset",
844 : &rh->history_offset),
845 5 : GNUNET_JSON_spec_end ()
846 : };
847 :
848 5 : if (GNUNET_OK !=
849 5 : GNUNET_JSON_parse (transaction,
850 : spec_glob,
851 : NULL, NULL))
852 : {
853 0 : GNUNET_break_op (0);
854 0 : return GNUNET_SYSERR;
855 : }
856 5 : if (GNUNET_YES !=
857 5 : TALER_amount_cmp_currency (&rh->amount,
858 : total_in))
859 : {
860 0 : GNUNET_break_op (0);
861 0 : return GNUNET_SYSERR;
862 : }
863 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
864 : "Operation of type %s with amount %s\n",
865 : type,
866 : TALER_amount2s (&rh->amount));
867 5 : add = GNUNET_SYSERR;
868 23 : for (unsigned int i = 0; NULL != map[i].type; i++)
869 : {
870 23 : if (0 == strcasecmp (type,
871 23 : map[i].type))
872 : {
873 5 : rh->type = map[i].ctt;
874 5 : add = map[i].helper (&pc,
875 : rh,
876 5 : &rh->amount,
877 : transaction);
878 5 : break;
879 : }
880 : }
881 5 : switch (add)
882 : {
883 0 : case GNUNET_SYSERR:
884 : /* entry type not supported, new version on server? */
885 0 : rh->type = TALER_EXCHANGE_CTT_NONE;
886 0 : GNUNET_break_op (0);
887 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
888 : "Unexpected type `%s' in response\n",
889 : type);
890 0 : return GNUNET_SYSERR;
891 5 : case GNUNET_YES:
892 : /* This amount should be debited from the coin */
893 5 : if (0 >
894 5 : TALER_amount_add (total_out,
895 : total_out,
896 5 : &rh->amount))
897 : {
898 : /* overflow in history already!? inconceivable! Bad exchange! */
899 0 : GNUNET_break_op (0);
900 0 : return GNUNET_SYSERR;
901 : }
902 5 : break;
903 0 : case GNUNET_NO:
904 : /* This amount should be credited to the coin. */
905 0 : if (0 >
906 0 : TALER_amount_add (total_in,
907 : total_in,
908 0 : &rh->amount))
909 : {
910 : /* overflow in refund history? inconceivable! Bad exchange! */
911 0 : GNUNET_break_op (0);
912 0 : return GNUNET_SYSERR;
913 : }
914 0 : break;
915 : } /* end of switch(add) */
916 : }
917 4 : return GNUNET_OK;
918 : }
919 :
920 :
921 : /**
922 : * We received an #MHD_HTTP_OK response. Handle the JSON response.
923 : *
924 : * @param gcsh handle of the request
925 : * @param j JSON response
926 : * @return #GNUNET_OK on success
927 : */
928 : static enum GNUNET_GenericReturnValue
929 4 : handle_coins_history_ok (struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
930 : const json_t *j)
931 : {
932 4 : struct TALER_EXCHANGE_GetCoinsHistoryResponse rs = {
933 : .hr.reply = j,
934 : .hr.http_status = MHD_HTTP_OK
935 : };
936 : struct GNUNET_JSON_Specification spec[] = {
937 4 : TALER_JSON_spec_amount_any ("balance",
938 : &rs.details.ok.balance),
939 4 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
940 : &rs.details.ok.h_denom_pub),
941 4 : GNUNET_JSON_spec_array_const ("history",
942 : &rs.details.ok.history),
943 4 : GNUNET_JSON_spec_end ()
944 : };
945 :
946 4 : if (GNUNET_OK !=
947 4 : GNUNET_JSON_parse (j,
948 : spec,
949 : NULL,
950 : NULL))
951 : {
952 0 : GNUNET_break_op (0);
953 0 : return GNUNET_SYSERR;
954 : }
955 4 : if (NULL != gcsh->cb)
956 : {
957 4 : gcsh->cb (gcsh->cb_cls,
958 : &rs);
959 4 : gcsh->cb = NULL;
960 : }
961 4 : GNUNET_JSON_parse_free (spec);
962 4 : return GNUNET_OK;
963 : }
964 :
965 :
966 : /**
967 : * Function called when we're done processing the
968 : * HTTP GET /coins/$COIN_PUB/history request.
969 : *
970 : * @param cls the `struct TALER_EXCHANGE_GetCoinsHistoryHandle`
971 : * @param response_code HTTP response code, 0 on error
972 : * @param response parsed JSON result, NULL on error
973 : */
974 : static void
975 4 : handle_coins_history_finished (void *cls,
976 : long response_code,
977 : const void *response)
978 : {
979 4 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh = cls;
980 4 : const json_t *j = response;
981 4 : struct TALER_EXCHANGE_GetCoinsHistoryResponse rs = {
982 : .hr.reply = j,
983 4 : .hr.http_status = (unsigned int) response_code
984 : };
985 :
986 4 : gcsh->job = NULL;
987 4 : switch (response_code)
988 : {
989 0 : case 0:
990 0 : rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
991 0 : break;
992 4 : case MHD_HTTP_OK:
993 4 : if (GNUNET_OK !=
994 4 : handle_coins_history_ok (gcsh,
995 : j))
996 : {
997 0 : rs.hr.http_status = 0;
998 0 : rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
999 : }
1000 4 : break;
1001 0 : case MHD_HTTP_NO_CONTENT:
1002 0 : break;
1003 0 : case MHD_HTTP_NOT_MODIFIED:
1004 0 : break;
1005 0 : case MHD_HTTP_BAD_REQUEST:
1006 : /* This should never happen, either us or the exchange is buggy
1007 : (or API version conflict); just pass JSON reply to the application */
1008 0 : GNUNET_break (0);
1009 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1010 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1011 0 : break;
1012 0 : case MHD_HTTP_FORBIDDEN:
1013 : /* This should never happen, either us or the exchange is buggy
1014 : (or API version conflict); just pass JSON reply to the application */
1015 0 : GNUNET_break (0);
1016 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1017 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1018 0 : break;
1019 0 : case MHD_HTTP_NOT_FOUND:
1020 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1021 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1022 0 : break;
1023 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
1024 : /* Server had an internal issue; we should retry, but this API
1025 : leaves this to the application */
1026 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1027 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1028 0 : break;
1029 0 : default:
1030 : /* unexpected response code */
1031 0 : GNUNET_break_op (0);
1032 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1033 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1034 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1035 : "Unexpected response code %u/%d for coins history\n",
1036 : (unsigned int) response_code,
1037 : (int) rs.hr.ec);
1038 0 : break;
1039 : }
1040 4 : if (NULL != gcsh->cb)
1041 : {
1042 0 : gcsh->cb (gcsh->cb_cls,
1043 : &rs);
1044 0 : gcsh->cb = NULL;
1045 : }
1046 4 : TALER_EXCHANGE_get_coins_history_cancel (gcsh);
1047 4 : }
1048 :
1049 :
1050 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *
1051 4 : TALER_EXCHANGE_get_coins_history_create (
1052 : struct GNUNET_CURL_Context *ctx,
1053 : const char *url,
1054 : const struct TALER_CoinSpendPrivateKeyP *coin_priv)
1055 : {
1056 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh;
1057 :
1058 4 : gcsh = GNUNET_new (struct TALER_EXCHANGE_GetCoinsHistoryHandle);
1059 4 : gcsh->ctx = ctx;
1060 4 : gcsh->base_url = GNUNET_strdup (url);
1061 4 : gcsh->coin_priv = *coin_priv;
1062 4 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
1063 : &gcsh->coin_pub.eddsa_pub);
1064 4 : gcsh->options.start_off = 0;
1065 4 : return gcsh;
1066 : }
1067 :
1068 :
1069 : enum GNUNET_GenericReturnValue
1070 0 : TALER_EXCHANGE_get_coins_history_set_options_ (
1071 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
1072 : unsigned int num_options,
1073 : const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *options)
1074 : {
1075 0 : for (unsigned int i = 0; i < num_options; i++)
1076 : {
1077 0 : const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *opt = &options[i];
1078 :
1079 0 : switch (opt->option)
1080 : {
1081 0 : case TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_END:
1082 0 : return GNUNET_OK;
1083 0 : case TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_START_OFF:
1084 0 : gcsh->options.start_off = opt->details.start_off;
1085 0 : break;
1086 : }
1087 : }
1088 0 : return GNUNET_OK;
1089 : }
1090 :
1091 :
1092 : enum TALER_ErrorCode
1093 4 : TALER_EXCHANGE_get_coins_history_start (
1094 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
1095 : TALER_EXCHANGE_GetCoinsHistoryCallback cb,
1096 : TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE *cb_cls)
1097 : {
1098 : char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 64];
1099 : struct curl_slist *job_headers;
1100 : CURL *eh;
1101 :
1102 4 : if (NULL != gcsh->job)
1103 : {
1104 0 : GNUNET_break (0);
1105 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1106 : }
1107 4 : gcsh->cb = cb;
1108 4 : gcsh->cb_cls = cb_cls;
1109 : {
1110 : char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
1111 : char *end;
1112 :
1113 4 : end = GNUNET_STRINGS_data_to_string (
1114 4 : &gcsh->coin_pub,
1115 : sizeof (gcsh->coin_pub),
1116 : pub_str,
1117 : sizeof (pub_str));
1118 4 : *end = '\0';
1119 4 : if (0 != gcsh->options.start_off)
1120 0 : GNUNET_snprintf (arg_str,
1121 : sizeof (arg_str),
1122 : "coins/%s/history?start=%llu",
1123 : pub_str,
1124 0 : (unsigned long long) gcsh->options.start_off);
1125 : else
1126 4 : GNUNET_snprintf (arg_str,
1127 : sizeof (arg_str),
1128 : "coins/%s/history",
1129 : pub_str);
1130 : }
1131 4 : gcsh->url = TALER_url_join (gcsh->base_url,
1132 : arg_str,
1133 : NULL);
1134 4 : if (NULL == gcsh->url)
1135 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
1136 4 : eh = TALER_EXCHANGE_curl_easy_get_ (gcsh->url);
1137 4 : if (NULL == eh)
1138 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
1139 : {
1140 : struct TALER_CoinSpendSignatureP coin_sig;
1141 : char *sig_hdr;
1142 : char *hdr;
1143 :
1144 4 : TALER_wallet_coin_history_sign (gcsh->options.start_off,
1145 4 : &gcsh->coin_priv,
1146 : &coin_sig);
1147 4 : sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
1148 : &coin_sig,
1149 : sizeof (coin_sig));
1150 4 : GNUNET_asprintf (&hdr,
1151 : "%s: %s",
1152 : TALER_COIN_HISTORY_SIGNATURE_HEADER,
1153 : sig_hdr);
1154 4 : GNUNET_free (sig_hdr);
1155 4 : job_headers = curl_slist_append (NULL,
1156 : hdr);
1157 4 : GNUNET_free (hdr);
1158 4 : if (NULL == job_headers)
1159 : {
1160 0 : GNUNET_break (0);
1161 0 : curl_easy_cleanup (eh);
1162 0 : GNUNET_free (gcsh->url);
1163 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1164 : }
1165 : }
1166 4 : gcsh->job = GNUNET_CURL_job_add2 (gcsh->ctx,
1167 : eh,
1168 : job_headers,
1169 : &handle_coins_history_finished,
1170 : gcsh);
1171 4 : curl_slist_free_all (job_headers);
1172 4 : if (NULL == gcsh->job)
1173 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1174 4 : return TALER_EC_NONE;
1175 : }
1176 :
1177 :
1178 : void
1179 4 : TALER_EXCHANGE_get_coins_history_cancel (
1180 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh)
1181 : {
1182 4 : if (NULL != gcsh->job)
1183 : {
1184 0 : GNUNET_CURL_job_cancel (gcsh->job);
1185 0 : gcsh->job = NULL;
1186 : }
1187 4 : GNUNET_free (gcsh->url);
1188 4 : GNUNET_free (gcsh->base_url);
1189 4 : GNUNET_free (gcsh);
1190 4 : }
1191 :
1192 :
1193 : enum GNUNET_GenericReturnValue
1194 0 : TALER_EXCHANGE_check_coin_signature_conflict (
1195 : const json_t *history,
1196 : const struct TALER_CoinSpendSignatureP *coin_sig)
1197 : {
1198 : size_t off;
1199 : json_t *entry;
1200 :
1201 0 : json_array_foreach (history, off, entry)
1202 : {
1203 : struct TALER_CoinSpendSignatureP cs;
1204 : struct GNUNET_JSON_Specification spec[] = {
1205 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
1206 : &cs),
1207 0 : GNUNET_JSON_spec_end ()
1208 : };
1209 :
1210 0 : if (GNUNET_OK !=
1211 0 : GNUNET_JSON_parse (entry,
1212 : spec,
1213 : NULL, NULL))
1214 0 : continue; /* entry without coin signature */
1215 0 : if (0 ==
1216 0 : GNUNET_memcmp (&cs,
1217 : coin_sig))
1218 : {
1219 0 : GNUNET_break_op (0);
1220 0 : return GNUNET_SYSERR;
1221 : }
1222 : }
1223 0 : return GNUNET_OK;
1224 : }
1225 :
1226 :
1227 : #if FIXME_IMPLEMENT /* #9422 */
1228 : /**
1229 : * FIXME-Oec-#9422: we need some specific routines that show
1230 : * that certain coin operations are indeed in conflict,
1231 : * for example that the coin is of a different denomination
1232 : * or different age restrictions.
1233 : * This relates to unimplemented error handling for
1234 : * coins in the exchange!
1235 : *
1236 : * Check that the provided @a proof indeeds indicates
1237 : * a conflict for @a coin_pub.
1238 : *
1239 : * @param keys exchange keys
1240 : * @param proof provided conflict proof
1241 : * @param dk denomination of @a coin_pub that the client
1242 : * used
1243 : * @param coin_pub public key of the coin
1244 : * @param required balance required on the coin for the operation
1245 : * @return #GNUNET_OK if @a proof holds
1246 : */
1247 : // FIXME-#9422: should be properly defined and implemented!
1248 : enum GNUNET_GenericReturnValue
1249 : TALER_EXCHANGE_check_coin_conflict_ (
1250 : const struct TALER_EXCHANGE_Keys *keys,
1251 : const json_t *proof,
1252 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
1253 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1254 : const struct TALER_Amount *required)
1255 : {
1256 : enum TALER_ErrorCode ec;
1257 :
1258 : ec = TALER_JSON_get_error_code (proof);
1259 : switch (ec)
1260 : {
1261 : case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
1262 : /* Nothing to check anymore here, proof needs to be
1263 : checked in the GET /coins/$COIN_PUB handler */
1264 : break;
1265 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
1266 : // FIXME-#9422: write check!
1267 : break;
1268 : default:
1269 : GNUNET_break_op (0);
1270 : return GNUNET_SYSERR;
1271 : }
1272 : return GNUNET_OK;
1273 : }
1274 :
1275 :
1276 : #endif
1277 :
1278 :
1279 : /* end of exchange_api_get-coins-COIN_PUB-history.c */
|