Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero 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 Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_responses.c
18 : * @brief API for generating generic replies of the exchange; these
19 : * functions are called TEH_RESPONSE_reply_ and they generate
20 : * and queue MHD response objects for a given connection.
21 : * @author Florian Dold
22 : * @author Benedikt Mueller
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include <zlib.h>
27 : #include "taler-exchange-httpd_responses.h"
28 : #include "taler_util.h"
29 : #include "taler_json_lib.h"
30 : #include "taler_mhd_lib.h"
31 : #include "taler-exchange-httpd_keys.h"
32 :
33 :
34 : /**
35 : * Compile the transaction history of a coin into a JSON object.
36 : *
37 : * @param coin_pub public key of the coin
38 : * @param tl transaction history to JSON-ify
39 : * @return json representation of the @a rh, NULL on error
40 : */
41 : json_t *
42 0 : TEH_RESPONSE_compile_transaction_history (
43 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
44 : const struct TALER_EXCHANGEDB_TransactionList *tl)
45 : {
46 : json_t *history;
47 :
48 0 : history = json_array ();
49 0 : if (NULL == history)
50 : {
51 0 : GNUNET_break (0); /* out of memory!? */
52 0 : return NULL;
53 : }
54 0 : for (const struct TALER_EXCHANGEDB_TransactionList *pos = tl;
55 : NULL != pos;
56 0 : pos = pos->next)
57 : {
58 0 : switch (pos->type)
59 : {
60 0 : case TALER_EXCHANGEDB_TT_DEPOSIT:
61 : {
62 0 : const struct TALER_EXCHANGEDB_DepositListEntry *deposit =
63 : pos->details.deposit;
64 : struct TALER_MerchantWireHashP h_wire;
65 :
66 0 : TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
67 : &deposit->wire_salt,
68 : &h_wire);
69 : #if ENABLE_SANITY_CHECKS
70 : /* internal sanity check before we hand out a bogus sig... */
71 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
72 0 : if (GNUNET_OK !=
73 0 : TALER_wallet_deposit_verify (
74 : &deposit->amount_with_fee,
75 : &deposit->deposit_fee,
76 : &h_wire,
77 : &deposit->h_contract_terms,
78 : &deposit->h_age_commitment,
79 : NULL /* h_extensions! */,
80 : &deposit->h_denom_pub,
81 : deposit->timestamp,
82 : &deposit->merchant_pub,
83 : deposit->refund_deadline,
84 : coin_pub,
85 : &deposit->csig))
86 : {
87 0 : GNUNET_break (0);
88 0 : json_decref (history);
89 0 : return NULL;
90 : }
91 : #endif
92 0 : if (0 !=
93 0 : json_array_append_new (
94 : history,
95 0 : GNUNET_JSON_PACK (
96 : GNUNET_JSON_pack_string ("type",
97 : "DEPOSIT"),
98 : TALER_JSON_pack_amount ("amount",
99 : &deposit->amount_with_fee),
100 : TALER_JSON_pack_amount ("deposit_fee",
101 : &deposit->deposit_fee),
102 : GNUNET_JSON_pack_timestamp ("timestamp",
103 : deposit->timestamp),
104 : GNUNET_JSON_pack_allow_null (
105 : GNUNET_JSON_pack_timestamp ("refund_deadline",
106 : deposit->refund_deadline)),
107 : GNUNET_JSON_pack_data_auto ("merchant_pub",
108 : &deposit->merchant_pub),
109 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
110 : &deposit->h_contract_terms),
111 : GNUNET_JSON_pack_data_auto ("h_wire",
112 : &h_wire),
113 : GNUNET_JSON_pack_allow_null (
114 : deposit->no_age_commitment ?
115 : GNUNET_JSON_pack_string (
116 : "h_age_commitment", NULL) :
117 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
118 : &deposit->h_age_commitment)),
119 : GNUNET_JSON_pack_data_auto ("coin_sig",
120 : &deposit->csig))))
121 : {
122 0 : GNUNET_break (0);
123 0 : json_decref (history);
124 0 : return NULL;
125 : }
126 0 : break;
127 : }
128 0 : case TALER_EXCHANGEDB_TT_MELT:
129 : {
130 0 : const struct TALER_EXCHANGEDB_MeltListEntry *melt =
131 : pos->details.melt;
132 0 : const struct TALER_AgeCommitmentHash *phac = NULL;
133 :
134 : #if ENABLE_SANITY_CHECKS
135 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
136 0 : if (GNUNET_OK !=
137 0 : TALER_wallet_melt_verify (
138 : &melt->amount_with_fee,
139 : &melt->melt_fee,
140 : &melt->rc,
141 : &melt->h_denom_pub,
142 : &melt->h_age_commitment,
143 : coin_pub,
144 : &melt->coin_sig))
145 : {
146 0 : GNUNET_break (0);
147 0 : json_decref (history);
148 0 : return NULL;
149 : }
150 : #endif
151 :
152 : /* Age restriction is optional. We communicate a NULL value to
153 : * JSON_PACK below */
154 0 : if (! melt->no_age_commitment)
155 0 : phac = &melt->h_age_commitment;
156 :
157 0 : if (0 !=
158 0 : json_array_append_new (
159 : history,
160 0 : GNUNET_JSON_PACK (
161 : GNUNET_JSON_pack_string ("type",
162 : "MELT"),
163 : TALER_JSON_pack_amount ("amount",
164 : &melt->amount_with_fee),
165 : TALER_JSON_pack_amount ("melt_fee",
166 : &melt->melt_fee),
167 : GNUNET_JSON_pack_data_auto ("rc",
168 : &melt->rc),
169 : GNUNET_JSON_pack_allow_null (
170 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
171 : phac)),
172 : GNUNET_JSON_pack_data_auto ("coin_sig",
173 : &melt->coin_sig))))
174 : {
175 0 : GNUNET_break (0);
176 0 : json_decref (history);
177 0 : return NULL;
178 : }
179 : }
180 0 : break;
181 0 : case TALER_EXCHANGEDB_TT_REFUND:
182 : {
183 0 : const struct TALER_EXCHANGEDB_RefundListEntry *refund =
184 : pos->details.refund;
185 : struct TALER_Amount value;
186 :
187 : #if ENABLE_SANITY_CHECKS
188 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
189 0 : if (GNUNET_OK !=
190 0 : TALER_merchant_refund_verify (
191 : coin_pub,
192 : &refund->h_contract_terms,
193 : refund->rtransaction_id,
194 : &refund->refund_amount,
195 : &refund->merchant_pub,
196 : &refund->merchant_sig))
197 : {
198 0 : GNUNET_break (0);
199 0 : json_decref (history);
200 0 : return NULL;
201 : }
202 : #endif
203 0 : if (0 >
204 0 : TALER_amount_subtract (&value,
205 : &refund->refund_amount,
206 : &refund->refund_fee))
207 : {
208 0 : GNUNET_break (0);
209 0 : json_decref (history);
210 0 : return NULL;
211 : }
212 0 : if (0 !=
213 0 : json_array_append_new (
214 : history,
215 0 : GNUNET_JSON_PACK (
216 : GNUNET_JSON_pack_string ("type",
217 : "REFUND"),
218 : TALER_JSON_pack_amount ("amount",
219 : &value),
220 : TALER_JSON_pack_amount ("refund_fee",
221 : &refund->refund_fee),
222 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
223 : &refund->h_contract_terms),
224 : GNUNET_JSON_pack_data_auto ("merchant_pub",
225 : &refund->merchant_pub),
226 : GNUNET_JSON_pack_uint64 ("rtransaction_id",
227 : refund->rtransaction_id),
228 : GNUNET_JSON_pack_data_auto ("merchant_sig",
229 : &refund->merchant_sig))))
230 : {
231 0 : GNUNET_break (0);
232 0 : json_decref (history);
233 0 : return NULL;
234 : }
235 : }
236 0 : break;
237 0 : case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
238 : {
239 0 : struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
240 : pos->details.old_coin_recoup;
241 : struct TALER_ExchangePublicKeyP epub;
242 : struct TALER_ExchangeSignatureP esig;
243 :
244 0 : if (TALER_EC_NONE !=
245 0 : TALER_exchange_online_confirm_recoup_refresh_sign (
246 : &TEH_keys_exchange_sign_,
247 : pr->timestamp,
248 0 : &pr->value,
249 0 : &pr->coin.coin_pub,
250 0 : &pr->old_coin_pub,
251 : &epub,
252 : &esig))
253 : {
254 0 : GNUNET_break (0);
255 0 : json_decref (history);
256 0 : return NULL;
257 : }
258 : /* NOTE: we could also provide coin_pub's coin_sig, denomination key hash and
259 : the denomination key's RSA signature over coin_pub, but as the
260 : wallet should really already have this information (and cannot
261 : check or do anything with it anyway if it doesn't), it seems
262 : strictly unnecessary. */
263 0 : if (0 !=
264 0 : json_array_append_new (
265 : history,
266 0 : GNUNET_JSON_PACK (
267 : GNUNET_JSON_pack_string ("type",
268 : "OLD-COIN-RECOUP"),
269 : TALER_JSON_pack_amount ("amount",
270 : &pr->value),
271 : GNUNET_JSON_pack_data_auto ("exchange_sig",
272 : &esig),
273 : GNUNET_JSON_pack_data_auto ("exchange_pub",
274 : &epub),
275 : GNUNET_JSON_pack_data_auto ("coin_pub",
276 : &pr->coin.coin_pub),
277 : GNUNET_JSON_pack_timestamp ("timestamp",
278 : pr->timestamp))))
279 : {
280 0 : GNUNET_break (0);
281 0 : json_decref (history);
282 0 : return NULL;
283 : }
284 0 : break;
285 : }
286 0 : case TALER_EXCHANGEDB_TT_RECOUP:
287 : {
288 0 : const struct TALER_EXCHANGEDB_RecoupListEntry *recoup =
289 : pos->details.recoup;
290 : struct TALER_ExchangePublicKeyP epub;
291 : struct TALER_ExchangeSignatureP esig;
292 :
293 0 : if (TALER_EC_NONE !=
294 0 : TALER_exchange_online_confirm_recoup_sign (
295 : &TEH_keys_exchange_sign_,
296 : recoup->timestamp,
297 : &recoup->value,
298 : coin_pub,
299 : &recoup->reserve_pub,
300 : &epub,
301 : &esig))
302 : {
303 0 : GNUNET_break (0);
304 0 : json_decref (history);
305 0 : return NULL;
306 : }
307 0 : if (0 !=
308 0 : json_array_append_new (
309 : history,
310 0 : GNUNET_JSON_PACK (
311 : GNUNET_JSON_pack_string ("type",
312 : "RECOUP"),
313 : TALER_JSON_pack_amount ("amount",
314 : &recoup->value),
315 : GNUNET_JSON_pack_data_auto ("exchange_sig",
316 : &esig),
317 : GNUNET_JSON_pack_data_auto ("exchange_pub",
318 : &epub),
319 : GNUNET_JSON_pack_data_auto ("reserve_pub",
320 : &recoup->reserve_pub),
321 : GNUNET_JSON_pack_data_auto ("coin_sig",
322 : &recoup->coin_sig),
323 : GNUNET_JSON_pack_data_auto ("coin_blind",
324 : &recoup->coin_blind),
325 : GNUNET_JSON_pack_data_auto ("reserve_pub",
326 : &recoup->reserve_pub),
327 : GNUNET_JSON_pack_timestamp ("timestamp",
328 : recoup->timestamp))))
329 : {
330 0 : GNUNET_break (0);
331 0 : json_decref (history);
332 0 : return NULL;
333 : }
334 : }
335 0 : break;
336 0 : case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
337 : {
338 0 : struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
339 : pos->details.recoup_refresh;
340 : struct TALER_ExchangePublicKeyP epub;
341 : struct TALER_ExchangeSignatureP esig;
342 :
343 0 : if (TALER_EC_NONE !=
344 0 : TALER_exchange_online_confirm_recoup_refresh_sign (
345 : &TEH_keys_exchange_sign_,
346 : pr->timestamp,
347 0 : &pr->value,
348 : coin_pub,
349 0 : &pr->old_coin_pub,
350 : &epub,
351 : &esig))
352 : {
353 0 : GNUNET_break (0);
354 0 : json_decref (history);
355 0 : return NULL;
356 : }
357 : /* NOTE: we could also provide coin_pub's coin_sig, denomination key
358 : hash and the denomination key's RSA signature over coin_pub, but as
359 : the wallet should really already have this information (and cannot
360 : check or do anything with it anyway if it doesn't), it seems
361 : strictly unnecessary. */
362 0 : if (0 !=
363 0 : json_array_append_new (
364 : history,
365 0 : GNUNET_JSON_PACK (
366 : GNUNET_JSON_pack_string ("type",
367 : "RECOUP-REFRESH"),
368 : TALER_JSON_pack_amount ("amount",
369 : &pr->value),
370 : GNUNET_JSON_pack_data_auto ("exchange_sig",
371 : &esig),
372 : GNUNET_JSON_pack_data_auto ("exchange_pub",
373 : &epub),
374 : GNUNET_JSON_pack_data_auto ("old_coin_pub",
375 : &pr->old_coin_pub),
376 : GNUNET_JSON_pack_data_auto ("coin_sig",
377 : &pr->coin_sig),
378 : GNUNET_JSON_pack_data_auto ("coin_blind",
379 : &pr->coin_blind),
380 : GNUNET_JSON_pack_timestamp ("timestamp",
381 : pr->timestamp))))
382 : {
383 0 : GNUNET_break (0);
384 0 : json_decref (history);
385 0 : return NULL;
386 : }
387 0 : break;
388 : }
389 :
390 0 : case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
391 : {
392 0 : struct TALER_EXCHANGEDB_PurseDepositListEntry *pd
393 : = pos->details.purse_deposit;
394 0 : const struct TALER_AgeCommitmentHash *phac = NULL;
395 :
396 0 : if (! pd->no_age_commitment)
397 0 : phac = &pd->h_age_commitment;
398 :
399 0 : if (0 !=
400 0 : json_array_append_new (
401 : history,
402 0 : GNUNET_JSON_PACK (
403 : GNUNET_JSON_pack_string ("type",
404 : "PURSE-DEPOSIT"),
405 : TALER_JSON_pack_amount ("amount",
406 : &pd->amount),
407 : GNUNET_JSON_pack_string ("exchange_base_url",
408 : NULL == pd->exchange_base_url
409 : ? TEH_base_url
410 : : pd->exchange_base_url),
411 : GNUNET_JSON_pack_allow_null (
412 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
413 : phac)),
414 : GNUNET_JSON_pack_data_auto ("purse_pub",
415 : &pd->purse_pub),
416 : GNUNET_JSON_pack_bool ("refunded",
417 : pd->refunded),
418 : GNUNET_JSON_pack_data_auto ("coin_sig",
419 : &pd->coin_sig))))
420 : {
421 0 : GNUNET_break (0);
422 0 : json_decref (history);
423 0 : return NULL;
424 : }
425 0 : break;
426 : }
427 : }
428 0 : }
429 0 : return history;
430 : }
431 :
432 :
433 : MHD_RESULT
434 0 : TEH_RESPONSE_reply_unknown_denom_pub_hash (
435 : struct MHD_Connection *connection,
436 : const struct TALER_DenominationHashP *dph)
437 : {
438 : struct TALER_ExchangePublicKeyP epub;
439 : struct TALER_ExchangeSignatureP esig;
440 : struct GNUNET_TIME_Timestamp now;
441 : enum TALER_ErrorCode ec;
442 :
443 0 : now = GNUNET_TIME_timestamp_get ();
444 0 : ec = TALER_exchange_online_denomination_unknown_sign (
445 : &TEH_keys_exchange_sign_,
446 : now,
447 : dph,
448 : &epub,
449 : &esig);
450 0 : if (TALER_EC_NONE != ec)
451 : {
452 0 : GNUNET_break (0);
453 0 : return TALER_MHD_reply_with_error (connection,
454 : MHD_HTTP_INTERNAL_SERVER_ERROR,
455 : ec,
456 : NULL);
457 : }
458 0 : return TALER_MHD_REPLY_JSON_PACK (
459 : connection,
460 : MHD_HTTP_NOT_FOUND,
461 : TALER_JSON_pack_ec (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN),
462 : GNUNET_JSON_pack_timestamp ("timestamp",
463 : now),
464 : GNUNET_JSON_pack_data_auto ("exchange_pub",
465 : &epub),
466 : GNUNET_JSON_pack_data_auto ("exchange_sig",
467 : &esig),
468 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
469 : dph));
470 : }
471 :
472 :
473 : MHD_RESULT
474 0 : TEH_RESPONSE_reply_expired_denom_pub_hash (
475 : struct MHD_Connection *connection,
476 : const struct TALER_DenominationHashP *dph,
477 : enum TALER_ErrorCode ec,
478 : const char *oper)
479 : {
480 : struct TALER_ExchangePublicKeyP epub;
481 : struct TALER_ExchangeSignatureP esig;
482 : enum TALER_ErrorCode ecr;
483 : struct GNUNET_TIME_Timestamp now
484 0 : = GNUNET_TIME_timestamp_get ();
485 :
486 0 : ecr = TALER_exchange_online_denomination_expired_sign (
487 : &TEH_keys_exchange_sign_,
488 : now,
489 : dph,
490 : oper,
491 : &epub,
492 : &esig);
493 0 : if (TALER_EC_NONE != ecr)
494 : {
495 0 : GNUNET_break (0);
496 0 : return TALER_MHD_reply_with_error (connection,
497 : MHD_HTTP_INTERNAL_SERVER_ERROR,
498 : ec,
499 : NULL);
500 : }
501 0 : return TALER_MHD_REPLY_JSON_PACK (
502 : connection,
503 : MHD_HTTP_GONE,
504 : TALER_JSON_pack_ec (ec),
505 : GNUNET_JSON_pack_string ("oper",
506 : oper),
507 : GNUNET_JSON_pack_timestamp ("timestamp",
508 : now),
509 : GNUNET_JSON_pack_data_auto ("exchange_pub",
510 : &epub),
511 : GNUNET_JSON_pack_data_auto ("exchange_sig",
512 : &esig),
513 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
514 : dph));
515 : }
516 :
517 :
518 : MHD_RESULT
519 0 : TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
520 : struct MHD_Connection *connection,
521 : const struct TALER_DenominationHashP *dph)
522 : {
523 : struct TALER_ExchangePublicKeyP epub;
524 : struct TALER_ExchangeSignatureP esig;
525 : struct GNUNET_TIME_Timestamp now;
526 : enum TALER_ErrorCode ec;
527 :
528 0 : now = GNUNET_TIME_timestamp_get ();
529 0 : ec = TALER_exchange_online_denomination_unknown_sign (
530 : &TEH_keys_exchange_sign_,
531 : now,
532 : dph,
533 : &epub,
534 : &esig);
535 0 : if (TALER_EC_NONE != ec)
536 : {
537 0 : GNUNET_break (0);
538 0 : return TALER_MHD_reply_with_error (connection,
539 : MHD_HTTP_INTERNAL_SERVER_ERROR,
540 : ec,
541 : NULL);
542 : }
543 0 : return TALER_MHD_REPLY_JSON_PACK (
544 : connection,
545 : MHD_HTTP_NOT_FOUND,
546 : TALER_JSON_pack_ec (
547 : TALER_EC_EXCHANGE_GENERIC_INVALID_DENOMINATION_CIPHER_FOR_OPERATION),
548 : GNUNET_JSON_pack_timestamp ("timestamp",
549 : now),
550 : GNUNET_JSON_pack_data_auto ("exchange_pub",
551 : &epub),
552 : GNUNET_JSON_pack_data_auto ("exchange_sig",
553 : &esig),
554 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
555 : dph));
556 : }
557 :
558 :
559 : MHD_RESULT
560 0 : TEH_RESPONSE_reply_coin_insufficient_funds (
561 : struct MHD_Connection *connection,
562 : enum TALER_ErrorCode ec,
563 : const struct TALER_DenominationHashP *h_denom_pub,
564 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
565 : {
566 : struct TALER_EXCHANGEDB_TransactionList *tl;
567 : enum GNUNET_DB_QueryStatus qs;
568 : json_t *history;
569 :
570 0 : TEH_plugin->rollback (TEH_plugin->cls);
571 0 : if (GNUNET_OK !=
572 0 : TEH_plugin->start_read_only (TEH_plugin->cls,
573 : "get_coin_transactions"))
574 : {
575 0 : return TALER_MHD_reply_with_error (
576 : connection,
577 : MHD_HTTP_INTERNAL_SERVER_ERROR,
578 : TALER_EC_GENERIC_DB_START_FAILED,
579 : NULL);
580 : }
581 0 : qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
582 : coin_pub,
583 : &tl);
584 0 : TEH_plugin->rollback (TEH_plugin->cls);
585 0 : if (0 > qs)
586 : {
587 0 : return TALER_MHD_reply_with_error (
588 : connection,
589 : MHD_HTTP_INTERNAL_SERVER_ERROR,
590 : TALER_EC_GENERIC_DB_FETCH_FAILED,
591 : NULL);
592 : }
593 :
594 0 : history = TEH_RESPONSE_compile_transaction_history (coin_pub,
595 : tl);
596 0 : TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
597 : tl);
598 0 : if (NULL == history)
599 : {
600 0 : GNUNET_break (0);
601 0 : return TALER_MHD_reply_with_error (connection,
602 : MHD_HTTP_INTERNAL_SERVER_ERROR,
603 : TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
604 : "Failed to generated proof of insufficient funds");
605 : }
606 0 : return TALER_MHD_REPLY_JSON_PACK (
607 : connection,
608 : TALER_ErrorCode_get_http_status_safe (ec),
609 : TALER_JSON_pack_ec (ec),
610 : GNUNET_JSON_pack_data_auto ("coin_pub",
611 : coin_pub),
612 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
613 : h_denom_pub),
614 : GNUNET_JSON_pack_array_steal ("history",
615 : history));
616 : }
617 :
618 :
619 : json_t *
620 0 : TEH_RESPONSE_compile_reserve_history (
621 : const struct TALER_EXCHANGEDB_ReserveHistory *rh)
622 : {
623 : json_t *json_history;
624 :
625 0 : json_history = json_array ();
626 0 : for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
627 : NULL != pos;
628 0 : pos = pos->next)
629 : {
630 0 : switch (pos->type)
631 : {
632 0 : case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
633 : {
634 0 : const struct TALER_EXCHANGEDB_BankTransfer *bank =
635 : pos->details.bank;
636 :
637 0 : if (0 !=
638 0 : json_array_append_new (
639 : json_history,
640 0 : GNUNET_JSON_PACK (
641 : GNUNET_JSON_pack_string ("type",
642 : "CREDIT"),
643 : GNUNET_JSON_pack_timestamp ("timestamp",
644 : bank->execution_date),
645 : GNUNET_JSON_pack_string ("sender_account_url",
646 : bank->sender_account_details),
647 : GNUNET_JSON_pack_uint64 ("wire_reference",
648 : bank->wire_reference),
649 : TALER_JSON_pack_amount ("amount",
650 : &bank->amount))))
651 : {
652 0 : GNUNET_break (0);
653 0 : json_decref (json_history);
654 0 : return NULL;
655 : }
656 0 : break;
657 : }
658 0 : case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
659 : {
660 0 : const struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw
661 : = pos->details.withdraw;
662 :
663 0 : if (0 !=
664 0 : json_array_append_new (
665 : json_history,
666 0 : GNUNET_JSON_PACK (
667 : GNUNET_JSON_pack_string ("type",
668 : "WITHDRAW"),
669 : GNUNET_JSON_pack_data_auto ("reserve_sig",
670 : &withdraw->reserve_sig),
671 : GNUNET_JSON_pack_data_auto ("h_coin_envelope",
672 : &withdraw->h_coin_envelope),
673 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
674 : &withdraw->denom_pub_hash),
675 : TALER_JSON_pack_amount ("withdraw_fee",
676 : &withdraw->withdraw_fee),
677 : TALER_JSON_pack_amount ("amount",
678 : &withdraw->amount_with_fee))))
679 : {
680 0 : GNUNET_break (0);
681 0 : json_decref (json_history);
682 0 : return NULL;
683 : }
684 : }
685 0 : break;
686 0 : case TALER_EXCHANGEDB_RO_RECOUP_COIN:
687 : {
688 0 : const struct TALER_EXCHANGEDB_Recoup *recoup
689 : = pos->details.recoup;
690 : struct TALER_ExchangePublicKeyP pub;
691 : struct TALER_ExchangeSignatureP sig;
692 :
693 0 : if (TALER_EC_NONE !=
694 0 : TALER_exchange_online_confirm_recoup_sign (
695 : &TEH_keys_exchange_sign_,
696 : recoup->timestamp,
697 : &recoup->value,
698 : &recoup->coin.coin_pub,
699 : &recoup->reserve_pub,
700 : &pub,
701 : &sig))
702 : {
703 0 : GNUNET_break (0);
704 0 : json_decref (json_history);
705 0 : return NULL;
706 : }
707 :
708 0 : if (0 !=
709 0 : json_array_append_new (
710 : json_history,
711 0 : GNUNET_JSON_PACK (
712 : GNUNET_JSON_pack_string ("type",
713 : "RECOUP"),
714 : GNUNET_JSON_pack_data_auto ("exchange_pub",
715 : &pub),
716 : GNUNET_JSON_pack_data_auto ("exchange_sig",
717 : &sig),
718 : GNUNET_JSON_pack_timestamp ("timestamp",
719 : recoup->timestamp),
720 : TALER_JSON_pack_amount ("amount",
721 : &recoup->value),
722 : GNUNET_JSON_pack_data_auto ("coin_pub",
723 : &recoup->coin.coin_pub))))
724 : {
725 0 : GNUNET_break (0);
726 0 : json_decref (json_history);
727 0 : return NULL;
728 : }
729 : }
730 0 : break;
731 0 : case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
732 : {
733 0 : const struct TALER_EXCHANGEDB_ClosingTransfer *closing =
734 : pos->details.closing;
735 : struct TALER_ExchangePublicKeyP pub;
736 : struct TALER_ExchangeSignatureP sig;
737 :
738 0 : if (TALER_EC_NONE !=
739 0 : TALER_exchange_online_reserve_closed_sign (
740 : &TEH_keys_exchange_sign_,
741 : closing->execution_date,
742 : &closing->amount,
743 : &closing->closing_fee,
744 0 : closing->receiver_account_details,
745 : &closing->wtid,
746 0 : &pos->details.closing->reserve_pub,
747 : &pub,
748 : &sig))
749 : {
750 0 : GNUNET_break (0);
751 0 : json_decref (json_history);
752 0 : return NULL;
753 : }
754 0 : if (0 !=
755 0 : json_array_append_new (
756 : json_history,
757 0 : GNUNET_JSON_PACK (
758 : GNUNET_JSON_pack_string ("type",
759 : "CLOSING"),
760 : GNUNET_JSON_pack_string ("receiver_account_details",
761 : closing->receiver_account_details),
762 : GNUNET_JSON_pack_data_auto ("wtid",
763 : &closing->wtid),
764 : GNUNET_JSON_pack_data_auto ("exchange_pub",
765 : &pub),
766 : GNUNET_JSON_pack_data_auto ("exchange_sig",
767 : &sig),
768 : GNUNET_JSON_pack_timestamp ("timestamp",
769 : closing->execution_date),
770 : TALER_JSON_pack_amount ("amount",
771 : &closing->amount),
772 : TALER_JSON_pack_amount ("closing_fee",
773 : &closing->closing_fee))))
774 : {
775 0 : GNUNET_break (0);
776 0 : json_decref (json_history);
777 0 : return NULL;
778 : }
779 : }
780 0 : break;
781 0 : case TALER_EXCHANGEDB_RO_PURSE_MERGE:
782 : {
783 0 : const struct TALER_EXCHANGEDB_PurseMerge *merge =
784 : pos->details.merge;
785 :
786 0 : if (0 !=
787 0 : json_array_append_new (
788 : json_history,
789 0 : GNUNET_JSON_PACK (
790 : GNUNET_JSON_pack_string ("type",
791 : "MERGE"),
792 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
793 : &merge->h_contract_terms),
794 : GNUNET_JSON_pack_data_auto ("merge_pub",
795 : &merge->merge_pub),
796 : GNUNET_JSON_pack_uint64 ("min_age",
797 : merge->min_age),
798 : GNUNET_JSON_pack_uint64 ("flags",
799 : merge->flags),
800 : GNUNET_JSON_pack_data_auto ("purse_pub",
801 : &merge->purse_pub),
802 : GNUNET_JSON_pack_data_auto ("reserve_sig",
803 : &merge->reserve_sig),
804 : GNUNET_JSON_pack_timestamp ("merge_timestamp",
805 : merge->merge_timestamp),
806 : GNUNET_JSON_pack_timestamp ("purse_expiration",
807 : merge->purse_expiration),
808 : TALER_JSON_pack_amount ("amount",
809 : &merge->amount_with_fee),
810 : TALER_JSON_pack_amount ("purse_fee",
811 : &merge->purse_fee),
812 : GNUNET_JSON_pack_bool ("merged",
813 : merge->merged))))
814 : {
815 0 : GNUNET_break (0);
816 0 : json_decref (json_history);
817 0 : return NULL;
818 : }
819 : }
820 0 : break;
821 0 : case TALER_EXCHANGEDB_RO_HISTORY_REQUEST:
822 : {
823 0 : const struct TALER_EXCHANGEDB_HistoryRequest *history =
824 : pos->details.history;
825 :
826 0 : if (0 !=
827 0 : json_array_append_new (
828 : json_history,
829 0 : GNUNET_JSON_PACK (
830 : GNUNET_JSON_pack_string ("type",
831 : "HISTORY"),
832 : GNUNET_JSON_pack_data_auto ("reserve_sig",
833 : &history->reserve_sig),
834 : GNUNET_JSON_pack_timestamp ("request_timestamp",
835 : history->request_timestamp),
836 : TALER_JSON_pack_amount ("amount",
837 : &history->history_fee))))
838 : {
839 0 : GNUNET_break (0);
840 0 : json_decref (json_history);
841 0 : return NULL;
842 : }
843 : }
844 0 : break;
845 : }
846 0 : }
847 :
848 0 : return json_history;
849 : }
850 :
851 :
852 : /**
853 : * Send reserve history information to client with the
854 : * message that we have insufficient funds for the
855 : * requested withdraw operation.
856 : *
857 : * @param connection connection to the client
858 : * @param ebalance expected balance based on our database
859 : * @param withdraw_amount amount that the client requested to withdraw
860 : * @param rh reserve history to return
861 : * @return MHD result code
862 : */
863 : static MHD_RESULT
864 0 : reply_withdraw_insufficient_funds (
865 : struct MHD_Connection *connection,
866 : const struct TALER_Amount *ebalance,
867 : const struct TALER_Amount *withdraw_amount,
868 : const struct TALER_EXCHANGEDB_ReserveHistory *rh)
869 : {
870 : json_t *json_history;
871 :
872 0 : json_history = TEH_RESPONSE_compile_reserve_history (rh);
873 0 : if (NULL == json_history)
874 0 : return TALER_MHD_reply_with_error (connection,
875 : MHD_HTTP_INTERNAL_SERVER_ERROR,
876 : TALER_EC_EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS,
877 : NULL);
878 0 : return TALER_MHD_REPLY_JSON_PACK (
879 : connection,
880 : MHD_HTTP_CONFLICT,
881 : TALER_JSON_pack_ec (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS),
882 : TALER_JSON_pack_amount ("balance",
883 : ebalance),
884 : TALER_JSON_pack_amount ("requested_amount",
885 : withdraw_amount),
886 : GNUNET_JSON_pack_array_steal ("history",
887 : json_history));
888 : }
889 :
890 :
891 : MHD_RESULT
892 0 : TEH_RESPONSE_reply_reserve_insufficient_balance (
893 : struct MHD_Connection *connection,
894 : const struct TALER_Amount *balance_required,
895 : const struct TALER_ReservePublicKeyP *reserve_pub)
896 : {
897 0 : struct TALER_EXCHANGEDB_ReserveHistory *rh = NULL;
898 : struct TALER_Amount balance;
899 : enum GNUNET_DB_QueryStatus qs;
900 : MHD_RESULT mhd_ret;
901 :
902 0 : if (GNUNET_OK !=
903 0 : TEH_plugin->start_read_only (TEH_plugin->cls,
904 : "get_reserve_history on insufficient balance"))
905 : {
906 0 : GNUNET_break (0);
907 0 : return TALER_MHD_reply_with_error (connection,
908 : MHD_HTTP_INTERNAL_SERVER_ERROR,
909 : TALER_EC_GENERIC_DB_START_FAILED,
910 : NULL);
911 : }
912 : /* The reserve does not have the required amount (actual
913 : * amount + withdraw fee) */
914 0 : qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
915 : reserve_pub,
916 : &balance,
917 : &rh);
918 0 : TEH_plugin->rollback (TEH_plugin->cls);
919 0 : if ( (qs < 0) ||
920 0 : (NULL == rh) )
921 : {
922 0 : return TALER_MHD_reply_with_error (connection,
923 : MHD_HTTP_INTERNAL_SERVER_ERROR,
924 : TALER_EC_GENERIC_DB_FETCH_FAILED,
925 : "reserve history");
926 : }
927 0 : mhd_ret = reply_withdraw_insufficient_funds (
928 : connection,
929 : &balance,
930 : balance_required,
931 : rh);
932 0 : TEH_plugin->free_reserve_history (TEH_plugin->cls,
933 : rh);
934 0 : return mhd_ret;
935 : }
936 :
937 :
938 : MHD_RESULT
939 0 : TEH_RESPONSE_reply_purse_created (
940 : struct MHD_Connection *connection,
941 : struct GNUNET_TIME_Timestamp exchange_timestamp,
942 : const struct TALER_Amount *purse_balance,
943 : const struct TEH_PurseDetails *pd)
944 : {
945 : struct TALER_ExchangePublicKeyP pub;
946 : struct TALER_ExchangeSignatureP sig;
947 : enum TALER_ErrorCode ec;
948 :
949 0 : if (TALER_EC_NONE !=
950 0 : (ec = TALER_exchange_online_purse_created_sign (
951 : &TEH_keys_exchange_sign_,
952 : exchange_timestamp,
953 : pd->purse_expiration,
954 : &pd->target_amount,
955 : purse_balance,
956 : &pd->purse_pub,
957 : &pd->h_contract_terms,
958 : &pub,
959 : &sig)))
960 : {
961 0 : GNUNET_break (0);
962 0 : return TALER_MHD_reply_with_ec (connection,
963 : ec,
964 : NULL);
965 : }
966 0 : return TALER_MHD_REPLY_JSON_PACK (
967 : connection,
968 : MHD_HTTP_OK,
969 : TALER_JSON_pack_amount ("total_deposited",
970 : purse_balance),
971 : GNUNET_JSON_pack_timestamp ("exchange_timestamp",
972 : exchange_timestamp),
973 : GNUNET_JSON_pack_data_auto ("exchange_sig",
974 : &sig),
975 : GNUNET_JSON_pack_data_auto ("exchange_pub",
976 : &pub));
977 : }
978 :
979 :
980 : MHD_RESULT
981 0 : TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection,
982 : const struct TALER_PaytoHashP *h_payto,
983 : const struct TALER_EXCHANGEDB_KycStatus *kyc)
984 : {
985 0 : return TALER_MHD_REPLY_JSON_PACK (
986 : connection,
987 : MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
988 : GNUNET_JSON_pack_data_auto ("h_payto",
989 : h_payto),
990 : GNUNET_JSON_pack_uint64 ("requirement_row",
991 : kyc->requirement_row));
992 : }
993 :
994 :
995 : /* end of taler-exchange-httpd_responses.c */
|