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 "taler/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/taler_json_lib.h"
29 : #include "taler/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/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 11 : create_transaction (void *cls,
111 : struct MHD_Connection *connection,
112 : MHD_RESULT *mhd_ret)
113 : {
114 11 : struct PurseCreateContext *pcc = cls;
115 : enum GNUNET_DB_QueryStatus qs;
116 : struct TALER_Amount purse_fee;
117 11 : bool in_conflict = true;
118 :
119 11 : GNUNET_assert (GNUNET_OK ==
120 : TALER_amount_set_zero (TEH_currency,
121 : &purse_fee));
122 : /* 1) create purse */
123 11 : qs = TEH_plugin->insert_purse_request (
124 11 : TEH_plugin->cls,
125 11 : &pcc->pd.purse_pub,
126 11 : &pcc->merge_pub,
127 : pcc->pd.purse_expiration,
128 11 : &pcc->pd.h_contract_terms,
129 : pcc->min_age,
130 : TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE,
131 : &purse_fee,
132 11 : &pcc->pd.target_amount,
133 11 : &pcc->purse_sig,
134 : &in_conflict);
135 11 : 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 11 : 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->get_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 20 : for (unsigned int i = 0; i<pcc->num_coins; i++)
200 : {
201 11 : struct TEH_PurseDepositedCoin *coin = &pcc->coins[i];
202 11 : bool balance_ok = false;
203 11 : bool conflict = true;
204 11 : bool too_late = true;
205 :
206 11 : qs = TEH_make_coin_known (&coin->cpi,
207 : connection,
208 : &coin->known_coin_id,
209 : mhd_ret);
210 11 : if (qs < 0)
211 2 : return qs;
212 11 : qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls,
213 11 : &pcc->pd.purse_pub,
214 11 : &coin->cpi.coin_pub,
215 11 : &coin->amount,
216 11 : &coin->coin_sig,
217 11 : &coin->amount_minus_fee,
218 : &balance_ok,
219 : &too_late,
220 : &conflict);
221 11 : if (qs <= 0)
222 : {
223 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
224 0 : return qs;
225 0 : GNUNET_break (0 != qs);
226 0 : TALER_LOG_WARNING (
227 : "Failed to store purse deposit information in database\n");
228 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
229 : MHD_HTTP_INTERNAL_SERVER_ERROR,
230 : TALER_EC_GENERIC_DB_STORE_FAILED,
231 : "purse create deposit");
232 0 : return GNUNET_DB_STATUS_HARD_ERROR;
233 : }
234 11 : if (! balance_ok)
235 : {
236 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
237 : "Coin %s has insufficient balance for purse deposit of amount %s\n",
238 : TALER_B2S (&coin->cpi.coin_pub),
239 : TALER_amount2s (&coin->amount));
240 : *mhd_ret
241 4 : = TEH_RESPONSE_reply_coin_insufficient_funds (
242 : connection,
243 : TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
244 2 : &coin->cpi.denom_pub_hash,
245 2 : &coin->cpi.coin_pub);
246 2 : return GNUNET_DB_STATUS_HARD_ERROR;
247 : }
248 9 : if (too_late)
249 : {
250 : *mhd_ret
251 0 : = TALER_MHD_reply_with_ec (
252 : connection,
253 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
254 : "too late to deposit on purse creation");
255 0 : return GNUNET_DB_STATUS_HARD_ERROR;
256 : }
257 9 : if (conflict)
258 : {
259 : struct TALER_Amount amount;
260 : struct TALER_CoinSpendPublicKeyP coin_pub;
261 : struct TALER_CoinSpendSignatureP coin_sig;
262 : struct TALER_DenominationHashP h_denom_pub;
263 : struct TALER_AgeCommitmentHashP phac;
264 0 : char *partner_url = NULL;
265 :
266 0 : TEH_plugin->rollback (TEH_plugin->cls);
267 0 : qs = TEH_plugin->get_purse_deposit (TEH_plugin->cls,
268 0 : &pcc->pd.purse_pub,
269 0 : &coin->cpi.coin_pub,
270 : &amount,
271 : &h_denom_pub,
272 : &phac,
273 : &coin_sig,
274 : &partner_url);
275 0 : if (qs < 0)
276 : {
277 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
278 0 : TALER_LOG_WARNING (
279 : "Failed to fetch purse deposit information from database\n");
280 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
281 : MHD_HTTP_INTERNAL_SERVER_ERROR,
282 : TALER_EC_GENERIC_DB_FETCH_FAILED,
283 : "get purse deposit");
284 0 : return GNUNET_DB_STATUS_HARD_ERROR;
285 : }
286 :
287 : *mhd_ret
288 0 : = TALER_MHD_REPLY_JSON_PACK (
289 : connection,
290 : MHD_HTTP_CONFLICT,
291 : TALER_JSON_pack_ec (
292 : TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA),
293 : GNUNET_JSON_pack_data_auto ("coin_pub",
294 : &coin_pub),
295 : GNUNET_JSON_pack_data_auto ("coin_sig",
296 : &coin_sig),
297 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
298 : &h_denom_pub),
299 : GNUNET_JSON_pack_data_auto ("h_age_restrictions",
300 : &phac),
301 : GNUNET_JSON_pack_allow_null (
302 : GNUNET_JSON_pack_string ("partner_url",
303 : partner_url)),
304 : TALER_JSON_pack_amount ("amount",
305 : &amount));
306 0 : GNUNET_free (partner_url);
307 0 : return GNUNET_DB_STATUS_HARD_ERROR;
308 : }
309 : }
310 : /* 3) if present, persist contract */
311 9 : if (! pcc->no_econtract)
312 : {
313 9 : in_conflict = true;
314 9 : qs = TEH_plugin->insert_contract (TEH_plugin->cls,
315 9 : &pcc->pd.purse_pub,
316 9 : &pcc->econtract,
317 : &in_conflict);
318 9 : if (qs < 0)
319 : {
320 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
321 0 : return qs;
322 0 : TALER_LOG_WARNING ("Failed to store purse information in database\n");
323 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
324 : MHD_HTTP_INTERNAL_SERVER_ERROR,
325 : TALER_EC_GENERIC_DB_STORE_FAILED,
326 : "purse create contract");
327 0 : return GNUNET_DB_STATUS_HARD_ERROR;
328 : }
329 9 : if (in_conflict)
330 : {
331 : struct TALER_EncryptedContract econtract;
332 : struct GNUNET_HashCode h_econtract;
333 :
334 0 : qs = TEH_plugin->select_contract_by_purse (
335 0 : TEH_plugin->cls,
336 0 : &pcc->pd.purse_pub,
337 : &econtract);
338 0 : if (qs <= 0)
339 : {
340 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
341 0 : return qs;
342 0 : GNUNET_break (0 != qs);
343 0 : TALER_LOG_WARNING (
344 : "Failed to store fetch contract information from database\n");
345 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
346 : MHD_HTTP_INTERNAL_SERVER_ERROR,
347 : TALER_EC_GENERIC_DB_FETCH_FAILED,
348 : "select contract");
349 0 : return GNUNET_DB_STATUS_HARD_ERROR;
350 : }
351 0 : GNUNET_CRYPTO_hash (econtract.econtract,
352 : econtract.econtract_size,
353 : &h_econtract);
354 : *mhd_ret
355 0 : = TALER_MHD_REPLY_JSON_PACK (
356 : connection,
357 : MHD_HTTP_CONFLICT,
358 : TALER_JSON_pack_ec (
359 : TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA),
360 : GNUNET_JSON_pack_data_auto ("h_econtract",
361 : &h_econtract),
362 : GNUNET_JSON_pack_data_auto ("econtract_sig",
363 : &econtract.econtract_sig),
364 : GNUNET_JSON_pack_data_auto ("contract_pub",
365 : &econtract.contract_pub));
366 0 : GNUNET_free (econtract.econtract);
367 0 : return GNUNET_DB_STATUS_HARD_ERROR;
368 : }
369 : }
370 9 : return qs;
371 : }
372 :
373 :
374 : /**
375 : * Parse a coin and check signature of the coin and the denomination
376 : * signature over the coin.
377 : *
378 : * @param[in,out] connection our HTTP connection
379 : * @param[in,out] pcc request context
380 : * @param[out] coin coin to initialize
381 : * @param jcoin coin to parse
382 : * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
383 : * #GNUNET_SYSERR on failure and no error could be returned
384 : */
385 : static enum GNUNET_GenericReturnValue
386 11 : parse_coin (struct MHD_Connection *connection,
387 : struct PurseCreateContext *pcc,
388 : struct TEH_PurseDepositedCoin *coin,
389 : const json_t *jcoin)
390 : {
391 : enum GNUNET_GenericReturnValue iret;
392 :
393 11 : if (GNUNET_OK !=
394 11 : (iret = TEH_common_purse_deposit_parse_coin (connection,
395 : coin,
396 : jcoin)))
397 0 : return iret;
398 11 : if (GNUNET_OK !=
399 11 : (iret = TEH_common_deposit_check_purse_deposit (
400 : connection,
401 : coin,
402 11 : &pcc->pd.purse_pub,
403 : pcc->min_age)))
404 0 : return iret;
405 11 : if (0 >
406 11 : TALER_amount_add (&pcc->deposit_total,
407 11 : &pcc->deposit_total,
408 11 : &coin->amount_minus_fee))
409 : {
410 0 : GNUNET_break (0);
411 : return (MHD_YES ==
412 0 : TALER_MHD_reply_with_error (connection,
413 : MHD_HTTP_INTERNAL_SERVER_ERROR,
414 : TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
415 : "total deposit contribution"))
416 : ? GNUNET_NO
417 0 : : GNUNET_SYSERR;
418 : }
419 11 : return GNUNET_OK;
420 : }
421 :
422 :
423 : MHD_RESULT
424 11 : TEH_handler_purses_create (
425 : struct TEH_RequestContext *rc,
426 : const struct TALER_PurseContractPublicKeyP *purse_pub,
427 : const json_t *root)
428 : {
429 11 : struct MHD_Connection *connection = rc->connection;
430 22 : struct PurseCreateContext pcc = {
431 : .pd.purse_pub = *purse_pub,
432 11 : .exchange_timestamp = GNUNET_TIME_timestamp_get ()
433 : };
434 : const json_t *deposits;
435 : json_t *deposit;
436 : unsigned int idx;
437 : struct GNUNET_JSON_Specification spec[] = {
438 11 : TALER_JSON_spec_amount ("amount",
439 : TEH_currency,
440 : &pcc.pd.target_amount),
441 11 : GNUNET_JSON_spec_uint32 ("min_age",
442 : &pcc.min_age),
443 11 : GNUNET_JSON_spec_mark_optional (
444 : TALER_JSON_spec_econtract ("econtract",
445 : &pcc.econtract),
446 : &pcc.no_econtract),
447 11 : GNUNET_JSON_spec_fixed_auto ("merge_pub",
448 : &pcc.merge_pub),
449 11 : GNUNET_JSON_spec_fixed_auto ("purse_sig",
450 : &pcc.purse_sig),
451 11 : GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
452 : &pcc.pd.h_contract_terms),
453 11 : GNUNET_JSON_spec_array_const ("deposits",
454 : &deposits),
455 11 : GNUNET_JSON_spec_timestamp ("purse_expiration",
456 : &pcc.pd.purse_expiration),
457 11 : GNUNET_JSON_spec_end ()
458 : };
459 : const struct TEH_GlobalFee *gf;
460 :
461 : {
462 : enum GNUNET_GenericReturnValue res;
463 :
464 11 : res = TALER_MHD_parse_json_data (connection,
465 : root,
466 : spec);
467 11 : if (GNUNET_SYSERR == res)
468 : {
469 0 : GNUNET_break (0);
470 0 : return MHD_NO; /* hard failure */
471 : }
472 11 : if (GNUNET_NO == res)
473 : {
474 0 : GNUNET_break_op (0);
475 0 : return MHD_YES; /* failure */
476 : }
477 : }
478 11 : GNUNET_assert (GNUNET_OK ==
479 : TALER_amount_set_zero (TEH_currency,
480 : &pcc.deposit_total));
481 11 : if (GNUNET_TIME_timestamp_cmp (pcc.pd.purse_expiration,
482 : <,
483 : pcc.exchange_timestamp))
484 : {
485 0 : GNUNET_break_op (0);
486 0 : GNUNET_JSON_parse_free (spec);
487 0 : return TALER_MHD_reply_with_error (connection,
488 : MHD_HTTP_BAD_REQUEST,
489 : TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_BEFORE_NOW,
490 : NULL);
491 : }
492 11 : if (GNUNET_TIME_absolute_is_never (pcc.pd.purse_expiration.abs_time))
493 : {
494 0 : GNUNET_break_op (0);
495 0 : GNUNET_JSON_parse_free (spec);
496 0 : return TALER_MHD_reply_with_error (connection,
497 : MHD_HTTP_BAD_REQUEST,
498 : TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_IS_NEVER,
499 : NULL);
500 : }
501 11 : pcc.num_coins = json_array_size (deposits);
502 11 : if ( (0 == pcc.num_coins) ||
503 11 : (pcc.num_coins > TALER_MAX_COINS) )
504 : {
505 0 : GNUNET_break_op (0);
506 0 : GNUNET_JSON_parse_free (spec);
507 0 : return TALER_MHD_reply_with_error (connection,
508 : MHD_HTTP_BAD_REQUEST,
509 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
510 : "deposits");
511 : }
512 : {
513 : struct TEH_KeyStateHandle *keys;
514 :
515 11 : keys = TEH_keys_get_state ();
516 11 : if (NULL == keys)
517 : {
518 0 : GNUNET_break (0);
519 0 : GNUNET_JSON_parse_free (spec);
520 0 : return TALER_MHD_reply_with_error (connection,
521 : MHD_HTTP_INTERNAL_SERVER_ERROR,
522 : TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
523 : NULL);
524 : }
525 11 : gf = TEH_keys_global_fee_by_time (keys,
526 : pcc.exchange_timestamp);
527 : }
528 11 : if (NULL == gf)
529 : {
530 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
531 : "Cannot create purse: global fees not configured!\n");
532 0 : GNUNET_JSON_parse_free (spec);
533 0 : return TALER_MHD_reply_with_error (connection,
534 : MHD_HTTP_INTERNAL_SERVER_ERROR,
535 : TALER_EC_EXCHANGE_GENERIC_GLOBAL_FEES_MISSING,
536 : NULL);
537 : }
538 : /* parse deposits */
539 11 : pcc.coins = GNUNET_new_array (pcc.num_coins,
540 : struct TEH_PurseDepositedCoin);
541 22 : json_array_foreach (deposits, idx, deposit)
542 : {
543 : enum GNUNET_GenericReturnValue res;
544 11 : struct TEH_PurseDepositedCoin *coin = &pcc.coins[idx];
545 :
546 11 : res = parse_coin (connection,
547 : &pcc,
548 : coin,
549 : deposit);
550 11 : if (GNUNET_OK != res)
551 : {
552 0 : for (unsigned int i = 0; i<idx; i++)
553 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
554 0 : GNUNET_free (pcc.coins);
555 0 : GNUNET_JSON_parse_free (spec);
556 0 : return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
557 : }
558 : }
559 :
560 11 : if (0 < TALER_amount_cmp (&gf->fees.purse,
561 : &pcc.deposit_total))
562 : {
563 0 : GNUNET_break_op (0);
564 0 : GNUNET_free (pcc.coins);
565 0 : GNUNET_JSON_parse_free (spec);
566 0 : return TALER_MHD_reply_with_error (connection,
567 : MHD_HTTP_BAD_REQUEST,
568 : TALER_EC_EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE,
569 : NULL);
570 : }
571 11 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
572 :
573 11 : if (GNUNET_OK !=
574 11 : TALER_wallet_purse_create_verify (
575 : pcc.pd.purse_expiration,
576 : &pcc.pd.h_contract_terms,
577 : &pcc.merge_pub,
578 : pcc.min_age,
579 : &pcc.pd.target_amount,
580 : &pcc.pd.purse_pub,
581 : &pcc.purse_sig))
582 : {
583 0 : TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
584 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
585 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
586 0 : GNUNET_free (pcc.coins);
587 0 : GNUNET_JSON_parse_free (spec);
588 0 : return TALER_MHD_reply_with_error (connection,
589 : MHD_HTTP_FORBIDDEN,
590 : TALER_EC_EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID,
591 : NULL);
592 : }
593 22 : if ( (! pcc.no_econtract) &&
594 : (GNUNET_OK !=
595 11 : TALER_wallet_econtract_upload_verify (pcc.econtract.econtract,
596 : pcc.econtract.econtract_size,
597 : &pcc.econtract.contract_pub,
598 : purse_pub,
599 : &pcc.econtract.econtract_sig)) )
600 : {
601 0 : TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
602 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
603 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
604 0 : GNUNET_free (pcc.coins);
605 0 : GNUNET_JSON_parse_free (spec);
606 0 : return TALER_MHD_reply_with_error (connection,
607 : MHD_HTTP_FORBIDDEN,
608 : TALER_EC_EXCHANGE_PURSE_ECONTRACT_SIGNATURE_INVALID,
609 : NULL);
610 : }
611 :
612 :
613 11 : if (GNUNET_SYSERR ==
614 11 : TEH_plugin->preflight (TEH_plugin->cls))
615 : {
616 0 : GNUNET_break (0);
617 0 : for (unsigned int i = 0; i<pcc.num_coins; i++)
618 0 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
619 0 : GNUNET_free (pcc.coins);
620 0 : GNUNET_JSON_parse_free (spec);
621 0 : return TALER_MHD_reply_with_error (connection,
622 : MHD_HTTP_INTERNAL_SERVER_ERROR,
623 : TALER_EC_GENERIC_DB_START_FAILED,
624 : "preflight failure");
625 : }
626 :
627 : /* execute transaction */
628 : {
629 : MHD_RESULT mhd_ret;
630 :
631 11 : if (GNUNET_OK !=
632 11 : TEH_DB_run_transaction (connection,
633 : "execute purse create",
634 : TEH_MT_REQUEST_PURSE_CREATE,
635 : &mhd_ret,
636 : &create_transaction,
637 : &pcc))
638 : {
639 4 : for (unsigned int i = 0; i<pcc.num_coins; i++)
640 2 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
641 2 : GNUNET_free (pcc.coins);
642 2 : GNUNET_JSON_parse_free (spec);
643 2 : return mhd_ret;
644 : }
645 : }
646 :
647 : /* generate regular response */
648 : {
649 : MHD_RESULT res;
650 :
651 9 : res = TEH_RESPONSE_reply_purse_created (connection,
652 : pcc.exchange_timestamp,
653 : &pcc.deposit_total,
654 : &pcc.pd);
655 18 : for (unsigned int i = 0; i<pcc.num_coins; i++)
656 9 : TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
657 9 : GNUNET_free (pcc.coins);
658 9 : GNUNET_JSON_parse_free (spec);
659 9 : return res;
660 : }
661 : }
662 :
663 :
664 : /* end of taler-exchange-httpd_purses_create.c */
|