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_batch-deposit.c
18 : * @brief Handle /batch-deposit requests; parses the POST and JSON and
19 : * verifies the coin signatures before handing things off
20 : * to the database.
21 : * @author Florian Dold
22 : * @author Benedikt Mueller
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include <gnunet/gnunet_util_lib.h>
27 : #include <gnunet/gnunet_json_lib.h>
28 : #include <jansson.h>
29 : #include <microhttpd.h>
30 : #include <pthread.h>
31 : #include "taler_json_lib.h"
32 : #include "taler_mhd_lib.h"
33 : #include "taler-exchange-httpd_deposit.h"
34 : #include "taler-exchange-httpd_responses.h"
35 : #include "taler_exchangedb_lib.h"
36 : #include "taler-exchange-httpd_keys.h"
37 :
38 :
39 : /**
40 : * Closure for #batch_deposit_transaction.
41 : */
42 : struct BatchDepositContext
43 : {
44 : /**
45 : * Information about the individual coin deposits.
46 : */
47 : struct TALER_EXCHANGEDB_Deposit *deposits;
48 :
49 : /**
50 : * Our timestamp (when we received the request).
51 : * Possibly updated by the transaction if the
52 : * request is idempotent (was repeated).
53 : */
54 : struct GNUNET_TIME_Timestamp exchange_timestamp;
55 :
56 : /**
57 : * Hash over the proposal data between merchant and customer
58 : * (remains unknown to the Exchange).
59 : */
60 : struct TALER_PrivateContractHashP h_contract_terms;
61 :
62 : /**
63 : * Public key of the merchant. Enables later identification
64 : * of the merchant in case of a need to rollback transactions.
65 : */
66 : struct TALER_MerchantPublicKeyP merchant_pub;
67 :
68 : /**
69 : * Salt used by the merchant to compute @e h_wire.
70 : */
71 : struct TALER_WireSaltP wire_salt;
72 :
73 : /**
74 : * Hash over the wire details (with @e wire_salt).
75 : */
76 : struct TALER_MerchantWireHashP h_wire;
77 :
78 : /**
79 : * Hash of the payto URI.
80 : */
81 : struct TALER_PaytoHashP h_payto;
82 :
83 : /**
84 : * Information about the receiver for executing the transaction. URI in
85 : * payto://-format.
86 : */
87 : const char *payto_uri;
88 :
89 : /**
90 : * Additional details for extensions relevant for this
91 : * deposit operation, possibly NULL!
92 : */
93 : json_t *extension_details;
94 :
95 : /**
96 : * Hash over @e extension_details.
97 : */
98 : struct TALER_ExtensionContractHashP h_extensions;
99 :
100 : /**
101 : * Time when this request was generated. Used, for example, to
102 : * assess when (roughly) the income was achieved for tax purposes.
103 : * Note that the Exchange will only check that the timestamp is not "too
104 : * far" into the future (i.e. several days). The fact that the
105 : * timestamp falls within the validity period of the coin's
106 : * denomination key is irrelevant for the validity of the deposit
107 : * request, as obviously the customer and merchant could conspire to
108 : * set any timestamp. Also, the Exchange must accept very old deposit
109 : * requests, as the merchant might have been unable to transmit the
110 : * deposit request in a timely fashion (so back-dating is not
111 : * prevented).
112 : */
113 : struct GNUNET_TIME_Timestamp timestamp;
114 :
115 : /**
116 : * How much time does the merchant have to issue a refund request?
117 : * Zero if refunds are not allowed. After this time, the coin
118 : * cannot be refunded.
119 : */
120 : struct GNUNET_TIME_Timestamp refund_deadline;
121 :
122 : /**
123 : * How much time does the merchant have to execute the wire transfer?
124 : * This time is advisory for aggregating transactions, not a hard
125 : * constraint (as the merchant can theoretically pick any time,
126 : * including one in the past).
127 : */
128 : struct GNUNET_TIME_Timestamp wire_deadline;
129 :
130 : /**
131 : * Number of coins in the batch.
132 : */
133 : unsigned int num_coins;
134 : };
135 :
136 :
137 : /**
138 : * Send confirmation of batch deposit success to client. This function will
139 : * create a signed message affirming the given information and return it to
140 : * the client. By this, the exchange affirms that the coins had sufficient
141 : * (residual) value for the specified transaction and that it will execute the
142 : * requested batch deposit operation with the given wiring details.
143 : *
144 : * @param connection connection to the client
145 : * @param bdc information about the batch deposit
146 : * @return MHD result code
147 : */
148 : static MHD_RESULT
149 0 : reply_batch_deposit_success (
150 : struct MHD_Connection *connection,
151 : const struct BatchDepositContext *bdc)
152 : {
153 : json_t *arr;
154 : struct TALER_ExchangePublicKeyP pub;
155 :
156 0 : again:
157 0 : arr = json_array ();
158 0 : GNUNET_assert (NULL != arr);
159 0 : for (unsigned int i = 0; i<bdc->num_coins; i++)
160 : {
161 0 : const struct TALER_EXCHANGEDB_Deposit *deposit = &bdc->deposits[i];
162 : struct TALER_ExchangePublicKeyP pubi;
163 : struct TALER_ExchangeSignatureP sig;
164 : enum TALER_ErrorCode ec;
165 : struct TALER_Amount amount_without_fee;
166 :
167 0 : GNUNET_assert (0 <=
168 : TALER_amount_subtract (&amount_without_fee,
169 : &deposit->amount_with_fee,
170 : &deposit->deposit_fee));
171 0 : if (TALER_EC_NONE !=
172 0 : (ec = TALER_exchange_online_deposit_confirmation_sign (
173 : &TEH_keys_exchange_sign_,
174 : &bdc->h_contract_terms,
175 : &bdc->h_wire,
176 : &bdc->h_extensions,
177 : bdc->exchange_timestamp,
178 : bdc->wire_deadline,
179 : bdc->refund_deadline,
180 : &amount_without_fee,
181 : &deposit->coin.coin_pub,
182 : &bdc->merchant_pub,
183 : &pubi,
184 : &sig)))
185 : {
186 0 : GNUNET_break (0);
187 0 : return TALER_MHD_reply_with_ec (connection,
188 : ec,
189 : NULL);
190 : }
191 0 : if (0 == i)
192 0 : pub = pubi;
193 0 : if (0 !=
194 0 : GNUNET_memcmp (&pub,
195 : &pubi))
196 : {
197 : /* note: in the future, maybe have batch
198 : sign API to avoid having to handle
199 : key rollover... */
200 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
201 : "Exchange public key changed during batch deposit, trying again\n");
202 0 : json_decref (arr);
203 0 : goto again;
204 : }
205 0 : GNUNET_assert (
206 : 0 ==
207 : json_array_append_new (arr,
208 : GNUNET_JSON_PACK (
209 : GNUNET_JSON_pack_data_auto (
210 : "exchange_sig",
211 : &sig))));
212 : }
213 0 : return TALER_MHD_REPLY_JSON_PACK (
214 : connection,
215 : MHD_HTTP_OK,
216 : GNUNET_JSON_pack_timestamp ("exchange_timestamp",
217 : bdc->exchange_timestamp),
218 : GNUNET_JSON_pack_data_auto (
219 : "exchange_pub",
220 : &pub),
221 : GNUNET_JSON_pack_array_steal ("exchange_sigs",
222 : arr));
223 : }
224 :
225 :
226 : /**
227 : * Execute database transaction for /batch-deposit. Runs the transaction
228 : * logic; IF it returns a non-error code, the transaction logic MUST
229 : * NOT queue a MHD response. IF it returns an hard error, the
230 : * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
231 : * it returns the soft error code, the function MAY be called again to
232 : * retry and MUST not queue a MHD response.
233 : *
234 : * @param cls a `struct BatchDepositContext`
235 : * @param connection MHD request context
236 : * @param[out] mhd_ret set to MHD status on error
237 : * @return transaction status
238 : */
239 : static enum GNUNET_DB_QueryStatus
240 0 : batch_deposit_transaction (void *cls,
241 : struct MHD_Connection *connection,
242 : MHD_RESULT *mhd_ret)
243 : {
244 0 : struct BatchDepositContext *dc = cls;
245 : enum GNUNET_DB_QueryStatus qs;
246 : bool balance_ok;
247 : bool in_conflict;
248 :
249 0 : for (unsigned int i = 0; i<dc->num_coins; i++)
250 : {
251 0 : const struct TALER_EXCHANGEDB_Deposit *deposit = &dc->deposits[i];
252 : uint64_t known_coin_id;
253 :
254 0 : qs = TEH_make_coin_known (&deposit->coin,
255 : connection,
256 : &known_coin_id,
257 : mhd_ret);
258 0 : if (qs < 0)
259 0 : return qs;
260 0 : qs = TEH_plugin->do_deposit (TEH_plugin->cls,
261 : deposit,
262 : known_coin_id,
263 0 : &dc->h_payto,
264 : false, /* FIXME-OEC: #7270 extension blocked */
265 : &dc->exchange_timestamp,
266 : &balance_ok,
267 : &in_conflict);
268 0 : if (qs < 0)
269 : {
270 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
271 0 : return qs;
272 0 : TALER_LOG_WARNING (
273 : "Failed to store /batch-deposit information in database\n");
274 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
275 : MHD_HTTP_INTERNAL_SERVER_ERROR,
276 : TALER_EC_GENERIC_DB_STORE_FAILED,
277 : "batch-deposit");
278 0 : return qs;
279 : }
280 0 : if (in_conflict)
281 : {
282 : /* FIXME: #7267 conficting contract != insufficient funds */
283 : *mhd_ret
284 0 : = TEH_RESPONSE_reply_coin_insufficient_funds (
285 : connection,
286 : TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
287 : &deposit->coin.denom_pub_hash,
288 : &deposit->coin.coin_pub);
289 0 : return GNUNET_DB_STATUS_HARD_ERROR;
290 : }
291 0 : if (! balance_ok)
292 : {
293 : *mhd_ret
294 0 : = TEH_RESPONSE_reply_coin_insufficient_funds (
295 : connection,
296 : TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
297 : &deposit->coin.denom_pub_hash,
298 : &deposit->coin.coin_pub);
299 0 : return GNUNET_DB_STATUS_HARD_ERROR;
300 : }
301 : }
302 0 : TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++;
303 0 : return qs;
304 : }
305 :
306 :
307 : /**
308 : * Parse per-coin deposit information from @a jcoin
309 : * into @a deposit. Fill in generic information from
310 : * @a ctx.
311 : *
312 : * @param connection connection we are handling
313 : * @param jcoin coin data to parse
314 : * @param dc overall batch deposit context information to use
315 : * @param[out] deposit where to store the result
316 : * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
317 : * #GNUNET_SYSERR on failure and no error could be returned
318 : */
319 : static enum GNUNET_GenericReturnValue
320 0 : parse_coin (struct MHD_Connection *connection,
321 : json_t *jcoin,
322 : const struct BatchDepositContext *dc,
323 : struct TALER_EXCHANGEDB_Deposit *deposit)
324 : {
325 : struct GNUNET_JSON_Specification spec[] = {
326 0 : TALER_JSON_spec_amount ("contribution",
327 : TEH_currency,
328 : &deposit->amount_with_fee),
329 0 : GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
330 : &deposit->coin.denom_pub_hash),
331 0 : TALER_JSON_spec_denom_sig ("ub_sig",
332 : &deposit->coin.denom_sig),
333 0 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
334 : &deposit->coin.coin_pub),
335 0 : GNUNET_JSON_spec_mark_optional (
336 0 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
337 : &deposit->coin.h_age_commitment),
338 : &deposit->coin.no_age_commitment),
339 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
340 : &deposit->csig),
341 0 : GNUNET_JSON_spec_end ()
342 : };
343 : enum GNUNET_GenericReturnValue res;
344 :
345 0 : if (GNUNET_OK !=
346 0 : (res = TALER_MHD_parse_json_data (connection,
347 : jcoin,
348 : spec)))
349 0 : return res;
350 : /* check denomination exists and is valid */
351 : {
352 : struct TEH_DenominationKey *dk;
353 : MHD_RESULT mret;
354 :
355 0 : dk = TEH_keys_denomination_by_hash (&deposit->coin.denom_pub_hash,
356 : connection,
357 : &mret);
358 0 : if (NULL == dk)
359 : {
360 0 : GNUNET_JSON_parse_free (spec);
361 0 : return mret;
362 : }
363 0 : if (0 > TALER_amount_cmp (&dk->meta.value,
364 0 : &deposit->amount_with_fee))
365 : {
366 0 : GNUNET_break_op (0);
367 0 : GNUNET_JSON_parse_free (spec);
368 : return (MHD_YES ==
369 0 : TALER_MHD_reply_with_error (connection,
370 : MHD_HTTP_BAD_REQUEST,
371 : TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
372 : NULL))
373 : ? GNUNET_NO
374 0 : : GNUNET_SYSERR;
375 : }
376 0 : if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
377 : {
378 : /* This denomination is past the expiration time for deposits */
379 0 : GNUNET_JSON_parse_free (spec);
380 : return (MHD_YES ==
381 0 : TEH_RESPONSE_reply_expired_denom_pub_hash (
382 : connection,
383 0 : &deposit->coin.denom_pub_hash,
384 : TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
385 : "DEPOSIT"))
386 : ? GNUNET_NO
387 0 : : GNUNET_SYSERR;
388 : }
389 0 : if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
390 : {
391 : /* This denomination is not yet valid */
392 0 : GNUNET_JSON_parse_free (spec);
393 : return (MHD_YES ==
394 0 : TEH_RESPONSE_reply_expired_denom_pub_hash (
395 : connection,
396 0 : &deposit->coin.denom_pub_hash,
397 : TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
398 : "DEPOSIT"))
399 : ? GNUNET_NO
400 0 : : GNUNET_SYSERR;
401 : }
402 0 : if (dk->recoup_possible)
403 : {
404 : /* This denomination has been revoked */
405 0 : GNUNET_JSON_parse_free (spec);
406 : return (MHD_YES ==
407 0 : TEH_RESPONSE_reply_expired_denom_pub_hash (
408 : connection,
409 0 : &deposit->coin.denom_pub_hash,
410 : TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
411 : "DEPOSIT"))
412 : ? GNUNET_NO
413 0 : : GNUNET_SYSERR;
414 : }
415 0 : if (dk->denom_pub.cipher != deposit->coin.denom_sig.cipher)
416 : {
417 : /* denomination cipher and denomination signature cipher not the same */
418 0 : GNUNET_JSON_parse_free (spec);
419 : return (MHD_YES ==
420 0 : TALER_MHD_reply_with_error (connection,
421 : MHD_HTTP_BAD_REQUEST,
422 : TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
423 : NULL))
424 : ? GNUNET_NO
425 0 : : GNUNET_SYSERR;
426 : }
427 :
428 0 : deposit->deposit_fee = dk->meta.fees.deposit;
429 : /* check coin signature */
430 0 : switch (dk->denom_pub.cipher)
431 : {
432 0 : case TALER_DENOMINATION_RSA:
433 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++;
434 0 : break;
435 0 : case TALER_DENOMINATION_CS:
436 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++;
437 0 : break;
438 0 : default:
439 0 : break;
440 : }
441 0 : if (GNUNET_YES !=
442 0 : TALER_test_coin_valid (&deposit->coin,
443 0 : &dk->denom_pub))
444 : {
445 0 : TALER_LOG_WARNING ("Invalid coin passed for /batch-deposit\n");
446 0 : GNUNET_JSON_parse_free (spec);
447 : return (MHD_YES ==
448 0 : TALER_MHD_reply_with_error (connection,
449 : MHD_HTTP_FORBIDDEN,
450 : TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
451 : NULL))
452 : ? GNUNET_NO
453 0 : : GNUNET_SYSERR;
454 : }
455 : }
456 0 : if (0 < TALER_amount_cmp (&deposit->deposit_fee,
457 0 : &deposit->amount_with_fee))
458 : {
459 0 : GNUNET_break_op (0);
460 0 : GNUNET_JSON_parse_free (spec);
461 : return (MHD_YES ==
462 0 : TALER_MHD_reply_with_error (connection,
463 : MHD_HTTP_BAD_REQUEST,
464 : TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
465 : NULL))
466 : ? GNUNET_NO
467 0 : : GNUNET_SYSERR;
468 : }
469 :
470 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
471 0 : if (GNUNET_OK !=
472 0 : TALER_wallet_deposit_verify (&deposit->amount_with_fee,
473 0 : &deposit->deposit_fee,
474 : &dc->h_wire,
475 : &dc->h_contract_terms,
476 0 : &deposit->coin.h_age_commitment,
477 : &dc->h_extensions,
478 0 : &deposit->coin.denom_pub_hash,
479 : dc->timestamp,
480 : &dc->merchant_pub,
481 : dc->refund_deadline,
482 0 : &deposit->coin.coin_pub,
483 0 : &deposit->csig))
484 : {
485 0 : TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
486 0 : GNUNET_JSON_parse_free (spec);
487 : return (MHD_YES ==
488 0 : TALER_MHD_reply_with_error (connection,
489 : MHD_HTTP_FORBIDDEN,
490 : TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
491 : NULL))
492 : ? GNUNET_NO
493 0 : : GNUNET_SYSERR;
494 : }
495 0 : deposit->merchant_pub = dc->merchant_pub;
496 0 : deposit->h_contract_terms = dc->h_contract_terms;
497 0 : deposit->wire_salt = dc->wire_salt;
498 0 : deposit->receiver_wire_account = (char *) dc->payto_uri;
499 : /* FIXME-OEC: #7270 should NOT insert the extension details N times,
500 : but rather insert them ONCE and then per-coin only use
501 : the resulting extension UUID/serial; so the data structure
502 : here should be changed once we look at extensions in earnest. */
503 0 : deposit->extension_details = dc->extension_details;
504 0 : deposit->timestamp = dc->timestamp;
505 0 : deposit->refund_deadline = dc->refund_deadline;
506 0 : deposit->wire_deadline = dc->wire_deadline;
507 0 : return GNUNET_OK;
508 : }
509 :
510 :
511 : MHD_RESULT
512 0 : TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
513 : const json_t *root,
514 : const char *const args[])
515 : {
516 0 : struct MHD_Connection *connection = rc->connection;
517 : struct BatchDepositContext dc;
518 : json_t *coins;
519 0 : bool no_refund_deadline = true;
520 0 : bool no_extensions = true;
521 : struct GNUNET_JSON_Specification spec[] = {
522 0 : GNUNET_JSON_spec_string ("merchant_payto_uri",
523 : &dc.payto_uri),
524 0 : GNUNET_JSON_spec_fixed_auto ("wire_salt",
525 : &dc.wire_salt),
526 0 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
527 : &dc.merchant_pub),
528 0 : GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
529 : &dc.h_contract_terms),
530 0 : GNUNET_JSON_spec_json ("coins",
531 : &coins),
532 0 : GNUNET_JSON_spec_mark_optional (
533 : GNUNET_JSON_spec_json ("extension_details",
534 : &dc.extension_details),
535 : &no_extensions),
536 0 : GNUNET_JSON_spec_timestamp ("timestamp",
537 : &dc.timestamp),
538 0 : GNUNET_JSON_spec_mark_optional (
539 : GNUNET_JSON_spec_timestamp ("refund_deadline",
540 : &dc.refund_deadline),
541 : &no_refund_deadline),
542 0 : GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
543 : &dc.wire_deadline),
544 0 : GNUNET_JSON_spec_end ()
545 : };
546 : enum GNUNET_GenericReturnValue res;
547 :
548 : (void) args;
549 0 : memset (&dc,
550 : 0,
551 : sizeof (dc));
552 0 : res = TALER_MHD_parse_json_data (connection,
553 : root,
554 : spec);
555 0 : if (GNUNET_SYSERR == res)
556 : {
557 0 : GNUNET_break (0);
558 0 : return MHD_NO; /* hard failure */
559 : }
560 0 : if (GNUNET_NO == res)
561 : {
562 0 : GNUNET_break_op (0);
563 0 : return MHD_YES; /* failure */
564 : }
565 :
566 : /* validate merchant's wire details (as far as we can) */
567 : {
568 : char *emsg;
569 :
570 0 : emsg = TALER_payto_validate (dc.payto_uri);
571 0 : if (NULL != emsg)
572 : {
573 : MHD_RESULT ret;
574 :
575 0 : GNUNET_break_op (0);
576 0 : GNUNET_JSON_parse_free (spec);
577 0 : ret = TALER_MHD_reply_with_error (connection,
578 : MHD_HTTP_BAD_REQUEST,
579 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
580 : emsg);
581 0 : GNUNET_free (emsg);
582 0 : return ret;
583 : }
584 : }
585 0 : if (GNUNET_TIME_timestamp_cmp (dc.refund_deadline,
586 : >,
587 : dc.wire_deadline))
588 : {
589 0 : GNUNET_break_op (0);
590 0 : GNUNET_JSON_parse_free (spec);
591 0 : return TALER_MHD_reply_with_error (connection,
592 : MHD_HTTP_BAD_REQUEST,
593 : TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
594 : NULL);
595 : }
596 0 : if (GNUNET_TIME_absolute_is_never (dc.wire_deadline.abs_time))
597 : {
598 0 : GNUNET_break_op (0);
599 0 : GNUNET_JSON_parse_free (spec);
600 0 : return TALER_MHD_reply_with_error (connection,
601 : MHD_HTTP_BAD_REQUEST,
602 : TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER,
603 : NULL);
604 : }
605 0 : TALER_payto_hash (dc.payto_uri,
606 : &dc.h_payto);
607 0 : TALER_merchant_wire_signature_hash (dc.payto_uri,
608 : &dc.wire_salt,
609 : &dc.h_wire);
610 : /* FIXME-OEC: #7270 hash actual extension JSON object here */
611 : // if (! no_extensions)
612 0 : memset (&dc.h_extensions,
613 : 0,
614 : sizeof (dc.h_extensions));
615 0 : dc.num_coins = json_array_size (coins);
616 0 : if (0 == dc.num_coins)
617 : {
618 0 : GNUNET_break_op (0);
619 0 : GNUNET_JSON_parse_free (spec);
620 0 : return TALER_MHD_reply_with_error (connection,
621 : MHD_HTTP_BAD_REQUEST,
622 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
623 : "coins");
624 : }
625 0 : if (TALER_MAX_FRESH_COINS < dc.num_coins)
626 : {
627 0 : GNUNET_break_op (0);
628 0 : GNUNET_JSON_parse_free (spec);
629 0 : return TALER_MHD_reply_with_error (connection,
630 : MHD_HTTP_BAD_REQUEST,
631 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
632 : "coins");
633 : }
634 0 : dc.deposits = GNUNET_new_array (dc.num_coins,
635 : struct TALER_EXCHANGEDB_Deposit);
636 0 : for (unsigned int i = 0; i<dc.num_coins; i++)
637 : {
638 0 : if (GNUNET_OK !=
639 0 : (res = parse_coin (connection,
640 : json_array_get (coins,
641 : i),
642 : &dc,
643 0 : &dc.deposits[i])))
644 : {
645 0 : for (unsigned int j = 0; j<i; j++)
646 0 : TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
647 0 : GNUNET_free (dc.deposits);
648 0 : GNUNET_JSON_parse_free (spec);
649 0 : return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
650 : }
651 : }
652 :
653 0 : dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
654 0 : if (GNUNET_SYSERR ==
655 0 : TEH_plugin->preflight (TEH_plugin->cls))
656 : {
657 0 : GNUNET_break (0);
658 0 : GNUNET_JSON_parse_free (spec);
659 0 : return TALER_MHD_reply_with_error (connection,
660 : MHD_HTTP_INTERNAL_SERVER_ERROR,
661 : TALER_EC_GENERIC_DB_START_FAILED,
662 : "preflight failure");
663 : }
664 :
665 : /* execute transaction */
666 : {
667 : MHD_RESULT mhd_ret;
668 :
669 0 : if (GNUNET_OK !=
670 0 : TEH_DB_run_transaction (connection,
671 : "execute batch deposit",
672 : TEH_MT_REQUEST_BATCH_DEPOSIT,
673 : &mhd_ret,
674 : &batch_deposit_transaction,
675 : &dc))
676 : {
677 0 : GNUNET_JSON_parse_free (spec);
678 0 : for (unsigned int j = 0; j<dc.num_coins; j++)
679 0 : TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
680 0 : GNUNET_free (dc.deposits);
681 0 : GNUNET_JSON_parse_free (spec);
682 0 : return mhd_ret;
683 : }
684 : }
685 :
686 : /* generate regular response */
687 : {
688 : MHD_RESULT res;
689 :
690 0 : res = reply_batch_deposit_success (connection,
691 : &dc);
692 0 : for (unsigned int j = 0; j<dc.num_coins; j++)
693 0 : TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
694 0 : GNUNET_free (dc.deposits);
695 0 : GNUNET_JSON_parse_free (spec);
696 0 : return res;
697 : }
698 : }
699 :
700 :
701 : /* end of taler-exchange-httpd_batch-deposit.c */
|