Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2022-2026 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_post-purses-PURSE_PUB-create.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 "taler/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/taler_json_lib.h"
30 : #include "taler/taler_exchange_service.h"
31 : #include "exchange_api_handle.h"
32 : #include "exchange_api_common.h"
33 : #include "taler/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 : * Signature proving the validity of the coin.
59 : */
60 : struct TALER_DenominationSignature denom_sig;
61 :
62 : /**
63 : * Age restriction hash for the coin.
64 : */
65 : struct TALER_AgeCommitmentHashP ahac;
66 :
67 : /**
68 : * How much did we say the coin contributed.
69 : */
70 : struct TALER_Amount contribution;
71 :
72 : /**
73 : * Age attestation for this coin. Valid if @e have_age.
74 : */
75 : struct TALER_AgeAttestationP attest;
76 :
77 : /**
78 : * True if this coin uses age attestation.
79 : */
80 : bool have_age;
81 :
82 : };
83 :
84 :
85 : /**
86 : * @brief A purse create with deposit handle
87 : */
88 : struct TALER_EXCHANGE_PostPursesCreateHandle
89 : {
90 :
91 : /**
92 : * The curl context for this request.
93 : */
94 : struct GNUNET_CURL_Context *ctx;
95 :
96 : /**
97 : * The base URL of the exchange.
98 : */
99 : char *base_url;
100 :
101 : /**
102 : * The keys of the exchange this request handle will use
103 : */
104 : struct TALER_EXCHANGE_Keys *keys;
105 :
106 : /**
107 : * The url for this request, set during _start.
108 : */
109 : char *url;
110 :
111 : /**
112 : * Context for #TEH_curl_easy_post(). Keeps the data that must
113 : * persist for Curl to make the upload.
114 : */
115 : struct TALER_CURL_PostContext post_ctx;
116 :
117 : /**
118 : * Handle for the request.
119 : */
120 : struct GNUNET_CURL_Job *job;
121 :
122 : /**
123 : * Function to call with the result.
124 : */
125 : TALER_EXCHANGE_PostPursesCreateCallback cb;
126 :
127 : /**
128 : * Closure for @a cb.
129 : */
130 : TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls;
131 :
132 : /**
133 : * Expected value in the purse after fees.
134 : */
135 : struct TALER_Amount purse_value_after_fees;
136 :
137 : /**
138 : * Our encrypted contract (if we had any).
139 : */
140 : struct TALER_EncryptedContract econtract;
141 :
142 : /**
143 : * Public key of the merge capability.
144 : */
145 : struct TALER_PurseMergePublicKeyP merge_pub;
146 :
147 : /**
148 : * Private key of the purse which we are creating.
149 : */
150 : struct TALER_PurseContractPrivateKeyP purse_priv;
151 :
152 : /**
153 : * Private key for the merge operation.
154 : */
155 : struct TALER_PurseMergePrivateKeyP merge_priv;
156 :
157 : /**
158 : * Private key to decrypt the contract.
159 : */
160 : struct TALER_ContractDiffiePrivateP contract_priv;
161 :
162 : /**
163 : * Contract terms for the payment.
164 : */
165 : json_t *contract_terms;
166 :
167 : /**
168 : * Minimum age, as per @e contract_terms.
169 : */
170 : uint32_t min_age;
171 :
172 : /**
173 : * Public key of the purse.
174 : */
175 : struct TALER_PurseContractPublicKeyP purse_pub;
176 :
177 : /**
178 : * Signature for purse creation.
179 : */
180 : struct TALER_PurseContractSignatureP purse_sig;
181 :
182 : /**
183 : * Hash over the purse's contract terms.
184 : */
185 : struct TALER_PrivateContractHashP h_contract_terms;
186 :
187 : /**
188 : * When does the purse expire.
189 : */
190 : struct GNUNET_TIME_Timestamp purse_expiration;
191 :
192 : /**
193 : * Array of @e num_deposit deposits.
194 : */
195 : struct Deposit *deposits;
196 :
197 : /**
198 : * How many deposits did we make?
199 : */
200 : unsigned int num_deposits;
201 :
202 : struct
203 : {
204 :
205 : /**
206 : * Whether we are uploading a contract.
207 : */
208 : bool upload_contract;
209 :
210 : } options;
211 :
212 : };
213 :
214 :
215 : /**
216 : * Function called when we're done processing the
217 : * HTTP /purses/$PID/create request.
218 : *
219 : * @param cls the `struct TALER_EXCHANGE_PostPursesCreateHandle`
220 : * @param response_code HTTP response code, 0 on error
221 : * @param response parsed JSON result, NULL on error
222 : */
223 : static void
224 11 : handle_purse_create_deposit_finished (void *cls,
225 : long response_code,
226 : const void *response)
227 : {
228 11 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch = cls;
229 11 : const json_t *j = response;
230 11 : struct TALER_EXCHANGE_PostPursesCreateResponse dr = {
231 : .hr.reply = j,
232 11 : .hr.http_status = (unsigned int) response_code
233 : };
234 11 : const struct TALER_EXCHANGE_Keys *keys = pch->keys;
235 :
236 11 : pch->job = NULL;
237 11 : switch (response_code)
238 : {
239 0 : case 0:
240 0 : dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
241 0 : break;
242 9 : case MHD_HTTP_OK:
243 : {
244 : struct GNUNET_TIME_Timestamp etime;
245 : struct TALER_Amount total_deposited;
246 : struct TALER_ExchangeSignatureP exchange_sig;
247 : struct TALER_ExchangePublicKeyP exchange_pub;
248 : struct GNUNET_JSON_Specification spec[] = {
249 9 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
250 : &exchange_sig),
251 9 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
252 : &exchange_pub),
253 9 : GNUNET_JSON_spec_timestamp ("exchange_timestamp",
254 : &etime),
255 9 : TALER_JSON_spec_amount ("total_deposited",
256 9 : pch->purse_value_after_fees.currency,
257 : &total_deposited),
258 9 : GNUNET_JSON_spec_end ()
259 : };
260 :
261 9 : if (GNUNET_OK !=
262 9 : GNUNET_JSON_parse (j,
263 : spec,
264 : NULL, NULL))
265 : {
266 0 : GNUNET_break_op (0);
267 0 : dr.hr.http_status = 0;
268 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
269 0 : break;
270 : }
271 9 : if (GNUNET_OK !=
272 9 : TALER_EXCHANGE_test_signing_key (keys,
273 : &exchange_pub))
274 : {
275 0 : GNUNET_break_op (0);
276 0 : dr.hr.http_status = 0;
277 0 : dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID;
278 0 : break;
279 : }
280 9 : if (GNUNET_OK !=
281 9 : TALER_exchange_online_purse_created_verify (
282 : etime,
283 : pch->purse_expiration,
284 9 : &pch->purse_value_after_fees,
285 : &total_deposited,
286 9 : &pch->purse_pub,
287 9 : &pch->h_contract_terms,
288 : &exchange_pub,
289 : &exchange_sig))
290 : {
291 0 : GNUNET_break_op (0);
292 0 : dr.hr.http_status = 0;
293 0 : dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID;
294 0 : break;
295 : }
296 9 : dr.details.ok.exchange_pub = exchange_pub;
297 9 : dr.details.ok.exchange_sig = exchange_sig;
298 : }
299 9 : break;
300 0 : case MHD_HTTP_BAD_REQUEST:
301 : /* This should never happen, either us or the exchange is buggy
302 : (or API version conflict); just pass JSON reply to the application */
303 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
304 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
305 0 : break;
306 0 : case MHD_HTTP_FORBIDDEN:
307 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
308 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
309 : /* Nothing really to verify, exchange says one of the signatures is
310 : invalid; as we checked them, this should never happen, we
311 : should pass the JSON reply to the application */
312 0 : break;
313 0 : case MHD_HTTP_NOT_FOUND:
314 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
315 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
316 : /* Nothing really to verify, this should never
317 : happen, we should pass the JSON reply to the application */
318 0 : break;
319 2 : case MHD_HTTP_CONFLICT:
320 : {
321 2 : dr.hr.ec = TALER_JSON_get_error_code (j);
322 2 : switch (dr.hr.ec)
323 : {
324 0 : case TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA:
325 0 : if (GNUNET_OK !=
326 0 : TALER_EXCHANGE_check_purse_create_conflict_ (
327 0 : &pch->purse_sig,
328 0 : &pch->purse_pub,
329 : j))
330 : {
331 0 : GNUNET_break_op (0);
332 0 : dr.hr.http_status = 0;
333 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
334 0 : break;
335 : }
336 0 : break;
337 2 : case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
338 : /* Nothing to check anymore here, proof needs to be
339 : checked in the GET /coins/$COIN_PUB handler */
340 2 : break;
341 0 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
342 : // FIXME #7267: write check (add to exchange_api_common!) */
343 0 : break;
344 0 : case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
345 : {
346 : struct TALER_CoinSpendPublicKeyP coin_pub;
347 : struct TALER_CoinSpendSignatureP coin_sig;
348 : struct TALER_DenominationHashP h_denom_pub;
349 : struct TALER_AgeCommitmentHashP phac;
350 0 : bool found = false;
351 :
352 0 : if (GNUNET_OK !=
353 0 : TALER_EXCHANGE_check_purse_coin_conflict_ (
354 0 : &pch->purse_pub,
355 0 : pch->base_url,
356 : j,
357 : &h_denom_pub,
358 : &phac,
359 : &coin_pub,
360 : &coin_sig))
361 : {
362 0 : GNUNET_break_op (0);
363 0 : dr.hr.http_status = 0;
364 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
365 0 : break;
366 : }
367 0 : for (unsigned int i = 0; i<pch->num_deposits; i++)
368 : {
369 0 : struct Deposit *deposit = &pch->deposits[i];
370 :
371 0 : if (0 !=
372 0 : GNUNET_memcmp (&coin_pub,
373 : &deposit->coin_pub))
374 0 : continue;
375 0 : if (0 !=
376 0 : GNUNET_memcmp (&deposit->h_denom_pub,
377 : &h_denom_pub))
378 : {
379 0 : found = true;
380 0 : break;
381 : }
382 0 : if (0 !=
383 0 : GNUNET_memcmp (&deposit->ahac,
384 : &phac))
385 : {
386 0 : found = true;
387 0 : break;
388 : }
389 0 : if (0 ==
390 0 : GNUNET_memcmp (&coin_sig,
391 : &deposit->coin_sig))
392 : {
393 0 : GNUNET_break_op (0);
394 0 : continue;
395 : }
396 0 : found = true;
397 0 : break;
398 : }
399 0 : if (! found)
400 : {
401 : /* conflict is for a different coin! */
402 0 : GNUNET_break_op (0);
403 0 : dr.hr.http_status = 0;
404 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
405 0 : break;
406 : }
407 : }
408 : /* fall-through */
409 : case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:
410 0 : if (GNUNET_OK !=
411 0 : TALER_EXCHANGE_check_purse_econtract_conflict_ (
412 0 : &pch->econtract.econtract_sig,
413 0 : &pch->purse_pub,
414 : j))
415 : {
416 0 : GNUNET_break_op (0);
417 0 : dr.hr.http_status = 0;
418 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
419 0 : break;
420 : }
421 0 : break;
422 0 : default:
423 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
424 : "Unexpected error code %d for conflicting deposit\n",
425 : dr.hr.ec);
426 0 : GNUNET_break_op (0);
427 0 : dr.hr.http_status = 0;
428 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
429 : }
430 : }
431 2 : break;
432 0 : case MHD_HTTP_GONE:
433 : /* could happen if denomination was revoked */
434 : /* Note: one might want to check /keys for revocation
435 : signature here, alas tricky in case our /keys
436 : is outdated => left to clients */
437 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
438 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
439 0 : break;
440 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
441 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
442 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
443 : /* Server had an internal issue; we should retry, but this API
444 : leaves this to the application */
445 0 : break;
446 0 : default:
447 : /* unexpected response code */
448 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
449 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
450 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
451 : "Unexpected response code %u/%d for exchange deposit\n",
452 : (unsigned int) response_code,
453 : dr.hr.ec);
454 0 : GNUNET_break_op (0);
455 0 : break;
456 : }
457 11 : if (NULL != pch->cb)
458 : {
459 11 : pch->cb (pch->cb_cls,
460 : &dr);
461 11 : pch->cb = NULL;
462 : }
463 11 : TALER_EXCHANGE_post_purses_create_cancel (pch);
464 11 : }
465 :
466 :
467 : struct TALER_EXCHANGE_PostPursesCreateHandle *
468 11 : TALER_EXCHANGE_post_purses_create_create (
469 : struct GNUNET_CURL_Context *ctx,
470 : const char *url,
471 : struct TALER_EXCHANGE_Keys *keys,
472 : const struct TALER_PurseContractPrivateKeyP *purse_priv,
473 : const struct TALER_PurseMergePrivateKeyP *merge_priv,
474 : const struct TALER_ContractDiffiePrivateP *contract_priv,
475 : const json_t *contract_terms,
476 : unsigned int num_deposits,
477 : const struct TALER_EXCHANGE_PurseDeposit deposits[static num_deposits])
478 11 : {
479 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch;
480 :
481 11 : pch = GNUNET_new (struct TALER_EXCHANGE_PostPursesCreateHandle);
482 11 : pch->ctx = ctx;
483 11 : pch->base_url = GNUNET_strdup (url);
484 11 : pch->purse_priv = *purse_priv;
485 11 : pch->merge_priv = *merge_priv;
486 11 : pch->contract_priv = *contract_priv;
487 11 : pch->contract_terms = json_incref ((json_t *) contract_terms);
488 : {
489 : struct GNUNET_JSON_Specification spec[] = {
490 11 : GNUNET_JSON_spec_timestamp ("pay_deadline",
491 : &pch->purse_expiration),
492 11 : TALER_JSON_spec_amount_any ("amount",
493 : &pch->purse_value_after_fees),
494 11 : GNUNET_JSON_spec_mark_optional (
495 : GNUNET_JSON_spec_uint32 ("minimum_age",
496 : &pch->min_age),
497 : NULL),
498 11 : GNUNET_JSON_spec_end ()
499 : };
500 :
501 11 : if (GNUNET_OK !=
502 11 : GNUNET_JSON_parse (contract_terms,
503 : spec,
504 : NULL, NULL))
505 : {
506 0 : GNUNET_break (0);
507 0 : GNUNET_free (pch->base_url);
508 0 : GNUNET_free (pch);
509 0 : return NULL;
510 : }
511 : }
512 11 : if (GNUNET_OK !=
513 11 : TALER_JSON_contract_hash (contract_terms,
514 : &pch->h_contract_terms))
515 : {
516 0 : GNUNET_break (0);
517 0 : GNUNET_free (pch->base_url);
518 0 : GNUNET_free (pch);
519 0 : return NULL;
520 : }
521 11 : GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv,
522 : &pch->purse_pub.eddsa_pub);
523 11 : GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv,
524 : &pch->merge_pub.eddsa_pub);
525 11 : pch->num_deposits = num_deposits;
526 11 : pch->deposits = GNUNET_new_array (num_deposits,
527 : struct Deposit);
528 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
529 : "Signing with URL `%s'\n",
530 : url);
531 22 : for (unsigned int i = 0; i<num_deposits; i++)
532 : {
533 11 : const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
534 11 : const struct TALER_AgeCommitmentProof *acp = deposit->age_commitment_proof;
535 11 : struct Deposit *d = &pch->deposits[i];
536 :
537 11 : if (NULL != acp)
538 : {
539 0 : TALER_age_commitment_hash (&acp->commitment,
540 : &d->ahac);
541 0 : d->have_age = true;
542 0 : if (GNUNET_OK !=
543 0 : TALER_age_commitment_attest (acp,
544 0 : pch->min_age,
545 : &d->attest))
546 : {
547 0 : GNUNET_break (0);
548 0 : GNUNET_array_grow (pch->deposits,
549 : pch->num_deposits,
550 : 0);
551 0 : GNUNET_free (pch->base_url);
552 0 : GNUNET_free (pch);
553 0 : return NULL;
554 : }
555 : }
556 11 : d->contribution = deposit->amount;
557 11 : d->h_denom_pub = deposit->h_denom_pub;
558 11 : TALER_denom_sig_copy (&d->denom_sig,
559 : &deposit->denom_sig);
560 11 : GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
561 : &d->coin_pub.eddsa_pub);
562 11 : TALER_wallet_purse_deposit_sign (
563 : url,
564 11 : &pch->purse_pub,
565 : &deposit->amount,
566 11 : &d->h_denom_pub,
567 11 : &d->ahac,
568 : &deposit->coin_priv,
569 : &d->coin_sig);
570 : }
571 11 : pch->keys = TALER_EXCHANGE_keys_incref (keys);
572 11 : return pch;
573 : }
574 :
575 :
576 : enum GNUNET_GenericReturnValue
577 11 : TALER_EXCHANGE_post_purses_create_set_options_ (
578 : struct TALER_EXCHANGE_PostPursesCreateHandle *ppch,
579 : unsigned int num_options,
580 : const struct TALER_EXCHANGE_PostPursesCreateOptionValue options[])
581 : {
582 22 : for (unsigned int i = 0; i < num_options; i++)
583 : {
584 22 : const struct TALER_EXCHANGE_PostPursesCreateOptionValue *opt = &options[i];
585 :
586 22 : switch (opt->option)
587 : {
588 11 : case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_END:
589 11 : return GNUNET_OK;
590 11 : case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_UPLOAD_CONTRACT:
591 11 : ppch->options.upload_contract = true;
592 11 : break;
593 : }
594 : }
595 0 : return GNUNET_OK;
596 : }
597 :
598 :
599 : enum TALER_ErrorCode
600 11 : TALER_EXCHANGE_post_purses_create_start (
601 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch,
602 : TALER_EXCHANGE_PostPursesCreateCallback cb,
603 : TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls)
604 : {
605 : char arg_str[sizeof (pch->purse_pub) * 2 + 32];
606 : CURL *eh;
607 : json_t *deposit_arr;
608 : json_t *body;
609 :
610 11 : pch->cb = cb;
611 11 : pch->cb_cls = cb_cls;
612 11 : deposit_arr = json_array ();
613 11 : GNUNET_assert (NULL != deposit_arr);
614 :
615 22 : for (unsigned int i = 0; i<pch->num_deposits; i++)
616 : {
617 11 : struct Deposit *d = &pch->deposits[i];
618 11 : struct TALER_AgeCommitmentHashP *aghp = NULL;
619 11 : struct TALER_AgeAttestationP *attestp = NULL;
620 : json_t *jdeposit;
621 :
622 11 : if (d->have_age)
623 : {
624 0 : aghp = &d->ahac;
625 0 : attestp = &d->attest;
626 : }
627 11 : jdeposit = GNUNET_JSON_PACK (
628 : GNUNET_JSON_pack_allow_null (
629 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
630 : aghp)),
631 : GNUNET_JSON_pack_allow_null (
632 : GNUNET_JSON_pack_data_auto ("age_attestation",
633 : attestp)),
634 : TALER_JSON_pack_amount ("amount",
635 : &d->contribution),
636 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
637 : &d->h_denom_pub),
638 : TALER_JSON_pack_denom_sig ("ub_sig",
639 : &d->denom_sig),
640 : GNUNET_JSON_pack_data_auto ("coin_sig",
641 : &d->coin_sig),
642 : GNUNET_JSON_pack_data_auto ("coin_pub",
643 : &d->coin_pub));
644 11 : GNUNET_assert (0 ==
645 : json_array_append_new (deposit_arr,
646 : jdeposit));
647 :
648 : }
649 :
650 11 : if (pch->options.upload_contract)
651 : {
652 11 : TALER_CRYPTO_contract_encrypt_for_merge (
653 11 : &pch->purse_pub,
654 11 : &pch->contract_priv,
655 11 : &pch->merge_priv,
656 11 : pch->contract_terms,
657 : &pch->econtract.econtract,
658 : &pch->econtract.econtract_size);
659 11 : GNUNET_CRYPTO_ecdhe_key_get_public (
660 11 : &pch->contract_priv.ecdhe_priv,
661 : &pch->econtract.contract_pub.ecdhe_pub);
662 11 : TALER_wallet_econtract_upload_sign (
663 11 : pch->econtract.econtract,
664 : pch->econtract.econtract_size,
665 11 : &pch->econtract.contract_pub,
666 11 : &pch->purse_priv,
667 : &pch->econtract.econtract_sig);
668 : }
669 11 : TALER_wallet_purse_create_sign (pch->purse_expiration,
670 11 : &pch->h_contract_terms,
671 11 : &pch->merge_pub,
672 : pch->min_age,
673 11 : &pch->purse_value_after_fees,
674 11 : &pch->purse_priv,
675 : &pch->purse_sig);
676 :
677 11 : body = GNUNET_JSON_PACK (
678 : TALER_JSON_pack_amount ("amount",
679 : &pch->purse_value_after_fees),
680 : GNUNET_JSON_pack_uint64 ("min_age",
681 : pch->min_age),
682 : GNUNET_JSON_pack_allow_null (
683 : TALER_JSON_pack_econtract ("econtract",
684 : pch->options.upload_contract
685 : ? &pch->econtract
686 : : NULL)),
687 : GNUNET_JSON_pack_data_auto ("purse_sig",
688 : &pch->purse_sig),
689 : GNUNET_JSON_pack_data_auto ("merge_pub",
690 : &pch->merge_pub),
691 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
692 : &pch->h_contract_terms),
693 : GNUNET_JSON_pack_timestamp ("purse_expiration",
694 : pch->purse_expiration),
695 : GNUNET_JSON_pack_array_steal ("deposits",
696 : deposit_arr));
697 11 : if (NULL == body)
698 : {
699 0 : GNUNET_break (0);
700 0 : return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
701 : }
702 :
703 : {
704 : char pub_str[sizeof (pch->purse_pub) * 2];
705 : char *end;
706 :
707 11 : end = GNUNET_STRINGS_data_to_string (
708 11 : &pch->purse_pub,
709 : sizeof (pch->purse_pub),
710 : pub_str,
711 : sizeof (pub_str));
712 11 : *end = '\0';
713 11 : GNUNET_snprintf (arg_str,
714 : sizeof (arg_str),
715 : "purses/%s/create",
716 : pub_str);
717 : }
718 11 : pch->url = TALER_url_join (pch->base_url,
719 : arg_str,
720 : NULL);
721 11 : if (NULL == pch->url)
722 : {
723 0 : GNUNET_break (0);
724 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
725 : }
726 11 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727 : "URL for purse create with deposit: `%s'\n",
728 : pch->url);
729 11 : eh = TALER_EXCHANGE_curl_easy_get_ (pch->url);
730 22 : if ( (NULL == eh) ||
731 : (GNUNET_OK !=
732 11 : TALER_curl_easy_post (&pch->post_ctx,
733 : eh,
734 : body)) )
735 : {
736 0 : GNUNET_break (0);
737 0 : if (NULL != eh)
738 0 : curl_easy_cleanup (eh);
739 0 : json_decref (body);
740 0 : return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
741 : }
742 11 : json_decref (body);
743 22 : pch->job = GNUNET_CURL_job_add2 (pch->ctx,
744 : eh,
745 11 : pch->post_ctx.headers,
746 : &handle_purse_create_deposit_finished,
747 : pch);
748 11 : if (NULL == pch->job)
749 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
750 11 : return TALER_EC_NONE;
751 : }
752 :
753 :
754 : void
755 11 : TALER_EXCHANGE_post_purses_create_cancel (
756 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch)
757 : {
758 11 : if (NULL != pch->job)
759 : {
760 0 : GNUNET_CURL_job_cancel (pch->job);
761 0 : pch->job = NULL;
762 : }
763 11 : json_decref (pch->contract_terms);
764 11 : GNUNET_free (pch->econtract.econtract);
765 11 : GNUNET_free (pch->base_url);
766 11 : GNUNET_free (pch->url);
767 :
768 22 : for (unsigned int i = 0; i<pch->num_deposits; i++)
769 : {
770 11 : struct Deposit *d = &pch->deposits[i];
771 :
772 11 : TALER_denom_sig_free (&d->denom_sig);
773 : }
774 11 : GNUNET_array_grow (pch->deposits,
775 : pch->num_deposits,
776 : 0);
777 11 : TALER_EXCHANGE_keys_decref (pch->keys);
778 11 : TALER_curl_easy_post_finished (&pch->post_ctx);
779 11 : GNUNET_free (pch);
780 11 : }
781 :
782 :
783 : /* end of exchange_api_post-purses-PURSE_PUB-create.c */
|