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 : const 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 4 : 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 4 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
402 : &rh->details.recoup.exchange_sig),
403 4 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
404 : &rh->details.recoup.exchange_pub),
405 4 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
406 : &rh->details.recoup.reserve_pub),
407 4 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
408 : &rh->details.recoup.coin_sig),
409 4 : GNUNET_JSON_spec_fixed_auto ("coin_blinding_secret",
410 : &rh->details.recoup.coin_bks),
411 4 : GNUNET_JSON_spec_fixed_auto ("planchets_h",
412 : &rh->details.recoup.planchets_h),
413 4 : GNUNET_JSON_spec_uint32 ("coin_index",
414 : &rh->details.recoup.coin_index),
415 4 : GNUNET_JSON_spec_timestamp ("timestamp",
416 : &rh->details.recoup.timestamp),
417 4 : GNUNET_JSON_spec_end ()
418 : };
419 :
420 4 : if (GNUNET_OK !=
421 4 : GNUNET_JSON_parse (transaction,
422 : spec,
423 : NULL, NULL))
424 : {
425 0 : GNUNET_break_op (0);
426 0 : return GNUNET_SYSERR;
427 : }
428 4 : if (GNUNET_OK !=
429 4 : TALER_EXCHANGE_test_signing_key (pc->keys,
430 4 : &rh->details.recoup.exchange_pub))
431 : {
432 0 : GNUNET_break_op (0);
433 0 : return GNUNET_SYSERR;
434 : }
435 4 : if (GNUNET_OK !=
436 4 : TALER_exchange_online_confirm_recoup_verify (
437 : rh->details.recoup.timestamp,
438 : amount,
439 : pc->coin_pub,
440 4 : &rh->details.recoup.reserve_pub,
441 4 : &rh->details.recoup.exchange_pub,
442 4 : &rh->details.recoup.exchange_sig))
443 : {
444 0 : GNUNET_break_op (0);
445 0 : return GNUNET_SYSERR;
446 : }
447 4 : if (GNUNET_OK !=
448 4 : TALER_wallet_recoup_verify (&pc->dk->h_key,
449 4 : &rh->details.recoup.coin_bks,
450 : pc->coin_pub,
451 4 : &rh->details.recoup.coin_sig))
452 : {
453 0 : GNUNET_break_op (0);
454 0 : return GNUNET_SYSERR;
455 : }
456 4 : return GNUNET_YES;
457 : }
458 :
459 :
460 : /**
461 : * Handle recoup-refresh entry in the coin's history.
462 : * This is the coin that was subjected to a recoup,
463 : * the value being credited to the old coin.
464 : *
465 : * @param[in,out] pc overall context
466 : * @param[out] rh history entry to initialize
467 : * @param amount main amount of this operation
468 : * @param transaction JSON details for the operation
469 : * @return #GNUNET_SYSERR on error,
470 : * #GNUNET_OK to add, #GNUNET_NO to subtract
471 : */
472 : static enum GNUNET_GenericReturnValue
473 0 : help_recoup_refresh (struct CoinHistoryParseContext *pc,
474 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
475 : const struct TALER_Amount *amount,
476 : json_t *transaction)
477 : {
478 : struct GNUNET_JSON_Specification spec[] = {
479 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
480 : &rh->details.recoup_refresh.exchange_sig),
481 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
482 : &rh->details.recoup_refresh.exchange_pub),
483 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
484 : &rh->details.recoup_refresh.coin_sig),
485 0 : GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
486 : &rh->details.recoup_refresh.old_coin_pub),
487 0 : GNUNET_JSON_spec_fixed_auto ("coin_blinding_secret",
488 : &rh->details.recoup_refresh.coin_bks),
489 0 : GNUNET_JSON_spec_fixed_auto ("rc",
490 : &rh->details.recoup_refresh.rc),
491 0 : GNUNET_JSON_spec_uint32 ("coin_index",
492 : &rh->details.recoup_refresh.coin_index),
493 0 : GNUNET_JSON_spec_timestamp ("timestamp",
494 : &rh->details.recoup_refresh.timestamp),
495 0 : GNUNET_JSON_spec_end ()
496 : };
497 :
498 0 : if (GNUNET_OK !=
499 0 : GNUNET_JSON_parse (transaction,
500 : spec,
501 : NULL, NULL))
502 : {
503 0 : GNUNET_break_op (0);
504 0 : return GNUNET_SYSERR;
505 : }
506 0 : if (GNUNET_OK !=
507 0 : TALER_EXCHANGE_test_signing_key (
508 : pc->keys,
509 0 : &rh->details.recoup_refresh.exchange_pub))
510 : {
511 0 : GNUNET_break_op (0);
512 0 : return GNUNET_SYSERR;
513 : }
514 0 : if (GNUNET_OK !=
515 0 : TALER_exchange_online_confirm_recoup_refresh_verify (
516 : rh->details.recoup_refresh.timestamp,
517 : amount,
518 : pc->coin_pub,
519 0 : &rh->details.recoup_refresh.old_coin_pub,
520 0 : &rh->details.recoup_refresh.exchange_pub,
521 0 : &rh->details.recoup_refresh.exchange_sig))
522 : {
523 0 : GNUNET_break_op (0);
524 0 : return GNUNET_SYSERR;
525 : }
526 0 : if (GNUNET_OK !=
527 0 : TALER_wallet_recoup_refresh_verify (&pc->dk->h_key,
528 0 : &rh->details.recoup_refresh.coin_bks,
529 : pc->coin_pub,
530 0 : &rh->details.recoup_refresh.coin_sig))
531 : {
532 0 : GNUNET_break_op (0);
533 0 : return GNUNET_SYSERR;
534 : }
535 0 : return GNUNET_YES;
536 : }
537 :
538 :
539 : /**
540 : * Handle old coin recoup entry in the coin's history.
541 : * This is the coin that was credited in a recoup,
542 : * the value being credited to this coin.
543 : *
544 : * @param[in,out] pc overall context
545 : * @param[out] rh history entry to initialize
546 : * @param amount main amount of this operation
547 : * @param transaction JSON details for the operation
548 : * @return #GNUNET_SYSERR on error,
549 : * #GNUNET_OK to add, #GNUNET_NO to subtract
550 : */
551 : static enum GNUNET_GenericReturnValue
552 0 : help_old_coin_recoup (struct CoinHistoryParseContext *pc,
553 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
554 : const struct TALER_Amount *amount,
555 : json_t *transaction)
556 : {
557 : struct GNUNET_JSON_Specification spec[] = {
558 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
559 : &rh->details.old_coin_recoup.exchange_sig),
560 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
561 : &rh->details.old_coin_recoup.exchange_pub),
562 0 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
563 : &rh->details.old_coin_recoup.new_coin_pub),
564 0 : GNUNET_JSON_spec_fixed_auto ("rc",
565 : &rh->details.old_coin_recoup.rc),
566 0 : GNUNET_JSON_spec_timestamp ("timestamp",
567 : &rh->details.old_coin_recoup.timestamp),
568 0 : GNUNET_JSON_spec_end ()
569 : };
570 :
571 0 : if (GNUNET_OK !=
572 0 : GNUNET_JSON_parse (transaction,
573 : spec,
574 : NULL, NULL))
575 : {
576 0 : GNUNET_break_op (0);
577 0 : return GNUNET_SYSERR;
578 : }
579 0 : if (GNUNET_OK !=
580 0 : TALER_EXCHANGE_test_signing_key (
581 : pc->keys,
582 0 : &rh->details.old_coin_recoup.exchange_pub))
583 : {
584 0 : GNUNET_break_op (0);
585 0 : return GNUNET_SYSERR;
586 : }
587 0 : if (GNUNET_OK !=
588 0 : TALER_exchange_online_confirm_recoup_refresh_verify (
589 : rh->details.old_coin_recoup.timestamp,
590 : amount,
591 0 : &rh->details.old_coin_recoup.new_coin_pub,
592 : pc->coin_pub,
593 0 : &rh->details.old_coin_recoup.exchange_pub,
594 0 : &rh->details.old_coin_recoup.exchange_sig))
595 : {
596 0 : GNUNET_break_op (0);
597 0 : return GNUNET_SYSERR;
598 : }
599 0 : return GNUNET_NO;
600 : }
601 :
602 :
603 : /**
604 : * Handle purse deposit entry in the coin's history.
605 : *
606 : * @param[in,out] pc overall context
607 : * @param[out] rh history entry to initialize
608 : * @param amount main amount of this operation
609 : * @param transaction JSON details for the operation
610 : * @return #GNUNET_SYSERR on error,
611 : * #GNUNET_OK to add, #GNUNET_NO to subtract
612 : */
613 : static enum GNUNET_GenericReturnValue
614 3 : help_purse_deposit (struct CoinHistoryParseContext *pc,
615 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
616 : const struct TALER_Amount *amount,
617 : json_t *transaction)
618 : {
619 : struct GNUNET_JSON_Specification spec[] = {
620 3 : TALER_JSON_spec_web_url ("exchange_base_url",
621 : &rh->details.purse_deposit.exchange_base_url),
622 3 : GNUNET_JSON_spec_mark_optional (
623 3 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
624 : &rh->details.purse_deposit.phac),
625 : NULL),
626 3 : GNUNET_JSON_spec_fixed_auto ("purse_pub",
627 : &rh->details.purse_deposit.purse_pub),
628 3 : GNUNET_JSON_spec_bool ("refunded",
629 : &rh->details.purse_deposit.refunded),
630 3 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
631 : &rh->details.purse_deposit.coin_sig),
632 3 : GNUNET_JSON_spec_end ()
633 : };
634 :
635 3 : if (GNUNET_OK !=
636 3 : GNUNET_JSON_parse (transaction,
637 : spec,
638 : NULL, NULL))
639 : {
640 0 : GNUNET_break_op (0);
641 0 : return GNUNET_SYSERR;
642 : }
643 3 : if (GNUNET_OK !=
644 3 : TALER_wallet_purse_deposit_verify (
645 : rh->details.purse_deposit.exchange_base_url,
646 3 : &rh->details.purse_deposit.purse_pub,
647 : amount,
648 3 : &pc->dk->h_key,
649 3 : &rh->details.purse_deposit.phac,
650 : pc->coin_pub,
651 3 : &rh->details.purse_deposit.coin_sig))
652 : {
653 0 : GNUNET_break_op (0);
654 0 : return GNUNET_SYSERR;
655 : }
656 3 : if (rh->details.purse_deposit.refunded)
657 : {
658 : /* We wave the deposit fee. */
659 0 : if (0 >
660 0 : TALER_amount_add (pc->total_in,
661 0 : pc->total_in,
662 0 : &pc->dk->fees.deposit))
663 : {
664 : /* overflow in refund history? inconceivable! Bad exchange! */
665 0 : GNUNET_break_op (0);
666 0 : return GNUNET_SYSERR;
667 : }
668 : }
669 3 : return GNUNET_YES;
670 : }
671 :
672 :
673 : /**
674 : * Handle purse refund entry in the coin's history.
675 : *
676 : * @param[in,out] pc overall context
677 : * @param[out] rh history entry to initialize
678 : * @param amount main amount of this operation
679 : * @param transaction JSON details for the operation
680 : * @return #GNUNET_SYSERR on error,
681 : * #GNUNET_OK to add, #GNUNET_NO to subtract
682 : */
683 : static enum GNUNET_GenericReturnValue
684 0 : help_purse_refund (struct CoinHistoryParseContext *pc,
685 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
686 : const struct TALER_Amount *amount,
687 : json_t *transaction)
688 : {
689 : struct GNUNET_JSON_Specification spec[] = {
690 0 : TALER_JSON_spec_amount_any ("refund_fee",
691 : &rh->details.purse_refund.refund_fee),
692 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
693 : &rh->details.purse_refund.exchange_sig),
694 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
695 : &rh->details.purse_refund.exchange_pub),
696 0 : GNUNET_JSON_spec_fixed_auto ("purse_pub",
697 : &rh->details.purse_refund.purse_pub),
698 0 : GNUNET_JSON_spec_end ()
699 : };
700 :
701 0 : if (GNUNET_OK !=
702 0 : GNUNET_JSON_parse (transaction,
703 : spec,
704 : NULL, NULL))
705 : {
706 0 : GNUNET_break_op (0);
707 0 : return GNUNET_SYSERR;
708 : }
709 0 : if (GNUNET_OK !=
710 0 : TALER_EXCHANGE_test_signing_key (
711 : pc->keys,
712 0 : &rh->details.purse_refund.exchange_pub))
713 : {
714 0 : GNUNET_break_op (0);
715 0 : return GNUNET_SYSERR;
716 : }
717 0 : if (GNUNET_OK !=
718 0 : TALER_exchange_online_purse_refund_verify (
719 : amount,
720 0 : &rh->details.purse_refund.refund_fee,
721 : pc->coin_pub,
722 0 : &rh->details.purse_refund.purse_pub,
723 0 : &rh->details.purse_refund.exchange_pub,
724 0 : &rh->details.purse_refund.exchange_sig))
725 : {
726 0 : GNUNET_break_op (0);
727 0 : return GNUNET_SYSERR;
728 : }
729 0 : if ( (GNUNET_YES !=
730 0 : TALER_amount_cmp_currency (&rh->details.purse_refund.refund_fee,
731 0 : &pc->dk->fees.refund)) ||
732 : (0 !=
733 0 : TALER_amount_cmp (&rh->details.purse_refund.refund_fee,
734 0 : &pc->dk->fees.refund)) )
735 : {
736 0 : GNUNET_break_op (0);
737 0 : return GNUNET_SYSERR;
738 : }
739 0 : return GNUNET_NO;
740 : }
741 :
742 :
743 : /**
744 : * Handle reserve deposit entry in the coin's history.
745 : *
746 : * @param[in,out] pc overall context
747 : * @param[out] rh history entry to initialize
748 : * @param amount main amount of this operation
749 : * @param transaction JSON details for the operation
750 : * @return #GNUNET_SYSERR on error,
751 : * #GNUNET_OK to add, #GNUNET_NO to subtract
752 : */
753 : static enum GNUNET_GenericReturnValue
754 0 : help_reserve_open_deposit (struct CoinHistoryParseContext *pc,
755 : struct TALER_EXCHANGE_CoinHistoryEntry *rh,
756 : const struct TALER_Amount *amount,
757 : json_t *transaction)
758 : {
759 : struct GNUNET_JSON_Specification spec[] = {
760 0 : GNUNET_JSON_spec_fixed_auto (
761 : "reserve_sig",
762 : &rh->details.reserve_open_deposit.reserve_sig),
763 0 : GNUNET_JSON_spec_fixed_auto (
764 : "coin_sig",
765 : &rh->details.reserve_open_deposit.coin_sig),
766 0 : TALER_JSON_spec_amount_any (
767 : "coin_contribution",
768 : &rh->details.reserve_open_deposit.coin_contribution),
769 0 : GNUNET_JSON_spec_mark_optional (
770 0 : GNUNET_JSON_spec_fixed_auto (
771 : "h_age_commitment",
772 : &rh->details.reserve_open_deposit.h_age_commitment),
773 : &rh->details.reserve_open_deposit.no_hac),
774 0 : GNUNET_JSON_spec_end ()
775 : };
776 :
777 0 : if (GNUNET_OK !=
778 0 : GNUNET_JSON_parse (transaction,
779 : spec,
780 : NULL, NULL))
781 : {
782 0 : GNUNET_break_op (0);
783 0 : return GNUNET_SYSERR;
784 : }
785 0 : if (GNUNET_OK !=
786 0 : TALER_wallet_reserve_open_deposit_verify (
787 : amount,
788 0 : &pc->dk->h_key,
789 0 : rh->details.reserve_open_deposit.no_hac
790 : ? NULL
791 : : &rh->details.reserve_open_deposit.h_age_commitment,
792 0 : &rh->details.reserve_open_deposit.reserve_sig,
793 : pc->coin_pub,
794 0 : &rh->details.reserve_open_deposit.coin_sig))
795 : {
796 0 : GNUNET_break_op (0);
797 0 : return GNUNET_SYSERR;
798 : }
799 0 : return GNUNET_YES;
800 : }
801 :
802 :
803 : enum GNUNET_GenericReturnValue
804 8 : TALER_EXCHANGE_parse_coin_history (
805 : const struct TALER_EXCHANGE_Keys *keys,
806 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
807 : const json_t *history,
808 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
809 : struct TALER_Amount *total_in,
810 : struct TALER_Amount *total_out,
811 : unsigned int rlen,
812 : struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen])
813 8 : {
814 : const struct
815 : {
816 : const char *type;
817 : CoinCheckHelper helper;
818 : enum TALER_EXCHANGE_CoinTransactionType ctt;
819 8 : } map[] = {
820 : { "DEPOSIT",
821 : &help_deposit,
822 : TALER_EXCHANGE_CTT_DEPOSIT },
823 : { "MELT",
824 : &help_melt,
825 : TALER_EXCHANGE_CTT_MELT },
826 : { "REFUND",
827 : &help_refund,
828 : TALER_EXCHANGE_CTT_REFUND },
829 : { "RECOUP-WITHDRAW",
830 : &help_recoup,
831 : TALER_EXCHANGE_CTT_RECOUP },
832 : { "RECOUP-REFRESH",
833 : &help_recoup_refresh,
834 : TALER_EXCHANGE_CTT_RECOUP_REFRESH },
835 : { "RECOUP-REFRESH-RECEIVER",
836 : &help_old_coin_recoup,
837 : TALER_EXCHANGE_CTT_OLD_COIN_RECOUP },
838 : { "PURSE-DEPOSIT",
839 : &help_purse_deposit,
840 : TALER_EXCHANGE_CTT_PURSE_DEPOSIT },
841 : { "PURSE-REFUND",
842 : &help_purse_refund,
843 : TALER_EXCHANGE_CTT_PURSE_REFUND },
844 : { "RESERVE-OPEN-DEPOSIT",
845 : &help_reserve_open_deposit,
846 : TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT },
847 : { NULL, NULL, TALER_EXCHANGE_CTT_NONE }
848 : };
849 8 : struct CoinHistoryParseContext pc = {
850 : .keys = keys,
851 : .dk = dk,
852 : .coin_pub = coin_pub,
853 : .total_out = total_out,
854 : .total_in = total_in
855 : };
856 : size_t len;
857 :
858 8 : if (NULL == history)
859 : {
860 0 : GNUNET_break_op (0);
861 0 : return GNUNET_SYSERR;
862 : }
863 8 : len = json_array_size (history);
864 8 : if (0 == len)
865 : {
866 0 : GNUNET_break_op (0);
867 0 : return GNUNET_SYSERR;
868 : }
869 8 : *total_in = dk->value;
870 8 : GNUNET_assert (GNUNET_OK ==
871 : TALER_amount_set_zero (total_in->currency,
872 : total_out));
873 17 : for (size_t off = 0; off < len; off++)
874 : {
875 9 : struct TALER_EXCHANGE_CoinHistoryEntry *rh = &rhistory[off];
876 9 : json_t *transaction = json_array_get (history,
877 : off);
878 : enum GNUNET_GenericReturnValue add;
879 : const char *type;
880 : struct GNUNET_JSON_Specification spec_glob[] = {
881 9 : TALER_JSON_spec_amount_any ("amount",
882 : &rh->amount),
883 9 : GNUNET_JSON_spec_string ("type",
884 : &type),
885 9 : GNUNET_JSON_spec_uint64 ("history_offset",
886 : &rh->history_offset),
887 9 : GNUNET_JSON_spec_end ()
888 : };
889 :
890 9 : if (GNUNET_OK !=
891 9 : GNUNET_JSON_parse (transaction,
892 : spec_glob,
893 : NULL, NULL))
894 : {
895 0 : GNUNET_break_op (0);
896 0 : return GNUNET_SYSERR;
897 : }
898 9 : if (GNUNET_YES !=
899 9 : TALER_amount_cmp_currency (&rh->amount,
900 : total_in))
901 : {
902 0 : GNUNET_break_op (0);
903 0 : return GNUNET_SYSERR;
904 : }
905 9 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
906 : "Operation of type %s with amount %s\n",
907 : type,
908 : TALER_amount2s (&rh->amount));
909 9 : add = GNUNET_SYSERR;
910 39 : for (unsigned int i = 0; NULL != map[i].type; i++)
911 : {
912 39 : if (0 == strcasecmp (type,
913 39 : map[i].type))
914 : {
915 9 : rh->type = map[i].ctt;
916 9 : add = map[i].helper (&pc,
917 : rh,
918 9 : &rh->amount,
919 : transaction);
920 9 : break;
921 : }
922 : }
923 9 : switch (add)
924 : {
925 0 : case GNUNET_SYSERR:
926 : /* entry type not supported, new version on server? */
927 0 : rh->type = TALER_EXCHANGE_CTT_NONE;
928 0 : GNUNET_break_op (0);
929 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
930 : "Unexpected type `%s' in response\n",
931 : type);
932 0 : return GNUNET_SYSERR;
933 9 : case GNUNET_YES:
934 : /* This amount should be debited from the coin */
935 9 : if (0 >
936 9 : TALER_amount_add (total_out,
937 : total_out,
938 9 : &rh->amount))
939 : {
940 : /* overflow in history already!? inconceivable! Bad exchange! */
941 0 : GNUNET_break_op (0);
942 0 : return GNUNET_SYSERR;
943 : }
944 9 : break;
945 0 : case GNUNET_NO:
946 : /* This amount should be credited to the coin. */
947 0 : if (0 >
948 0 : TALER_amount_add (total_in,
949 : total_in,
950 0 : &rh->amount))
951 : {
952 : /* overflow in refund history? inconceivable! Bad exchange! */
953 0 : GNUNET_break_op (0);
954 0 : return GNUNET_SYSERR;
955 : }
956 0 : break;
957 : } /* end of switch(add) */
958 : }
959 8 : return GNUNET_OK;
960 : }
961 :
962 :
963 : /**
964 : * We received an #MHD_HTTP_OK response. Handle the JSON response.
965 : *
966 : * @param gcsh handle of the request
967 : * @param j JSON response
968 : * @return #GNUNET_OK on success
969 : */
970 : static enum GNUNET_GenericReturnValue
971 8 : handle_coins_history_ok (struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
972 : const json_t *j)
973 : {
974 8 : struct TALER_EXCHANGE_GetCoinsHistoryResponse rs = {
975 : .hr.reply = j,
976 : .hr.http_status = MHD_HTTP_OK
977 : };
978 : struct GNUNET_JSON_Specification spec[] = {
979 8 : TALER_JSON_spec_amount_any ("balance",
980 : &rs.details.ok.balance),
981 8 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
982 : &rs.details.ok.h_denom_pub),
983 8 : GNUNET_JSON_spec_array_const ("history",
984 : &rs.details.ok.history),
985 8 : GNUNET_JSON_spec_end ()
986 : };
987 :
988 8 : if (GNUNET_OK !=
989 8 : GNUNET_JSON_parse (j,
990 : spec,
991 : NULL,
992 : NULL))
993 : {
994 0 : GNUNET_break_op (0);
995 0 : return GNUNET_SYSERR;
996 : }
997 8 : if (NULL != gcsh->cb)
998 : {
999 8 : gcsh->cb (gcsh->cb_cls,
1000 : &rs);
1001 8 : gcsh->cb = NULL;
1002 : }
1003 8 : GNUNET_JSON_parse_free (spec);
1004 8 : return GNUNET_OK;
1005 : }
1006 :
1007 :
1008 : /**
1009 : * Function called when we're done processing the
1010 : * HTTP GET /coins/$COIN_PUB/history request.
1011 : *
1012 : * @param cls the `struct TALER_EXCHANGE_GetCoinsHistoryHandle`
1013 : * @param response_code HTTP response code, 0 on error
1014 : * @param response parsed JSON result, NULL on error
1015 : */
1016 : static void
1017 10 : handle_coins_history_finished (void *cls,
1018 : long response_code,
1019 : const void *response)
1020 : {
1021 10 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh = cls;
1022 10 : const json_t *j = response;
1023 10 : struct TALER_EXCHANGE_GetCoinsHistoryResponse rs = {
1024 : .hr.reply = j,
1025 10 : .hr.http_status = (unsigned int) response_code
1026 : };
1027 :
1028 10 : gcsh->job = NULL;
1029 10 : switch (response_code)
1030 : {
1031 0 : case 0:
1032 0 : rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
1033 0 : break;
1034 8 : case MHD_HTTP_OK:
1035 8 : if (GNUNET_OK !=
1036 8 : handle_coins_history_ok (gcsh,
1037 : j))
1038 : {
1039 0 : rs.hr.http_status = 0;
1040 0 : rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
1041 : }
1042 8 : break;
1043 0 : case MHD_HTTP_NO_CONTENT:
1044 0 : break;
1045 0 : case MHD_HTTP_NOT_MODIFIED:
1046 0 : break;
1047 0 : case MHD_HTTP_BAD_REQUEST:
1048 : /* This should never happen, either us or the exchange is buggy
1049 : (or API version conflict); just pass JSON reply to the application */
1050 0 : GNUNET_break (0);
1051 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1052 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1053 0 : break;
1054 0 : case MHD_HTTP_FORBIDDEN:
1055 : /* This should never happen, either us or the exchange is buggy
1056 : (or API version conflict); just pass JSON reply to the application */
1057 0 : GNUNET_break (0);
1058 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1059 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1060 0 : break;
1061 2 : case MHD_HTTP_NOT_FOUND:
1062 2 : rs.hr.ec = TALER_JSON_get_error_code (j);
1063 2 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1064 2 : break;
1065 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
1066 : /* Server had an internal issue; we should retry, but this API
1067 : leaves this to the application */
1068 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1069 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1070 0 : break;
1071 0 : default:
1072 : /* unexpected response code */
1073 0 : GNUNET_break_op (0);
1074 0 : rs.hr.ec = TALER_JSON_get_error_code (j);
1075 0 : rs.hr.hint = TALER_JSON_get_error_hint (j);
1076 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1077 : "Unexpected response code %u/%d for coins history\n",
1078 : (unsigned int) response_code,
1079 : (int) rs.hr.ec);
1080 0 : break;
1081 : }
1082 10 : if (NULL != gcsh->cb)
1083 : {
1084 2 : gcsh->cb (gcsh->cb_cls,
1085 : &rs);
1086 2 : gcsh->cb = NULL;
1087 : }
1088 10 : TALER_EXCHANGE_get_coins_history_cancel (gcsh);
1089 10 : }
1090 :
1091 :
1092 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *
1093 10 : TALER_EXCHANGE_get_coins_history_create (
1094 : struct GNUNET_CURL_Context *ctx,
1095 : const char *url,
1096 : const struct TALER_CoinSpendPrivateKeyP *coin_priv)
1097 : {
1098 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh;
1099 :
1100 10 : gcsh = GNUNET_new (struct TALER_EXCHANGE_GetCoinsHistoryHandle);
1101 10 : gcsh->ctx = ctx;
1102 10 : gcsh->base_url = GNUNET_strdup (url);
1103 10 : gcsh->coin_priv = *coin_priv;
1104 10 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
1105 : &gcsh->coin_pub.eddsa_pub);
1106 10 : gcsh->options.start_off = 0;
1107 10 : return gcsh;
1108 : }
1109 :
1110 :
1111 : enum GNUNET_GenericReturnValue
1112 0 : TALER_EXCHANGE_get_coins_history_set_options_ (
1113 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
1114 : unsigned int num_options,
1115 : const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *options)
1116 : {
1117 0 : for (unsigned int i = 0; i < num_options; i++)
1118 : {
1119 0 : const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *opt = &options[i];
1120 :
1121 0 : switch (opt->option)
1122 : {
1123 0 : case TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_END:
1124 0 : return GNUNET_OK;
1125 0 : case TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_START_OFF:
1126 0 : gcsh->options.start_off = opt->details.start_off;
1127 0 : break;
1128 : }
1129 : }
1130 0 : return GNUNET_OK;
1131 : }
1132 :
1133 :
1134 : enum TALER_ErrorCode
1135 10 : TALER_EXCHANGE_get_coins_history_start (
1136 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
1137 : TALER_EXCHANGE_GetCoinsHistoryCallback cb,
1138 : TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE *cb_cls)
1139 : {
1140 : char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 64];
1141 : struct curl_slist *job_headers;
1142 : CURL *eh;
1143 :
1144 10 : if (NULL != gcsh->job)
1145 : {
1146 0 : GNUNET_break (0);
1147 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1148 : }
1149 10 : gcsh->cb = cb;
1150 10 : gcsh->cb_cls = cb_cls;
1151 : {
1152 : char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
1153 : char *end;
1154 :
1155 10 : end = GNUNET_STRINGS_data_to_string (
1156 10 : &gcsh->coin_pub,
1157 : sizeof (gcsh->coin_pub),
1158 : pub_str,
1159 : sizeof (pub_str));
1160 10 : *end = '\0';
1161 10 : if (0 != gcsh->options.start_off)
1162 0 : GNUNET_snprintf (arg_str,
1163 : sizeof (arg_str),
1164 : "coins/%s/history?start=%llu",
1165 : pub_str,
1166 0 : (unsigned long long) gcsh->options.start_off);
1167 : else
1168 10 : GNUNET_snprintf (arg_str,
1169 : sizeof (arg_str),
1170 : "coins/%s/history",
1171 : pub_str);
1172 : }
1173 10 : gcsh->url = TALER_url_join (gcsh->base_url,
1174 : arg_str,
1175 : NULL);
1176 10 : if (NULL == gcsh->url)
1177 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
1178 10 : eh = TALER_EXCHANGE_curl_easy_get_ (gcsh->url);
1179 10 : if (NULL == eh)
1180 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
1181 : {
1182 : struct TALER_CoinSpendSignatureP coin_sig;
1183 : char *sig_hdr;
1184 : char *hdr;
1185 :
1186 10 : TALER_wallet_coin_history_sign (gcsh->options.start_off,
1187 10 : &gcsh->coin_priv,
1188 : &coin_sig);
1189 10 : sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
1190 : &coin_sig,
1191 : sizeof (coin_sig));
1192 10 : GNUNET_asprintf (&hdr,
1193 : "%s: %s",
1194 : TALER_COIN_HISTORY_SIGNATURE_HEADER,
1195 : sig_hdr);
1196 10 : GNUNET_free (sig_hdr);
1197 10 : job_headers = curl_slist_append (NULL,
1198 : hdr);
1199 10 : GNUNET_free (hdr);
1200 10 : if (NULL == job_headers)
1201 : {
1202 0 : GNUNET_break (0);
1203 0 : curl_easy_cleanup (eh);
1204 0 : GNUNET_free (gcsh->url);
1205 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1206 : }
1207 : }
1208 10 : gcsh->job = GNUNET_CURL_job_add2 (gcsh->ctx,
1209 : eh,
1210 : job_headers,
1211 : &handle_coins_history_finished,
1212 : gcsh);
1213 10 : curl_slist_free_all (job_headers);
1214 10 : if (NULL == gcsh->job)
1215 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1216 10 : return TALER_EC_NONE;
1217 : }
1218 :
1219 :
1220 : void
1221 10 : TALER_EXCHANGE_get_coins_history_cancel (
1222 : struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh)
1223 : {
1224 10 : if (NULL != gcsh->job)
1225 : {
1226 0 : GNUNET_CURL_job_cancel (gcsh->job);
1227 0 : gcsh->job = NULL;
1228 : }
1229 10 : GNUNET_free (gcsh->url);
1230 10 : GNUNET_free (gcsh->base_url);
1231 10 : GNUNET_free (gcsh);
1232 10 : }
1233 :
1234 :
1235 : enum GNUNET_GenericReturnValue
1236 0 : TALER_EXCHANGE_check_coin_signature_conflict (
1237 : const json_t *history,
1238 : const struct TALER_CoinSpendSignatureP *coin_sig)
1239 : {
1240 : size_t off;
1241 : json_t *entry;
1242 :
1243 0 : json_array_foreach (history, off, entry)
1244 : {
1245 : struct TALER_CoinSpendSignatureP cs;
1246 : struct GNUNET_JSON_Specification spec[] = {
1247 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
1248 : &cs),
1249 0 : GNUNET_JSON_spec_end ()
1250 : };
1251 :
1252 0 : if (GNUNET_OK !=
1253 0 : GNUNET_JSON_parse (entry,
1254 : spec,
1255 : NULL, NULL))
1256 0 : continue; /* entry without coin signature */
1257 0 : if (0 ==
1258 0 : GNUNET_memcmp (&cs,
1259 : coin_sig))
1260 : {
1261 0 : GNUNET_break_op (0);
1262 0 : return GNUNET_SYSERR;
1263 : }
1264 : }
1265 0 : return GNUNET_OK;
1266 : }
1267 :
1268 :
1269 : /* end of exchange_api_get-coins-COIN_PUB-history.c */
|