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