Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 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_purses_create.c
18 : * @brief Handle /purses/$PID/create requests; parses the POST and JSON and
19 : * verifies the coin signature before handing things off
20 : * to the database.
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 : #include <jansson.h>
27 : #include <microhttpd.h>
28 : #include "taler_json_lib.h"
29 : #include "taler_mhd_lib.h"
30 : #include "taler-exchange-httpd_common_deposit.h"
31 : #include "taler-exchange-httpd_purses_create.h"
32 : #include "taler-exchange-httpd_responses.h"
33 : #include "taler_exchangedb_lib.h"
34 : #include "taler-exchange-httpd_keys.h"
35 :
36 :
37 : /**
38 : * Closure for #create_transaction.
39 : */
40 : struct PurseCreateContext
41 : {
42 :
43 : /**
44 : * Total actually deposited by all the coins.
45 : */
46 : struct TALER_Amount deposit_total;
47 :
48 : /**
49 : * Our current time.
50 : */
51 : struct GNUNET_TIME_Timestamp exchange_timestamp;
52 :
53 : /**
54 : * Merge key for the purse.
55 : */
56 : struct TALER_PurseMergePublicKeyP merge_pub;
57 :
58 : /**
59 : * Encrypted contract of for the purse.
60 : */
61 : struct TALER_EncryptedContract econtract;
62 :
63 : /**
64 : * Signature of the client affiming this request.
65 : */
66 : struct TALER_PurseContractSignatureP purse_sig;
67 :
68 : /**
69 : * Fundamental details about the purse.
70 : */
71 : struct TEH_PurseDetails pd;
72 :
73 : /**
74 : * Array of coins being deposited.
75 : */
76 : struct TEH_PurseDepositedCoin *coins;
77 :
78 : /**
79 : * Length of the @e coins array.
80 : */
81 : unsigned int num_coins;
82 :
83 : /**
84 : * Minimum age for deposits into this purse.
85 : */
86 : uint32_t min_age;
87 :
88 : /**
89 : * Do we have an @e econtract?
90 : */
91 : bool no_econtract;
92 :
93 : };
94 :
95 :
96 : /**
97 : * Execute database transaction for /purses/$PID/create. Runs the transaction
98 : * logic; IF it returns a non-error code, the transaction logic MUST NOT queue
99 : * a MHD response. IF it returns an hard error, the transaction logic MUST
100 : * queue a MHD response and set @a mhd_ret. IF it returns the soft error
101 : * code, the function MAY be called again to retry and MUST not queue a MHD
102 : * response.
103 : *
104 : * @param cls a `struct PurseCreateContext`
105 : * @param connection MHD request context
106 : * @param[out] mhd_ret set to MHD status on error
107 : * @return transaction status
108 : */
109 : static enum GNUNET_DB_QueryStatus
110 0 : create_transaction (void *cls,
111 : struct MHD_Connection *connection,
112 : MHD_RESULT *mhd_ret)
113 : {
114 0 : struct PurseCreateContext *pcc = cls;
115 : enum GNUNET_DB_QueryStatus qs;
116 : struct TALER_Amount purse_fee;
117 0 : bool in_conflict = true;
118 :
119 0 : GNUNET_assert (GNUNET_OK ==
120 : TALER_amount_set_zero (TEH_currency,
121 : &purse_fee));
122 : /* 1) create purse */
123 0 : qs = TEH_plugin->insert_purse_request (
124 0 : TEH_plugin->cls,
125 0 : &pcc->pd.purse_pub,
126 0 : &pcc->merge_pub,
127 : pcc->pd.purse_expiration,
128 0 : &pcc->pd.h_contract_terms,
129 : pcc->min_age,
130 : TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE,
131 : &purse_fee,
132 0 : &pcc->pd.target_amount,
133 0 : &pcc->purse_sig,
134 : &in_conflict);
135 0 : if (qs < 0)
136 : {
137 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
138 0 : return qs;
139 0 : TALER_LOG_WARNING (
140 : "Failed to store create purse information in database\n");
141 0 : *mhd_ret =
142 0 : TALER_MHD_reply_with_error (connection,
143 : MHD_HTTP_INTERNAL_SERVER_ERROR,
144 : TALER_EC_GENERIC_DB_STORE_FAILED,
145 : "purse create");
146 0 : return GNUNET_DB_STATUS_HARD_ERROR;
147 : }
148 0 : if (in_conflict)
149 : {
150 : struct TALER_PurseMergePublicKeyP merge_pub;
151 : struct GNUNET_TIME_Timestamp purse_expiration;
152 : struct TALER_PrivateContractHashP h_contract_terms;
153 : struct TALER_Amount target_amount;
154 : struct TALER_Amount balance;
155 : struct TALER_PurseContractSignatureP purse_sig;
156 : uint32_t min_age;
157 :
158 0 : TEH_plugin->rollback (TEH_plugin->cls);
159 0 : qs = TEH_plugin->select_purse_request (TEH_plugin->cls,
160 0 : &pcc->pd.purse_pub,
161 : &merge_pub,
162 : &purse_expiration,
163 : &h_contract_terms,
164 : &min_age,
165 : &target_amount,
166 : &balance,
167 : &purse_sig);
168 0 : if (qs < 0)
169 : {
170 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
171 0 : TALER_LOG_WARNING ("Failed to fetch purse information from database\n");
172 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
173 : MHD_HTTP_INTERNAL_SERVER_ERROR,
174 : TALER_EC_GENERIC_DB_FETCH_FAILED,
175 : "select purse request");
176 0 : return GNUNET_DB_STATUS_HARD_ERROR;
177 : }
178 : *mhd_ret
179 0 : = TALER_MHD_REPLY_JSON_PACK (
180 : connection,
181 : MHD_HTTP_CONFLICT,
182 : TALER_JSON_pack_ec (
183 : TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA),
184 : TALER_JSON_pack_amount ("amount",
185 : &target_amount),
186 : GNUNET_JSON_pack_uint64 ("min_age",
187 : min_age),
188 : GNUNET_JSON_pack_timestamp ("purse_expiration",
189 : purse_expiration),
190 : GNUNET_JSON_pack_data_auto ("purse_sig",
191 : &purse_sig),
192 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
193 : &h_contract_terms),
194 : GNUNET_JSON_pack_data_auto ("merge_pub",
195 : &merge_pub));
196 0 : return GNUNET_DB_STATUS_HARD_ERROR;
197 : }
198 : /* 2) deposit all coins */
199 0 : for (unsigned int i = 0; i<pcc->num_coins; i++)
200 : {
201 0 : struct TEH_PurseDepositedCoin *coin = &pcc->coins[i];
202 0 : bool balance_ok = false;
203 0 : bool conflict = true;
204 :
205 0 : qs = TEH_make_coin_known (&coin->cpi,
206 : connection,
207 : &coin->known_coin_id,
208 : mhd_ret);
209 0 : if (qs < 0)
210 0 : return qs;
211 0 : qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls,
212 0 : &pcc->pd.purse_pub,
213 0 : &coin->cpi.coin_pub,
214 0 : &coin->amount,
215 0 : &coin->coin_sig,
216 0 : &coin->amount_minus_fee,
217 : &balance_ok,
218 : &conflict);
219 0 : if (qs <= 0)
220 : {
221 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
222 0 : return qs;
223 0 : GNUNET_break (0 != qs);
224 0 : TALER_LOG_WARNING (
225 : "Failed to store purse deposit information in database\n");
226 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
227 : MHD_HTTP_INTERNAL_SERVER_ERROR,
228 : TALER_EC_GENERIC_DB_STORE_FAILED,
229 : "purse create deposit");
230 0 : return GNUNET_DB_STATUS_HARD_ERROR;
231 : }
232 0 : if (! balance_ok)
233 : {
234 : *mhd_ret
235 0 : = TEH_RESPONSE_reply_coin_insufficient_funds (
236 : connection,
237 : TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
238 0 : &coin->cpi.denom_pub_hash,
239 0 : &coin->cpi.coin_pub);
240 0 : return GNUNET_DB_STATUS_HARD_ERROR;
241 : }
242 0 : if (conflict)
243 : {
244 : struct TALER_Amount amount;
245 : struct TALER_CoinSpendPublicKeyP coin_pub;
246 : struct TALER_CoinSpendSignatureP coin_sig;
247 : struct TALER_DenominationHashP h_denom_pub;
248 : struct TALER_AgeCommitmentHash phac;
249 0 : char *partner_url = NULL;
250 :
251 0 : TEH_plugin->rollback (TEH_plugin->cls);
252 0 : qs = TEH_plugin->get_purse_deposit (TEH_plugin->cls,
253 0 : &pcc->pd.purse_pub,
254 0 : &coin->cpi.coin_pub,
255 : &amount,
256 : &h_denom_pub,
257 : &phac,
258 : &coin_sig,
259 : &partner_url);
260 0 : if (qs < 0)
261 : {
262 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
263 0 : TALER_LOG_WARNING (
264 : "Failed to fetch purse deposit information from database\n");
265 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
266 : MHD_HTTP_INTERNAL_SERVER_ERROR,
267 : TALER_EC_GENERIC_DB_FETCH_FAILED,
268 : "get purse deposit");
269 0 : return GNUNET_DB_STATUS_HARD_ERROR;
270 : }
271 :
272 : *mhd_ret
273 0 : = TALER_MHD_REPLY_JSON_PACK (
274 : connection,
275 : MHD_HTTP_CONFLICT,
276 : TALER_JSON_pack_ec (
277 : TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA),
278 : GNUNET_JSON_pack_data_auto ("coin_pub",
279 : &coin_pub),
280 : GNUNET_JSON_pack_data_auto ("coin_sig",
281 : &coin_sig),
282 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
283 : &h_denom_pub),
284 : GNUNET_JSON_pack_data_auto ("h_age_restrictions",
285 : &phac),
286 : GNUNET_JSON_pack_allow_null (
287 : GNUNET_JSON_pack_string ("partner_url",
288 : partner_url)),
289 : TALER_JSON_pack_amount ("amount",
290 : &amount));
291 0 : GNUNET_free (partner_url);
292 0 : return GNUNET_DB_STATUS_HARD_ERROR;
293 : }
294 : }
295 : /* 3) if present, persist contract */
296 0 : if (! pcc->no_econtract)
297 : {
298 0 : in_conflict = true;
299 0 : qs = TEH_plugin->insert_contract (TEH_plugin->cls,
300 0 : &pcc->pd.purse_pub,
301 0 : &pcc->econtract,
302 : &in_conflict);
303 0 : if (qs < 0)
304 : {
305 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
306 0 : return qs;
307 0 : TALER_LOG_WARNING ("Failed to store purse information in database\n");
308 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
309 : MHD_HTTP_INTERNAL_SERVER_ERROR,
310 : TALER_EC_GENERIC_DB_STORE_FAILED,
311 : "purse create contract");
312 0 : return GNUNET_DB_STATUS_HARD_ERROR;
313 : }
314 0 : if (in_conflict)
315 : {
316 : struct TALER_EncryptedContract econtract;
317 : struct GNUNET_HashCode h_econtract;
318 :
319 0 : qs = TEH_plugin->select_contract_by_purse (
320 0 : TEH_plugin->cls,
321 0 : &pcc->pd.purse_pub,
322 : &econtract);
323 0 : if (qs <= 0)
324 : {
325 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
326 0 : return qs;
327 0 : GNUNET_break (0 != qs);
328 0 : TALER_LOG_WARNING (
329 : "Failed to store fetch contract information from database\n");
330 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
331 : MHD_HTTP_INTERNAL_SERVER_ERROR,
332 : TALER_EC_GENERIC_DB_FETCH_FAILED,
333 : "select contract");
334 0 : return GNUNET_DB_STATUS_HARD_ERROR;
335 : }
336 0 : GNUNET_CRYPTO_hash (econtract.econtract,
337 : econtract.econtract_size,
338 : &h_econtract);
339 : *mhd_ret
340 0 : = TALER_MHD_REPLY_JSON_PACK (
341 : connection,
342 : MHD_HTTP_CONFLICT,
343 : TALER_JSON_pack_ec (
344 : TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA),
345 : GNUNET_JSON_pack_data_auto ("h_econtract",
346 : &h_econtract),
347 : GNUNET_JSON_pack_data_auto ("econtract_sig",
348 : &econtract.econtract_sig),
349 : GNUNET_JSON_pack_data_auto ("contract_pub",
350 : &econtract.contract_pub));
351 0 : GNUNET_free (econtract.econtract);
352 0 : return GNUNET_DB_STATUS_HARD_ERROR;
353 : }
354 : }
355 0 : return qs;
356 : }
357 :
358 :
359 : /**
360 : * Parse a coin and check signature of the coin and the denomination
361 : * signature over the coin.
362 : *
363 : * @param[in,out] connection our HTTP connection
364 : * @param[in,out] pcc request context
365 : * @param[out] coin coin to initialize
366 : * @param jcoin coin to parse
367 : * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
368 : * #GNUNET_SYSERR on failure and no error could be returned
369 : */
370 : static enum GNUNET_GenericReturnValue
371 0 : parse_coin (struct MHD_Connection *connection,
372 : struct PurseCreateContext *pcc,
373 : struct TEH_PurseDepositedCoin *coin,
374 : const json_t *jcoin)
375 : {
376 : enum GNUNET_GenericReturnValue iret;
377 :
378 0 : if (GNUNET_OK !=
379 0 : (iret = TEH_common_purse_deposit_parse_coin (connection,
380 : coin,
381 : jcoin)))
382 0 : return iret;
383 0 : if (GNUNET_OK !=
384 0 : (iret = TEH_common_deposit_check_purse_deposit (
385 : connection,
386 : coin,
387 0 : &pcc->pd.purse_pub,
388 : pcc->min_age)))
389 0 : return iret;
390 0 : if (0 >
391 0 : TALER_amount_add (&pcc->deposit_total,
392 0 : &pcc->deposit_total,
393 0 : &coin->amount_minus_fee))
394 : {
395 0 : GNUNET_break (0);
396 : return (MHD_YES ==
397 0 : TALER_MHD_reply_with_error (connection,
398 : MHD_HTTP_INTERNAL_SERVER_ERROR,
399 : TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
400 : "total deposit contribution"))
401 : ? GNUNET_NO
402 0 : : GNUNET_SYSERR;
403 : }
404 0 : return GNUNET_OK;
405 : }
406 :
407 :
408 : MHD_RESULT
409 0 : TEH_handler_purses_create (
410 : struct MHD_Connection *connection,
411 : const struct TALER_PurseContractPublicKeyP *purse_pub,
412 : const json_t *root)
413 : {
414 0 : struct PurseCreateContext pcc = {
415 : .pd.purse_pub = *purse_pub,
416 0 : .exchange_timestamp = GNUNET_TIME_timestamp_get ()
417 : };
418 : json_t *deposits;
419 : json_t *deposit;
420 : unsigned int idx;
421 : struct GNUNET_JSON_Specification spec[] = {
422 0 : TALER_JSON_spec_amount ("amount",
423 : TEH_currency,
424 : &pcc.pd.target_amount),
425 0 : GNUNET_JSON_spec_uint32 ("min_age",
426 : &pcc.min_age),
427 0 : GNUNET_JSON_spec_mark_optional (
428 : TALER_JSON_spec_econtract ("econtract",
429 : &pcc.econtract),
430 : &pcc.no_econtract),
431 0 : GNUNET_JSON_spec_fixed_auto ("merge_pub",
432 : &pcc.merge_pub),
433 0 : GNUNET_JSON_spec_fixed_auto ("purse_sig",
434 : &pcc.purse_sig),
435 0 : GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
436 : &pcc.pd.h_contract_terms),
437 0 : GNUNET_JSON_spec_json ("deposits",
438 : &deposits),
439 0 : GNUNET_JSON_spec_timestamp ("purse_expiration",
440 : &pcc.pd.purse_expiration),
441 0 : GNUNET_JSON_spec_end ()
442 : };
443 : const struct TEH_GlobalFee *gf;
444 :
445 : {
446 : enum GNUNET_GenericReturnValue res;
447 :
448 0 : res = TALER_MHD_parse_json_data (connection,
449 : root,
450 : spec);
451 0 : if (GNUNET_SYSERR == res)
452 : {
453 0 : GNUNET_break (0);
454 0 : return MHD_NO; /* hard failure */
455 : }
456 0 : if (GNUNET_NO == res)
457 : {
458 0 : GNUNET_break_op (0);
459 0 : return MHD_YES; /* failure */
460 : }
461 : }
462 0 : GNUNET_assert (GNUNET_OK ==
463 : TALER_amount_set_zero (TEH_currency,
464 : &pcc.deposit_total));
465 0 : if (GNUNET_TIME_timestamp_cmp (pcc.pd.purse_expiration,
466 : <,
467 : pcc.exchange_timestamp))
468 : {
469 0 : GNUNET_break_op (0);
470 0 : GNUNET_JSON_parse_free (spec);
471 0 : return TALER_MHD_reply_with_error (connection,
472 : MHD_HTTP_BAD_REQUEST,
473 : TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_BEFORE_NOW,
474 : NULL);
475 : }
476 0 : if (GNUNET_TIME_absolute_is_never (pcc.pd.purse_expiration.abs_time))
477 : {
478 0 : GNUNET_break_op (0);
479 0 : GNUNET_JSON_parse_free (spec);
480 0 : return TALER_MHD_reply_with_error (connection,
481 : MHD_HTTP_BAD_REQUEST,
482 : TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_IS_NEVER,
483 : NULL);
484 : }
485 0 : pcc.num_coins = json_array_size (deposits);
486 0 : if ( (0 == pcc.num_coins) ||
487 0 : (pcc.num_coins > TALER_MAX_FRESH_COINS) )
488 : {
489 0 : GNUNET_break_op (0);
490 0 : GNUNET_JSON_parse_free (spec);
491 0 : return TALER_MHD_reply_with_error (connection,
492 : MHD_HTTP_BAD_REQUEST,
493 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
494 : "deposits");
495 : }
496 : {
497 : struct TEH_KeyStateHandle *keys;
498 :
499 0 : keys = TEH_keys_get_state ();
500 0 : if (NULL == keys)
501 : {
502 0 : GNUNET_break (0);
503 0 : GNUNET_JSON_parse_free (spec);
504 0 : return TALER_MHD_reply_with_error (connection,
505 : MHD_HTTP_INTERNAL_SERVER_ERROR,
506 : TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
507 : NULL);
508 : }
509 0 : gf = TEH_keys_global_fee_by_time (keys,
510 : pcc.exchange_timestamp);
511 : }
512 0 : if (NULL == gf)
513 : {
514 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
515 : "Cannot create purse: global fees not configured!\n");
516 0 : return TALER_MHD_reply_with_error (connection,
517 : MHD_HTTP_INTERNAL_SERVER_ERROR,
518 : TALER_EC_EXCHANGE_GENERIC_GLOBAL_FEES_MISSING,
519 : NULL);
520 : }
521 : /* parse deposits */
522 0 : pcc.coins = GNUNET_new_array (pcc.num_coins,
523 : struct TEH_PurseDepositedCoin);
524 0 : json_array_foreach (deposits, idx, deposit)
525 : {
526 : enum GNUNET_GenericReturnValue res;
527 0 : struct TEH_PurseDepositedCoin *coin = &pcc.coins[idx];
528 :
529 0 : res = parse_coin (connection,
530 : &pcc,
531 : coin,
532 : deposit);
533 0 : if (GNUNET_OK != res)
534 : {
535 0 : GNUNET_JSON_parse_free (spec);
536 0 : for (unsigned int i = 0; i<idx; i++)
537 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
538 0 : GNUNET_free (pcc.coins);
539 0 : return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
540 : }
541 : }
542 :
543 0 : if (0 < TALER_amount_cmp (&gf->fees.purse,
544 : &pcc.deposit_total))
545 : {
546 0 : GNUNET_break_op (0);
547 0 : GNUNET_JSON_parse_free (spec);
548 0 : GNUNET_free (pcc.coins);
549 0 : return TALER_MHD_reply_with_error (connection,
550 : MHD_HTTP_BAD_REQUEST,
551 : TALER_EC_EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE,
552 : NULL);
553 : }
554 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
555 :
556 0 : if (GNUNET_OK !=
557 0 : TALER_wallet_purse_create_verify (
558 : pcc.pd.purse_expiration,
559 : &pcc.pd.h_contract_terms,
560 : &pcc.merge_pub,
561 : pcc.min_age,
562 : &pcc.pd.target_amount,
563 : &pcc.pd.purse_pub,
564 : &pcc.purse_sig))
565 : {
566 0 : TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
567 0 : GNUNET_JSON_parse_free (spec);
568 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
569 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
570 0 : GNUNET_free (pcc.coins);
571 0 : return TALER_MHD_reply_with_error (connection,
572 : MHD_HTTP_FORBIDDEN,
573 : TALER_EC_EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID,
574 : NULL);
575 : }
576 0 : if ( (! pcc.no_econtract) &&
577 : (GNUNET_OK !=
578 0 : TALER_wallet_econtract_upload_verify (pcc.econtract.econtract,
579 : pcc.econtract.econtract_size,
580 : &pcc.econtract.contract_pub,
581 : purse_pub,
582 : &pcc.econtract.econtract_sig)) )
583 : {
584 0 : TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
585 0 : GNUNET_JSON_parse_free (spec);
586 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
587 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
588 0 : GNUNET_free (pcc.coins);
589 0 : return TALER_MHD_reply_with_error (connection,
590 : MHD_HTTP_FORBIDDEN,
591 : TALER_EC_EXCHANGE_PURSE_ECONTRACT_SIGNATURE_INVALID,
592 : NULL);
593 : }
594 :
595 :
596 0 : if (GNUNET_SYSERR ==
597 0 : TEH_plugin->preflight (TEH_plugin->cls))
598 : {
599 0 : GNUNET_break (0);
600 0 : GNUNET_JSON_parse_free (spec);
601 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
602 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
603 0 : GNUNET_free (pcc.coins);
604 0 : return TALER_MHD_reply_with_error (connection,
605 : MHD_HTTP_INTERNAL_SERVER_ERROR,
606 : TALER_EC_GENERIC_DB_START_FAILED,
607 : "preflight failure");
608 : }
609 :
610 : /* execute transaction */
611 : {
612 : MHD_RESULT mhd_ret;
613 :
614 0 : if (GNUNET_OK !=
615 0 : TEH_DB_run_transaction (connection,
616 : "execute purse create",
617 : TEH_MT_REQUEST_PURSE_CREATE,
618 : &mhd_ret,
619 : &create_transaction,
620 : &pcc))
621 : {
622 0 : GNUNET_JSON_parse_free (spec);
623 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
624 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
625 0 : GNUNET_free (pcc.coins);
626 0 : return mhd_ret;
627 : }
628 : }
629 :
630 : /* generate regular response */
631 : {
632 : MHD_RESULT res;
633 :
634 0 : res = TEH_RESPONSE_reply_purse_created (connection,
635 : pcc.exchange_timestamp,
636 : &pcc.deposit_total,
637 : &pcc.pd);
638 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
639 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
640 0 : GNUNET_free (pcc.coins);
641 0 : GNUNET_JSON_parse_free (spec);
642 0 : return res;
643 : }
644 : }
645 :
646 :
647 : /* end of taler-exchange-httpd_purses_create.c */
|