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