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 ("reserve_sig",
720 : &rh->details.reserve_open_deposit.reserve_sig),
721 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
722 : &rh->details.reserve_open_deposit.coin_sig),
723 0 : TALER_JSON_spec_amount_any ("coin_contribution",
724 : &rh->details.reserve_open_deposit.
725 : coin_contribution),
726 0 : GNUNET_JSON_spec_end ()
727 : };
728 :
729 0 : if (GNUNET_OK !=
730 0 : GNUNET_JSON_parse (transaction,
731 : spec,
732 : NULL, NULL))
733 : {
734 0 : GNUNET_break_op (0);
735 0 : return GNUNET_SYSERR;
736 : }
737 0 : if (GNUNET_OK !=
738 0 : TALER_wallet_reserve_open_deposit_verify (
739 : amount,
740 0 : &rh->details.reserve_open_deposit.reserve_sig,
741 : pc->coin_pub,
742 0 : &rh->details.reserve_open_deposit.coin_sig))
743 : {
744 0 : GNUNET_break_op (0);
745 0 : return GNUNET_SYSERR;
746 : }
747 0 : return GNUNET_YES;
748 : }
749 :
750 :
751 : enum GNUNET_GenericReturnValue
752 4 : TALER_EXCHANGE_parse_coin_history (
753 : const struct TALER_EXCHANGE_Keys *keys,
754 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
755 : const json_t *history,
756 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
757 : struct TALER_Amount *total_in,
758 : struct TALER_Amount *total_out,
759 : unsigned int rlen,
760 : struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen])
761 4 : {
762 : const struct
763 : {
764 : const char *type;
765 : CoinCheckHelper helper;
766 : enum TALER_EXCHANGE_CoinTransactionType ctt;
767 4 : } map[] = {
768 : { "DEPOSIT",
769 : &help_deposit,
770 : TALER_EXCHANGE_CTT_DEPOSIT },
771 : { "MELT",
772 : &help_melt,
773 : TALER_EXCHANGE_CTT_MELT },
774 : { "REFUND",
775 : &help_refund,
776 : TALER_EXCHANGE_CTT_REFUND },
777 : { "RECOUP-WITHDRAW",
778 : &help_recoup,
779 : TALER_EXCHANGE_CTT_RECOUP },
780 : { "RECOUP-REFRESH",
781 : &help_recoup_refresh,
782 : TALER_EXCHANGE_CTT_RECOUP_REFRESH },
783 : { "RECOUP-REFRESH-RECEIVER",
784 : &help_old_coin_recoup,
785 : TALER_EXCHANGE_CTT_OLD_COIN_RECOUP },
786 : { "PURSE-DEPOSIT",
787 : &help_purse_deposit,
788 : TALER_EXCHANGE_CTT_PURSE_DEPOSIT },
789 : { "PURSE-REFUND",
790 : &help_purse_refund,
791 : TALER_EXCHANGE_CTT_PURSE_REFUND },
792 : { "RESERVE-OPEN-DEPOSIT",
793 : &help_reserve_open_deposit,
794 : TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT },
795 : { NULL, NULL, TALER_EXCHANGE_CTT_NONE }
796 : };
797 4 : struct CoinHistoryParseContext pc = {
798 : .dk = dk,
799 : .coin_pub = coin_pub,
800 : .total_out = total_out,
801 : .total_in = total_in
802 : };
803 : size_t len;
804 :
805 4 : if (NULL == history)
806 : {
807 0 : GNUNET_break_op (0);
808 0 : return GNUNET_SYSERR;
809 : }
810 4 : len = json_array_size (history);
811 4 : if (0 == len)
812 : {
813 0 : GNUNET_break_op (0);
814 0 : return GNUNET_SYSERR;
815 : }
816 4 : *total_in = dk->value;
817 4 : GNUNET_assert (GNUNET_OK ==
818 : TALER_amount_set_zero (total_in->currency,
819 : total_out));
820 9 : for (size_t off = 0; off < len; off++)
821 : {
822 5 : struct TALER_EXCHANGE_CoinHistoryEntry *rh = &rhistory[off];
823 5 : json_t *transaction = json_array_get (history,
824 : off);
825 : enum GNUNET_GenericReturnValue add;
826 : const char *type;
827 : struct GNUNET_JSON_Specification spec_glob[] = {
828 5 : TALER_JSON_spec_amount_any ("amount",
829 : &rh->amount),
830 5 : GNUNET_JSON_spec_string ("type",
831 : &type),
832 5 : GNUNET_JSON_spec_uint64 ("history_offset",
833 : &rh->history_offset),
834 5 : GNUNET_JSON_spec_end ()
835 : };
836 :
837 5 : if (GNUNET_OK !=
838 5 : GNUNET_JSON_parse (transaction,
839 : spec_glob,
840 : NULL, NULL))
841 : {
842 0 : GNUNET_break_op (0);
843 0 : return GNUNET_SYSERR;
844 : }
845 5 : if (GNUNET_YES !=
846 5 : TALER_amount_cmp_currency (&rh->amount,
847 : total_in))
848 : {
849 0 : GNUNET_break_op (0);
850 0 : return GNUNET_SYSERR;
851 : }
852 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
853 : "Operation of type %s with amount %s\n",
854 : type,
855 : TALER_amount2s (&rh->amount));
856 5 : add = GNUNET_SYSERR;
857 23 : for (unsigned int i = 0; NULL != map[i].type; i++)
858 : {
859 23 : if (0 == strcasecmp (type,
860 23 : map[i].type))
861 : {
862 5 : rh->type = map[i].ctt;
863 5 : add = map[i].helper (&pc,
864 : rh,
865 5 : &rh->amount,
866 : transaction);
867 5 : break;
868 : }
869 : }
870 5 : switch (add)
871 : {
872 0 : case GNUNET_SYSERR:
873 : /* entry type not supported, new version on server? */
874 0 : rh->type = TALER_EXCHANGE_CTT_NONE;
875 0 : GNUNET_break_op (0);
876 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
877 : "Unexpected type `%s' in response\n",
878 : type);
879 0 : return GNUNET_SYSERR;
880 5 : case GNUNET_YES:
881 : /* This amount should be debited from the coin */
882 5 : if (0 >
883 5 : TALER_amount_add (total_out,
884 : total_out,
885 5 : &rh->amount))
886 : {
887 : /* overflow in history already!? inconceivable! Bad exchange! */
888 0 : GNUNET_break_op (0);
889 0 : return GNUNET_SYSERR;
890 : }
891 5 : break;
892 0 : case GNUNET_NO:
893 : /* This amount should be credited to the coin. */
894 0 : if (0 >
895 0 : TALER_amount_add (total_in,
896 : total_in,
897 0 : &rh->amount))
898 : {
899 : /* overflow in refund history? inconceivable! Bad exchange! */
900 0 : GNUNET_break_op (0);
901 0 : return GNUNET_SYSERR;
902 : }
903 0 : break;
904 : } /* end of switch(add) */
905 : }
906 4 : return GNUNET_OK;
907 : }
908 :
909 :
910 : /**
911 : * We received an #MHD_HTTP_OK response. Handle the JSON response.
912 : *
913 : * @param gcsh handle of the request
914 : * @param j JSON response
915 : * @return #GNUNET_OK on success
916 : */
917 : static enum GNUNET_GenericReturnValue
918 4 : handle_coins_history_ok (struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
919 : const json_t *j)
920 : {
921 4 : struct TALER_EXCHANGE_GetCoinsHistoryResponse rs = {
922 : .hr.reply = j,
923 : .hr.http_status = MHD_HTTP_OK
924 : };
925 : struct GNUNET_JSON_Specification spec[] = {
926 4 : TALER_JSON_spec_amount_any ("balance",
927 : &rs.details.ok.balance),
928 4 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
929 : &rs.details.ok.h_denom_pub),
930 4 : GNUNET_JSON_spec_array_const ("history",
931 : &rs.details.ok.history),
932 4 : GNUNET_JSON_spec_end ()
933 : };
934 :
935 4 : if (GNUNET_OK !=
936 4 : GNUNET_JSON_parse (j,
937 : spec,
938 : NULL,
939 : NULL))
940 : {
941 0 : GNUNET_break_op (0);
942 0 : return GNUNET_SYSERR;
943 : }
944 4 : if (NULL != gcsh->cb)
945 : {
946 4 : gcsh->cb (gcsh->cb_cls,
947 : &rs);
948 4 : gcsh->cb = NULL;
949 : }
950 4 : GNUNET_JSON_parse_free (spec);
951 4 : return GNUNET_OK;
952 : }
953 :
954 :
955 : /**
956 : * Function called when we're done processing the
957 : * HTTP GET /coins/$COIN_PUB/history request.
958 : *
959 : * @param cls the `struct TALER_EXCHANGE_GetCoinsHistoryHandle`
960 : * @param response_code HTTP response code, 0 on error
961 : * @param response parsed JSON result, NULL on error
962 : */
963 : static void
964 4 : handle_coins_history_finished (void *cls,
965 : long response_code,
966 : const void *response)
967 : {
968 4 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh = cls;
969 4 : const json_t *j = response;
970 4 : struct TALER_EXCHANGE_GetCoinsHistoryResponse rs = {
971 : .hr.reply = j,
972 4 : .hr.http_status = (unsigned int) response_code
973 : };
974 :
975 4 : gcsh->job = NULL;
976 4 : switch (response_code)
977 : {
978 0 : case 0:
979 0 : rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
980 0 : break;
981 4 : case MHD_HTTP_OK:
982 4 : if (GNUNET_OK !=
983 4 : handle_coins_history_ok (gcsh,
984 : j))
985 : {
986 0 : rs.hr.http_status = 0;
987 0 : rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
988 : }
989 4 : break;
990 0 : case MHD_HTTP_NO_CONTENT:
991 0 : break;
992 0 : case MHD_HTTP_NOT_MODIFIED:
993 0 : break;
994 0 : case MHD_HTTP_BAD_REQUEST:
995 : /* This should never happen, either us or the exchange is buggy
996 : (or API version conflict); just pass JSON reply to the application */
997 0 : GNUNET_break (0);
998 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
999 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1000 0 : break;
1001 0 : case MHD_HTTP_FORBIDDEN:
1002 : /* This should never happen, either us or the exchange is buggy
1003 : (or API version conflict); just pass JSON reply to the application */
1004 0 : GNUNET_break (0);
1005 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1006 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1007 0 : break;
1008 0 : case MHD_HTTP_NOT_FOUND:
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_INTERNAL_SERVER_ERROR:
1013 : /* Server had an internal issue; we should retry, but this API
1014 : leaves this to the application */
1015 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1016 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1017 0 : break;
1018 0 : default:
1019 : /* unexpected response code */
1020 0 : GNUNET_break_op (0);
1021 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1022 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1023 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1024 : "Unexpected response code %u/%d for coins history\n",
1025 : (unsigned int) response_code,
1026 : (int) rs.hr.ec);
1027 0 : break;
1028 : }
1029 4 : if (NULL != gcsh->cb)
1030 : {
1031 0 : gcsh->cb (gcsh->cb_cls,
1032 : &rs);
1033 0 : gcsh->cb = NULL;
1034 : }
1035 4 : TALER_EXCHANGE_get_coins_history_cancel (gcsh);
1036 4 : }
1037 :
1038 :
1039 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *
1040 4 : TALER_EXCHANGE_get_coins_history_create (
1041 : struct GNUNET_CURL_Context *ctx,
1042 : const char *url,
1043 : const struct TALER_CoinSpendPrivateKeyP *coin_priv)
1044 : {
1045 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh;
1046 :
1047 4 : gcsh = GNUNET_new (struct TALER_EXCHANGE_GetCoinsHistoryHandle);
1048 4 : gcsh->ctx = ctx;
1049 4 : gcsh->base_url = GNUNET_strdup (url);
1050 4 : gcsh->coin_priv = *coin_priv;
1051 4 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
1052 : &gcsh->coin_pub.eddsa_pub);
1053 4 : gcsh->options.start_off = 0;
1054 4 : return gcsh;
1055 : }
1056 :
1057 :
1058 : enum GNUNET_GenericReturnValue
1059 0 : TALER_EXCHANGE_get_coins_history_set_options_ (
1060 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
1061 : unsigned int num_options,
1062 : const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *options)
1063 : {
1064 0 : for (unsigned int i = 0; i < num_options; i++)
1065 : {
1066 0 : const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *opt = &options[i];
1067 :
1068 0 : switch (opt->option)
1069 : {
1070 0 : case TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_END:
1071 0 : return GNUNET_OK;
1072 0 : case TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_START_OFF:
1073 0 : gcsh->options.start_off = opt->details.start_off;
1074 0 : break;
1075 : }
1076 : }
1077 0 : return GNUNET_OK;
1078 : }
1079 :
1080 :
1081 : enum TALER_ErrorCode
1082 4 : TALER_EXCHANGE_get_coins_history_start (
1083 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
1084 : TALER_EXCHANGE_GetCoinsHistoryCallback cb,
1085 : TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE *cb_cls)
1086 : {
1087 : char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 64];
1088 : struct curl_slist *job_headers;
1089 : CURL *eh;
1090 :
1091 4 : if (NULL != gcsh->job)
1092 : {
1093 0 : GNUNET_break (0);
1094 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1095 : }
1096 4 : gcsh->cb = cb;
1097 4 : gcsh->cb_cls = cb_cls;
1098 : {
1099 : char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
1100 : char *end;
1101 :
1102 4 : end = GNUNET_STRINGS_data_to_string (
1103 4 : &gcsh->coin_pub,
1104 : sizeof (gcsh->coin_pub),
1105 : pub_str,
1106 : sizeof (pub_str));
1107 4 : *end = '\0';
1108 4 : if (0 != gcsh->options.start_off)
1109 0 : GNUNET_snprintf (arg_str,
1110 : sizeof (arg_str),
1111 : "coins/%s/history?start=%llu",
1112 : pub_str,
1113 0 : (unsigned long long) gcsh->options.start_off);
1114 : else
1115 4 : GNUNET_snprintf (arg_str,
1116 : sizeof (arg_str),
1117 : "coins/%s/history",
1118 : pub_str);
1119 : }
1120 4 : gcsh->url = TALER_url_join (gcsh->base_url,
1121 : arg_str,
1122 : NULL);
1123 4 : if (NULL == gcsh->url)
1124 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
1125 4 : eh = TALER_EXCHANGE_curl_easy_get_ (gcsh->url);
1126 4 : if (NULL == eh)
1127 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
1128 : {
1129 : struct TALER_CoinSpendSignatureP coin_sig;
1130 : char *sig_hdr;
1131 : char *hdr;
1132 :
1133 4 : TALER_wallet_coin_history_sign (gcsh->options.start_off,
1134 4 : &gcsh->coin_priv,
1135 : &coin_sig);
1136 4 : sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
1137 : &coin_sig,
1138 : sizeof (coin_sig));
1139 4 : GNUNET_asprintf (&hdr,
1140 : "%s: %s",
1141 : TALER_COIN_HISTORY_SIGNATURE_HEADER,
1142 : sig_hdr);
1143 4 : GNUNET_free (sig_hdr);
1144 4 : job_headers = curl_slist_append (NULL,
1145 : hdr);
1146 4 : GNUNET_free (hdr);
1147 4 : if (NULL == job_headers)
1148 : {
1149 0 : GNUNET_break (0);
1150 0 : curl_easy_cleanup (eh);
1151 0 : GNUNET_free (gcsh->url);
1152 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1153 : }
1154 : }
1155 4 : gcsh->job = GNUNET_CURL_job_add2 (gcsh->ctx,
1156 : eh,
1157 : job_headers,
1158 : &handle_coins_history_finished,
1159 : gcsh);
1160 4 : curl_slist_free_all (job_headers);
1161 4 : if (NULL == gcsh->job)
1162 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1163 4 : return TALER_EC_NONE;
1164 : }
1165 :
1166 :
1167 : void
1168 4 : TALER_EXCHANGE_get_coins_history_cancel (
1169 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh)
1170 : {
1171 4 : if (NULL != gcsh->job)
1172 : {
1173 0 : GNUNET_CURL_job_cancel (gcsh->job);
1174 0 : gcsh->job = NULL;
1175 : }
1176 4 : GNUNET_free (gcsh->url);
1177 4 : GNUNET_free (gcsh->base_url);
1178 4 : GNUNET_free (gcsh);
1179 4 : }
1180 :
1181 :
1182 : enum GNUNET_GenericReturnValue
1183 0 : TALER_EXCHANGE_check_coin_signature_conflict (
1184 : const json_t *history,
1185 : const struct TALER_CoinSpendSignatureP *coin_sig)
1186 : {
1187 : size_t off;
1188 : json_t *entry;
1189 :
1190 0 : json_array_foreach (history, off, entry)
1191 : {
1192 : struct TALER_CoinSpendSignatureP cs;
1193 : struct GNUNET_JSON_Specification spec[] = {
1194 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
1195 : &cs),
1196 0 : GNUNET_JSON_spec_end ()
1197 : };
1198 :
1199 0 : if (GNUNET_OK !=
1200 0 : GNUNET_JSON_parse (entry,
1201 : spec,
1202 : NULL, NULL))
1203 0 : continue; /* entry without coin signature */
1204 0 : if (0 ==
1205 0 : GNUNET_memcmp (&cs,
1206 : coin_sig))
1207 : {
1208 0 : GNUNET_break_op (0);
1209 0 : return GNUNET_SYSERR;
1210 : }
1211 : }
1212 0 : return GNUNET_OK;
1213 : }
1214 :
1215 :
1216 : #if FIXME_IMPLEMENT /* #9422 */
1217 : /**
1218 : * FIXME-Oec-#9422: we need some specific routines that show
1219 : * that certain coin operations are indeed in conflict,
1220 : * for example that the coin is of a different denomination
1221 : * or different age restrictions.
1222 : * This relates to unimplemented error handling for
1223 : * coins in the exchange!
1224 : *
1225 : * Check that the provided @a proof indeeds indicates
1226 : * a conflict for @a coin_pub.
1227 : *
1228 : * @param keys exchange keys
1229 : * @param proof provided conflict proof
1230 : * @param dk denomination of @a coin_pub that the client
1231 : * used
1232 : * @param coin_pub public key of the coin
1233 : * @param required balance required on the coin for the operation
1234 : * @return #GNUNET_OK if @a proof holds
1235 : */
1236 : // FIXME-#9422: should be properly defined and implemented!
1237 : enum GNUNET_GenericReturnValue
1238 : TALER_EXCHANGE_check_coin_conflict_ (
1239 : const struct TALER_EXCHANGE_Keys *keys,
1240 : const json_t *proof,
1241 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
1242 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1243 : const struct TALER_Amount *required)
1244 : {
1245 : enum TALER_ErrorCode ec;
1246 :
1247 : ec = TALER_JSON_get_error_code (proof);
1248 : switch (ec)
1249 : {
1250 : case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
1251 : /* Nothing to check anymore here, proof needs to be
1252 : checked in the GET /coins/$COIN_PUB handler */
1253 : break;
1254 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
1255 : // FIXME-#9422: write check!
1256 : break;
1257 : default:
1258 : GNUNET_break_op (0);
1259 : return GNUNET_SYSERR;
1260 : }
1261 : return GNUNET_OK;
1262 : }
1263 :
1264 :
1265 : #endif
1266 :
1267 :
1268 : /* end of exchange_api_get-coins-COIN_PUB-history.c */
|