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 General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file lib/exchange_api_purse_create_with_deposit.c
19 : * @brief Implementation of the client to create a purse with
20 : * an initial set of deposits (and a contract)
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <jansson.h>
25 : #include <microhttpd.h> /* just for HTTP status codes */
26 : #include <gnunet/gnunet_util_lib.h>
27 : #include <gnunet/gnunet_json_lib.h>
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler_json_lib.h"
30 : #include "taler_exchange_service.h"
31 : #include "exchange_api_handle.h"
32 : #include "exchange_api_common.h"
33 : #include "taler_signatures.h"
34 : #include "exchange_api_curl_defaults.h"
35 :
36 :
37 : /**
38 : * Information we track per deposited coin.
39 : */
40 : struct Deposit
41 : {
42 : /**
43 : * Coin's public key.
44 : */
45 : struct TALER_CoinSpendPublicKeyP coin_pub;
46 :
47 : /**
48 : * Signature made with the coin.
49 : */
50 : struct TALER_CoinSpendSignatureP coin_sig;
51 :
52 : /**
53 : * Coin's denomination.
54 : */
55 : struct TALER_DenominationHashP h_denom_pub;
56 :
57 : /**
58 : * Age restriction hash for the coin.
59 : */
60 : struct TALER_AgeCommitmentHash ahac;
61 :
62 : /**
63 : * How much did we say the coin contributed.
64 : */
65 : struct TALER_Amount contribution;
66 : };
67 :
68 :
69 : /**
70 : * @brief A purse create with deposit handle
71 : */
72 : struct TALER_EXCHANGE_PurseCreateDepositHandle
73 : {
74 :
75 : /**
76 : * The connection to exchange this request handle will use
77 : */
78 : struct TALER_EXCHANGE_Handle *exchange;
79 :
80 : /**
81 : * The url for this request.
82 : */
83 : char *url;
84 :
85 : /**
86 : * Context for #TEH_curl_easy_post(). Keeps the data that must
87 : * persist for Curl to make the upload.
88 : */
89 : struct TALER_CURL_PostContext ctx;
90 :
91 : /**
92 : * Handle for the request.
93 : */
94 : struct GNUNET_CURL_Job *job;
95 :
96 : /**
97 : * Function to call with the result.
98 : */
99 : TALER_EXCHANGE_PurseCreateDepositCallback cb;
100 :
101 : /**
102 : * Closure for @a cb.
103 : */
104 : void *cb_cls;
105 :
106 : /**
107 : * Expected value in the purse after fees.
108 : */
109 : struct TALER_Amount purse_value_after_fees;
110 :
111 : /**
112 : * Our encryped contract (if we had any).
113 : */
114 : struct TALER_EncryptedContract econtract;
115 :
116 : /**
117 : * Public key of the merge capability.
118 : */
119 : struct TALER_PurseMergePublicKeyP merge_pub;
120 :
121 : /**
122 : * Public key of the purse.
123 : */
124 : struct TALER_PurseContractPublicKeyP purse_pub;
125 :
126 : /**
127 : * Signature with the purse key on the request.
128 : */
129 : struct TALER_PurseContractSignatureP purse_sig;
130 :
131 : /**
132 : * Hash over the purse's contract terms.
133 : */
134 : struct TALER_PrivateContractHashP h_contract_terms;
135 :
136 : /**
137 : * When does the purse expire.
138 : */
139 : struct GNUNET_TIME_Timestamp purse_expiration;
140 :
141 : /**
142 : * Array of @e num_deposit deposits.
143 : */
144 : struct Deposit *deposits;
145 :
146 : /**
147 : * How many deposits did we make?
148 : */
149 : unsigned int num_deposits;
150 :
151 : };
152 :
153 :
154 : /**
155 : * Function called when we're done processing the
156 : * HTTP /deposit request.
157 : *
158 : * @param cls the `struct TALER_EXCHANGE_DepositHandle`
159 : * @param response_code HTTP response code, 0 on error
160 : * @param response parsed JSON result, NULL on error
161 : */
162 : static void
163 0 : handle_purse_create_deposit_finished (void *cls,
164 : long response_code,
165 : const void *response)
166 : {
167 0 : struct TALER_EXCHANGE_PurseCreateDepositHandle *pch = cls;
168 0 : const json_t *j = response;
169 0 : struct TALER_EXCHANGE_PurseCreateDepositResponse dr = {
170 : .hr.reply = j,
171 0 : .hr.http_status = (unsigned int) response_code
172 : };
173 : const struct TALER_EXCHANGE_Keys *keys;
174 :
175 0 : pch->job = NULL;
176 0 : keys = TALER_EXCHANGE_get_keys (pch->exchange);
177 0 : switch (response_code)
178 : {
179 0 : case 0:
180 0 : dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
181 0 : break;
182 0 : case MHD_HTTP_OK:
183 : {
184 : const struct TALER_EXCHANGE_Keys *key_state;
185 : struct GNUNET_TIME_Timestamp etime;
186 : struct TALER_Amount total_deposited;
187 : struct TALER_ExchangeSignatureP exchange_sig;
188 : struct TALER_ExchangePublicKeyP exchange_pub;
189 : struct GNUNET_JSON_Specification spec[] = {
190 0 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
191 : &exchange_sig),
192 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
193 : &exchange_pub),
194 0 : GNUNET_JSON_spec_timestamp ("exchange_timestamp",
195 : &etime),
196 0 : TALER_JSON_spec_amount ("total_deposited",
197 0 : pch->purse_value_after_fees.currency,
198 : &total_deposited),
199 0 : GNUNET_JSON_spec_end ()
200 : };
201 :
202 0 : if (GNUNET_OK !=
203 0 : GNUNET_JSON_parse (j,
204 : spec,
205 : NULL, NULL))
206 : {
207 0 : GNUNET_break_op (0);
208 0 : dr.hr.http_status = 0;
209 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
210 0 : break;
211 : }
212 0 : key_state = TALER_EXCHANGE_get_keys (pch->exchange);
213 0 : if (GNUNET_OK !=
214 0 : TALER_EXCHANGE_test_signing_key (key_state,
215 : &exchange_pub))
216 : {
217 0 : GNUNET_break_op (0);
218 0 : dr.hr.http_status = 0;
219 0 : dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID;
220 0 : break;
221 : }
222 0 : if (GNUNET_OK !=
223 0 : TALER_exchange_online_purse_created_verify (
224 : etime,
225 : pch->purse_expiration,
226 0 : &pch->purse_value_after_fees,
227 : &total_deposited,
228 0 : &pch->purse_pub,
229 0 : &pch->h_contract_terms,
230 : &exchange_pub,
231 : &exchange_sig))
232 : {
233 0 : GNUNET_break_op (0);
234 0 : dr.hr.http_status = 0;
235 0 : dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID;
236 0 : break;
237 : }
238 : }
239 0 : break;
240 0 : case MHD_HTTP_BAD_REQUEST:
241 : /* This should never happen, either us or the exchange is buggy
242 : (or API version conflict); just pass JSON reply to the application */
243 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
244 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
245 0 : break;
246 0 : case MHD_HTTP_FORBIDDEN:
247 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
248 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
249 : /* Nothing really to verify, exchange says one of the signatures is
250 : invalid; as we checked them, this should never happen, we
251 : should pass the JSON reply to the application */
252 0 : break;
253 0 : case MHD_HTTP_NOT_FOUND:
254 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
255 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
256 : /* Nothing really to verify, this should never
257 : happen, we should pass the JSON reply to the application */
258 0 : break;
259 0 : case MHD_HTTP_CONFLICT:
260 : {
261 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
262 0 : switch (dr.hr.ec)
263 : {
264 0 : case TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA:
265 0 : if (GNUNET_OK !=
266 0 : TALER_EXCHANGE_check_purse_create_conflict_ (
267 0 : &pch->purse_sig,
268 0 : &pch->purse_pub,
269 : j))
270 : {
271 0 : GNUNET_break_op (0);
272 0 : dr.hr.http_status = 0;
273 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
274 0 : break;
275 : }
276 0 : break;
277 0 : case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
278 : {
279 : struct TALER_Amount left;
280 : struct TALER_CoinSpendPublicKeyP pcoin_pub;
281 0 : bool found = false;
282 :
283 0 : if (GNUNET_OK !=
284 0 : TALER_EXCHANGE_check_coin_amount_conflict_ (
285 : keys,
286 : j,
287 : &pcoin_pub,
288 : &left))
289 : {
290 0 : GNUNET_break_op (0);
291 0 : dr.hr.http_status = 0;
292 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
293 0 : break;
294 : }
295 0 : for (unsigned int i = 0; i<pch->num_deposits; i++)
296 : {
297 0 : struct Deposit *deposit = &pch->deposits[i];
298 :
299 0 : if (0 != GNUNET_memcmp (&pcoin_pub,
300 : &deposit->coin_pub))
301 0 : continue;
302 0 : if (-1 !=
303 0 : TALER_amount_cmp (&left,
304 0 : &deposit->contribution))
305 : {
306 : /* Balance was sufficient after all; recoup MAY have still been possible */
307 0 : GNUNET_break_op (0);
308 0 : continue;
309 : }
310 0 : if (GNUNET_OK !=
311 0 : TALER_EXCHANGE_check_coin_signature_conflict_ (
312 : j,
313 0 : &deposit->coin_sig))
314 : {
315 0 : GNUNET_break_op (0);
316 0 : continue;
317 : }
318 0 : found = true;
319 0 : break;
320 : }
321 0 : if (! found)
322 : {
323 : /* conflict is for a different coin! */
324 0 : GNUNET_break_op (0);
325 0 : dr.hr.http_status = 0;
326 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
327 0 : break;
328 : }
329 0 : break;
330 : }
331 0 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
332 : {
333 : struct TALER_Amount left;
334 : struct TALER_CoinSpendPublicKeyP pcoin_pub;
335 0 : bool found = false;
336 :
337 0 : if (GNUNET_OK !=
338 0 : TALER_EXCHANGE_check_coin_amount_conflict_ (
339 : keys,
340 : j,
341 : &pcoin_pub,
342 : &left))
343 : {
344 0 : GNUNET_break_op (0);
345 0 : dr.hr.http_status = 0;
346 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
347 0 : break;
348 : }
349 0 : for (unsigned int i = 0; i<pch->num_deposits; i++)
350 : {
351 0 : struct Deposit *deposit = &pch->deposits[i];
352 :
353 0 : if (0 !=
354 0 : GNUNET_memcmp (&pcoin_pub,
355 : &deposit->coin_pub))
356 0 : continue;
357 0 : if (GNUNET_OK !=
358 0 : TALER_EXCHANGE_check_coin_denomination_conflict_ (
359 : j,
360 0 : &deposit->h_denom_pub))
361 : {
362 : /* Eh, same denomination, hence no conflict */
363 0 : GNUNET_break_op (0);
364 0 : continue;
365 : }
366 0 : found = true;
367 : }
368 0 : if (! found)
369 : {
370 : /* conflict is for a different coin! */
371 0 : GNUNET_break_op (0);
372 0 : dr.hr.http_status = 0;
373 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
374 0 : break;
375 : }
376 : /* meta data conflict is real! */
377 0 : break;
378 : }
379 0 : case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
380 : {
381 : struct TALER_CoinSpendPublicKeyP coin_pub;
382 : struct TALER_CoinSpendSignatureP coin_sig;
383 : struct TALER_DenominationHashP h_denom_pub;
384 : struct TALER_AgeCommitmentHash phac;
385 0 : bool found = false;
386 :
387 0 : if (GNUNET_OK !=
388 0 : TALER_EXCHANGE_check_purse_coin_conflict_ (
389 0 : &pch->purse_pub,
390 0 : pch->exchange->url,
391 : j,
392 : &h_denom_pub,
393 : &phac,
394 : &coin_pub,
395 : &coin_sig))
396 : {
397 0 : GNUNET_break_op (0);
398 0 : dr.hr.http_status = 0;
399 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
400 0 : break;
401 : }
402 0 : for (unsigned int i = 0; i<pch->num_deposits; i++)
403 : {
404 0 : struct Deposit *deposit = &pch->deposits[i];
405 :
406 0 : if (0 !=
407 0 : GNUNET_memcmp (&coin_pub,
408 : &deposit->coin_pub))
409 0 : continue;
410 0 : if (0 !=
411 0 : GNUNET_memcmp (&deposit->h_denom_pub,
412 : &h_denom_pub))
413 : {
414 0 : found = true;
415 0 : break;
416 : }
417 0 : if (0 !=
418 0 : GNUNET_memcmp (&deposit->ahac,
419 : &phac))
420 : {
421 0 : found = true;
422 0 : break;
423 : }
424 0 : if (0 ==
425 0 : GNUNET_memcmp (&coin_sig,
426 : &deposit->coin_sig))
427 : {
428 0 : GNUNET_break_op (0);
429 0 : continue;
430 : }
431 0 : found = true;
432 0 : break;
433 : }
434 0 : if (! found)
435 : {
436 : /* conflict is for a different coin! */
437 0 : GNUNET_break_op (0);
438 0 : dr.hr.http_status = 0;
439 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
440 0 : break;
441 : }
442 : }
443 : case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:
444 0 : if (GNUNET_OK !=
445 0 : TALER_EXCHANGE_check_purse_econtract_conflict_ (
446 0 : &pch->econtract.econtract_sig,
447 0 : &pch->purse_pub,
448 : j))
449 : {
450 0 : GNUNET_break_op (0);
451 0 : dr.hr.http_status = 0;
452 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
453 0 : break;
454 : }
455 0 : break;
456 0 : default:
457 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
458 : "Unexpected error code %d for conflcting deposit\n",
459 : dr.hr.ec);
460 0 : GNUNET_break_op (0);
461 0 : dr.hr.http_status = 0;
462 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
463 : }
464 : }
465 0 : break;
466 0 : case MHD_HTTP_GONE:
467 : /* could happen if denomination was revoked */
468 : /* Note: one might want to check /keys for revocation
469 : signature here, alas tricky in case our /keys
470 : is outdated => left to clients */
471 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
472 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
473 0 : break;
474 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
475 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
476 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
477 : /* Server had an internal issue; we should retry, but this API
478 : leaves this to the application */
479 0 : break;
480 0 : default:
481 : /* unexpected response code */
482 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
483 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
484 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
485 : "Unexpected response code %u/%d for exchange deposit\n",
486 : (unsigned int) response_code,
487 : dr.hr.ec);
488 0 : GNUNET_break_op (0);
489 0 : break;
490 : }
491 0 : pch->cb (pch->cb_cls,
492 : &dr);
493 0 : TALER_EXCHANGE_purse_create_with_deposit_cancel (pch);
494 0 : }
495 :
496 :
497 : struct TALER_EXCHANGE_PurseCreateDepositHandle *
498 0 : TALER_EXCHANGE_purse_create_with_deposit (
499 : struct TALER_EXCHANGE_Handle *exchange,
500 : const struct TALER_PurseContractPrivateKeyP *purse_priv,
501 : const struct TALER_PurseMergePrivateKeyP *merge_priv,
502 : const struct TALER_ContractDiffiePrivateP *contract_priv,
503 : const json_t *contract_terms,
504 : unsigned int num_deposits,
505 : const struct TALER_EXCHANGE_PurseDeposit *deposits,
506 : bool upload_contract,
507 : TALER_EXCHANGE_PurseCreateDepositCallback cb,
508 : void *cb_cls)
509 : {
510 : struct TALER_EXCHANGE_PurseCreateDepositHandle *pch;
511 : struct GNUNET_CURL_Context *ctx;
512 : json_t *create_obj;
513 : json_t *deposit_arr;
514 : CURL *eh;
515 : char arg_str[sizeof (pch->purse_pub) * 2 + 32];
516 : char *url;
517 0 : uint32_t min_age = 0;
518 :
519 0 : pch = GNUNET_new (struct TALER_EXCHANGE_PurseCreateDepositHandle);
520 0 : pch->exchange = exchange;
521 0 : pch->cb = cb;
522 0 : pch->cb_cls = cb_cls;
523 : {
524 : struct GNUNET_JSON_Specification spec[] = {
525 0 : GNUNET_JSON_spec_timestamp ("pay_deadline",
526 : &pch->purse_expiration),
527 0 : TALER_JSON_spec_amount_any ("amount",
528 : &pch->purse_value_after_fees),
529 0 : GNUNET_JSON_spec_mark_optional (
530 : GNUNET_JSON_spec_uint32 ("minimum_age",
531 : &min_age),
532 : NULL),
533 0 : GNUNET_JSON_spec_end ()
534 : };
535 :
536 0 : if (GNUNET_OK !=
537 0 : GNUNET_JSON_parse (contract_terms,
538 : spec,
539 : NULL, NULL))
540 : {
541 0 : GNUNET_break (0);
542 0 : return NULL;
543 : }
544 : }
545 0 : GNUNET_assert (GNUNET_YES ==
546 : TEAH_handle_is_ready (exchange));
547 0 : if (GNUNET_OK !=
548 0 : TALER_JSON_contract_hash (contract_terms,
549 : &pch->h_contract_terms))
550 : {
551 0 : GNUNET_break (0);
552 0 : return NULL;
553 : }
554 0 : GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv,
555 : &pch->purse_pub.eddsa_pub);
556 : {
557 : char pub_str[sizeof (pch->purse_pub) * 2];
558 : char *end;
559 :
560 0 : end = GNUNET_STRINGS_data_to_string (
561 0 : &pch->purse_pub,
562 : sizeof (pch->purse_pub),
563 : pub_str,
564 : sizeof (pub_str));
565 0 : *end = '\0';
566 0 : GNUNET_snprintf (arg_str,
567 : sizeof (arg_str),
568 : "/purses/%s/create",
569 : pub_str);
570 : }
571 0 : GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv,
572 : &pch->merge_pub.eddsa_pub);
573 0 : pch->url = TEAH_path_to_url (exchange,
574 : arg_str);
575 0 : if (NULL == pch->url)
576 : {
577 0 : GNUNET_break (0);
578 0 : GNUNET_free (pch);
579 0 : return NULL;
580 : }
581 0 : pch->num_deposits = num_deposits;
582 0 : pch->deposits = GNUNET_new_array (num_deposits,
583 : struct Deposit);
584 0 : deposit_arr = json_array ();
585 0 : GNUNET_assert (NULL != deposit_arr);
586 0 : url = TEAH_path_to_url (exchange,
587 : "/");
588 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
589 : "Signing with URL `%s'\n",
590 : url);
591 0 : for (unsigned int i = 0; i<num_deposits; i++)
592 : {
593 0 : const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
594 0 : const struct TALER_AgeCommitmentProof *acp = deposit->age_commitment_proof;
595 0 : struct Deposit *d = &pch->deposits[i];
596 : json_t *jdeposit;
597 0 : struct TALER_AgeCommitmentHash *aghp = NULL;
598 : struct TALER_AgeAttestation attest;
599 0 : struct TALER_AgeAttestation *attestp = NULL;
600 :
601 0 : if (NULL != acp)
602 : {
603 0 : TALER_age_commitment_hash (&acp->commitment,
604 : &d->ahac);
605 0 : aghp = &d->ahac;
606 0 : if (GNUNET_OK !=
607 0 : TALER_age_commitment_attest (acp,
608 : min_age,
609 : &attest))
610 : {
611 0 : GNUNET_break (0);
612 0 : json_decref (deposit_arr);
613 0 : GNUNET_free (url);
614 0 : GNUNET_free (pch);
615 0 : return NULL;
616 : }
617 : }
618 0 : d->contribution = deposit->amount;
619 0 : d->h_denom_pub = deposit->h_denom_pub;
620 0 : GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
621 : &d->coin_pub.eddsa_pub);
622 0 : TALER_wallet_purse_deposit_sign (
623 : url,
624 0 : &pch->purse_pub,
625 : &deposit->amount,
626 0 : &d->h_denom_pub,
627 0 : &d->ahac,
628 : &deposit->coin_priv,
629 : &d->coin_sig);
630 0 : jdeposit = GNUNET_JSON_PACK (
631 : GNUNET_JSON_pack_allow_null (
632 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
633 : aghp)),
634 : GNUNET_JSON_pack_allow_null (
635 : GNUNET_JSON_pack_data_auto ("age_attestation",
636 : attestp)),
637 : TALER_JSON_pack_amount ("amount",
638 : &deposit->amount),
639 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
640 : &deposit->h_denom_pub),
641 : TALER_JSON_pack_denom_sig ("ub_sig",
642 : &deposit->denom_sig),
643 : GNUNET_JSON_pack_data_auto ("coin_sig",
644 : &d->coin_sig),
645 : GNUNET_JSON_pack_data_auto ("coin_pub",
646 : &d->coin_pub));
647 0 : GNUNET_assert (0 ==
648 : json_array_append_new (deposit_arr,
649 : jdeposit));
650 : }
651 0 : GNUNET_free (url);
652 0 : TALER_wallet_purse_create_sign (pch->purse_expiration,
653 : &pch->h_contract_terms,
654 0 : &pch->merge_pub,
655 : min_age,
656 0 : &pch->purse_value_after_fees,
657 : purse_priv,
658 : &pch->purse_sig);
659 0 : if (upload_contract)
660 : {
661 0 : TALER_CRYPTO_contract_encrypt_for_merge (&pch->purse_pub,
662 : contract_priv,
663 : merge_priv,
664 : contract_terms,
665 : &pch->econtract.econtract,
666 : &pch->econtract.econtract_size);
667 0 : GNUNET_CRYPTO_ecdhe_key_get_public (&contract_priv->ecdhe_priv,
668 : &pch->econtract.contract_pub.ecdhe_pub);
669 0 : TALER_wallet_econtract_upload_sign (pch->econtract.econtract,
670 : pch->econtract.econtract_size,
671 0 : &pch->econtract.contract_pub,
672 : purse_priv,
673 : &pch->econtract.econtract_sig);
674 : }
675 0 : create_obj = GNUNET_JSON_PACK (
676 : TALER_JSON_pack_amount ("amount",
677 : &pch->purse_value_after_fees),
678 : GNUNET_JSON_pack_uint64 ("min_age",
679 : min_age),
680 : GNUNET_JSON_pack_allow_null (
681 : TALER_JSON_pack_econtract ("econtract",
682 : upload_contract
683 : ? &pch->econtract
684 : : NULL)),
685 : GNUNET_JSON_pack_data_auto ("purse_sig",
686 : &pch->purse_sig),
687 : GNUNET_JSON_pack_data_auto ("merge_pub",
688 : &pch->merge_pub),
689 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
690 : &pch->h_contract_terms),
691 : GNUNET_JSON_pack_timestamp ("purse_expiration",
692 : pch->purse_expiration),
693 : GNUNET_JSON_pack_array_steal ("deposits",
694 : deposit_arr));
695 0 : GNUNET_assert (NULL != create_obj);
696 0 : eh = TALER_EXCHANGE_curl_easy_get_ (pch->url);
697 0 : if ( (NULL == eh) ||
698 : (GNUNET_OK !=
699 0 : TALER_curl_easy_post (&pch->ctx,
700 : eh,
701 : create_obj)) )
702 : {
703 0 : GNUNET_break (0);
704 0 : if (NULL != eh)
705 0 : curl_easy_cleanup (eh);
706 0 : json_decref (create_obj);
707 0 : GNUNET_free (pch->econtract.econtract);
708 0 : GNUNET_free (pch->deposits);
709 0 : GNUNET_free (pch->url);
710 0 : GNUNET_free (pch);
711 0 : return NULL;
712 : }
713 0 : json_decref (create_obj);
714 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
715 : "URL for purse create with deposit: `%s'\n",
716 : pch->url);
717 0 : ctx = TEAH_handle_to_context (exchange);
718 0 : pch->job = GNUNET_CURL_job_add2 (ctx,
719 : eh,
720 0 : pch->ctx.headers,
721 : &handle_purse_create_deposit_finished,
722 : pch);
723 0 : return pch;
724 : }
725 :
726 :
727 : void
728 0 : TALER_EXCHANGE_purse_create_with_deposit_cancel (
729 : struct TALER_EXCHANGE_PurseCreateDepositHandle *pch)
730 : {
731 0 : if (NULL != pch->job)
732 : {
733 0 : GNUNET_CURL_job_cancel (pch->job);
734 0 : pch->job = NULL;
735 : }
736 0 : GNUNET_free (pch->econtract.econtract);
737 0 : GNUNET_free (pch->url);
738 0 : GNUNET_free (pch->deposits);
739 0 : TALER_curl_easy_post_finished (&pch->ctx);
740 0 : GNUNET_free (pch);
741 0 : }
742 :
743 :
744 : /* end of exchange_api_purse_create_with_deposit.c */
|