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_JSON_Specification spec[] = {
245 9 : GNUNET_JSON_spec_fixed_auto ("exchange_sig",
246 : &dr.details.ok.exchange_sig),
247 9 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
248 : &dr.details.ok.exchange_pub),
249 9 : GNUNET_JSON_spec_timestamp ("exchange_timestamp",
250 : &dr.details.ok.exchange_timestamp),
251 9 : TALER_JSON_spec_amount ("total_deposited",
252 9 : pch->purse_value_after_fees.currency,
253 : &dr.details.ok.total_deposited),
254 9 : GNUNET_JSON_spec_end ()
255 : };
256 :
257 9 : if (GNUNET_OK !=
258 9 : GNUNET_JSON_parse (j,
259 : spec,
260 : NULL, NULL))
261 : {
262 0 : GNUNET_break_op (0);
263 0 : dr.hr.http_status = 0;
264 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
265 0 : break;
266 : }
267 9 : if (GNUNET_OK !=
268 9 : TALER_EXCHANGE_test_signing_key (keys,
269 : &dr.details.ok.exchange_pub))
270 : {
271 0 : GNUNET_break_op (0);
272 0 : dr.hr.http_status = 0;
273 0 : dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID;
274 0 : break;
275 : }
276 9 : if (GNUNET_OK !=
277 9 : TALER_exchange_online_purse_created_verify (
278 : dr.details.ok.exchange_timestamp,
279 : pch->purse_expiration,
280 9 : &pch->purse_value_after_fees,
281 : &dr.details.ok.total_deposited,
282 9 : &pch->purse_pub,
283 9 : &pch->h_contract_terms,
284 : &dr.details.ok.exchange_pub,
285 : &dr.details.ok.exchange_sig))
286 : {
287 0 : GNUNET_break_op (0);
288 0 : dr.hr.http_status = 0;
289 0 : dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID;
290 0 : break;
291 : }
292 : }
293 9 : break;
294 0 : case MHD_HTTP_BAD_REQUEST:
295 : /* This should never happen, either us or the exchange is buggy
296 : (or API version conflict); just pass JSON reply to the application */
297 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
298 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
299 0 : break;
300 0 : case MHD_HTTP_FORBIDDEN:
301 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
302 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
303 : /* Nothing really to verify, exchange says one of the signatures is
304 : invalid; as we checked them, this should never happen, we
305 : should pass the JSON reply to the application */
306 0 : break;
307 0 : case MHD_HTTP_NOT_FOUND:
308 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
309 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
310 : /* Nothing really to verify, this should never
311 : happen, we should pass the JSON reply to the application */
312 0 : break;
313 2 : case MHD_HTTP_CONFLICT:
314 : {
315 2 : dr.hr.ec = TALER_JSON_get_error_code (j);
316 2 : switch (dr.hr.ec)
317 : {
318 0 : case TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA:
319 0 : if (GNUNET_OK !=
320 0 : TALER_EXCHANGE_check_purse_create_conflict_ (
321 0 : &pch->purse_sig,
322 0 : &pch->purse_pub,
323 : j))
324 : {
325 0 : GNUNET_break_op (0);
326 0 : dr.hr.http_status = 0;
327 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
328 0 : break;
329 : }
330 0 : break;
331 2 : case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
332 : /* Nothing to check anymore here, proof needs to be
333 : checked in the GET /coins/$COIN_PUB handler */
334 2 : break;
335 0 : case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
336 : // FIXME #7267: write check (add to exchange_api_common!) */
337 0 : break;
338 0 : case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
339 : {
340 : struct TALER_CoinSpendPublicKeyP coin_pub;
341 : struct TALER_CoinSpendSignatureP coin_sig;
342 : struct TALER_DenominationHashP h_denom_pub;
343 : struct TALER_AgeCommitmentHashP phac;
344 0 : bool found = false;
345 :
346 0 : if (GNUNET_OK !=
347 0 : TALER_EXCHANGE_check_purse_coin_conflict_ (
348 0 : &pch->purse_pub,
349 0 : pch->base_url,
350 : j,
351 : &h_denom_pub,
352 : &phac,
353 : &coin_pub,
354 : &coin_sig))
355 : {
356 0 : GNUNET_break_op (0);
357 0 : dr.hr.http_status = 0;
358 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
359 0 : break;
360 : }
361 0 : for (unsigned int i = 0; i<pch->num_deposits; i++)
362 : {
363 0 : struct Deposit *deposit = &pch->deposits[i];
364 :
365 0 : if (0 !=
366 0 : GNUNET_memcmp (&coin_pub,
367 : &deposit->coin_pub))
368 0 : continue;
369 0 : if (0 !=
370 0 : GNUNET_memcmp (&deposit->h_denom_pub,
371 : &h_denom_pub))
372 : {
373 0 : found = true;
374 0 : break;
375 : }
376 0 : if (0 !=
377 0 : GNUNET_memcmp (&deposit->ahac,
378 : &phac))
379 : {
380 0 : found = true;
381 0 : break;
382 : }
383 0 : if (0 ==
384 0 : GNUNET_memcmp (&coin_sig,
385 : &deposit->coin_sig))
386 : {
387 0 : GNUNET_break_op (0);
388 0 : continue;
389 : }
390 0 : found = true;
391 0 : break;
392 : }
393 0 : if (! found)
394 : {
395 : /* conflict is for a different coin! */
396 0 : GNUNET_break_op (0);
397 0 : dr.hr.http_status = 0;
398 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
399 0 : break;
400 : }
401 : }
402 0 : break;
403 0 : case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:
404 0 : if (GNUNET_OK !=
405 0 : TALER_EXCHANGE_check_purse_econtract_conflict_ (
406 0 : &pch->econtract.econtract_sig,
407 0 : &pch->purse_pub,
408 : j))
409 : {
410 0 : GNUNET_break_op (0);
411 0 : dr.hr.http_status = 0;
412 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
413 0 : break;
414 : }
415 0 : break;
416 0 : default:
417 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
418 : "Unexpected error code %d for conflicting deposit\n",
419 : dr.hr.ec);
420 0 : GNUNET_break_op (0);
421 0 : dr.hr.http_status = 0;
422 0 : dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
423 : } /* switch on (ec) */
424 : }
425 2 : break;
426 0 : case MHD_HTTP_GONE:
427 : /* could happen if denomination was revoked */
428 : /* Note: one might want to check /keys for revocation
429 : signature here, alas tricky in case our /keys
430 : is outdated => left to clients */
431 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
432 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
433 0 : break;
434 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
435 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
436 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
437 : /* Server had an internal issue; we should retry, but this API
438 : leaves this to the application */
439 0 : break;
440 0 : default:
441 : /* unexpected response code */
442 0 : dr.hr.ec = TALER_JSON_get_error_code (j);
443 0 : dr.hr.hint = TALER_JSON_get_error_hint (j);
444 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
445 : "Unexpected response code %u/%d for exchange deposit\n",
446 : (unsigned int) response_code,
447 : dr.hr.ec);
448 0 : GNUNET_break_op (0);
449 0 : break;
450 : }
451 11 : if (NULL != pch->cb)
452 : {
453 11 : pch->cb (pch->cb_cls,
454 : &dr);
455 11 : pch->cb = NULL;
456 : }
457 11 : TALER_EXCHANGE_post_purses_create_cancel (pch);
458 11 : }
459 :
460 :
461 : struct TALER_EXCHANGE_PostPursesCreateHandle *
462 11 : TALER_EXCHANGE_post_purses_create_create (
463 : struct GNUNET_CURL_Context *ctx,
464 : const char *url,
465 : struct TALER_EXCHANGE_Keys *keys,
466 : const struct TALER_PurseContractPrivateKeyP *purse_priv,
467 : const struct TALER_PurseMergePrivateKeyP *merge_priv,
468 : const struct TALER_ContractDiffiePrivateP *contract_priv,
469 : const json_t *contract_terms,
470 : unsigned int num_deposits,
471 : const struct TALER_EXCHANGE_PurseDeposit deposits[static num_deposits])
472 11 : {
473 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch;
474 :
475 11 : pch = GNUNET_new (struct TALER_EXCHANGE_PostPursesCreateHandle);
476 11 : pch->ctx = ctx;
477 11 : pch->base_url = GNUNET_strdup (url);
478 11 : pch->purse_priv = *purse_priv;
479 11 : pch->merge_priv = *merge_priv;
480 11 : pch->contract_priv = *contract_priv;
481 11 : pch->contract_terms = json_incref ((json_t *) contract_terms);
482 : {
483 : struct GNUNET_JSON_Specification spec[] = {
484 11 : GNUNET_JSON_spec_timestamp ("pay_deadline",
485 : &pch->purse_expiration),
486 11 : TALER_JSON_spec_amount_any ("amount",
487 : &pch->purse_value_after_fees),
488 11 : GNUNET_JSON_spec_mark_optional (
489 : GNUNET_JSON_spec_uint32 ("minimum_age",
490 : &pch->min_age),
491 : NULL),
492 11 : GNUNET_JSON_spec_end ()
493 : };
494 :
495 11 : if (GNUNET_OK !=
496 11 : GNUNET_JSON_parse (contract_terms,
497 : spec,
498 : NULL, NULL))
499 : {
500 0 : GNUNET_break (0);
501 0 : GNUNET_free (pch->base_url);
502 0 : GNUNET_free (pch);
503 0 : return NULL;
504 : }
505 : }
506 11 : if (GNUNET_OK !=
507 11 : TALER_JSON_contract_hash (contract_terms,
508 : &pch->h_contract_terms))
509 : {
510 0 : GNUNET_break (0);
511 0 : GNUNET_free (pch->base_url);
512 0 : GNUNET_free (pch);
513 0 : return NULL;
514 : }
515 11 : GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv,
516 : &pch->purse_pub.eddsa_pub);
517 11 : GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv,
518 : &pch->merge_pub.eddsa_pub);
519 11 : pch->num_deposits = num_deposits;
520 11 : pch->deposits = GNUNET_new_array (num_deposits,
521 : struct Deposit);
522 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
523 : "Signing with URL `%s'\n",
524 : url);
525 22 : for (unsigned int i = 0; i<num_deposits; i++)
526 : {
527 11 : const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
528 11 : const struct TALER_AgeCommitmentProof *acp = deposit->age_commitment_proof;
529 11 : struct Deposit *d = &pch->deposits[i];
530 :
531 11 : if (NULL != acp)
532 : {
533 0 : TALER_age_commitment_hash (&acp->commitment,
534 : &d->ahac);
535 0 : d->have_age = true;
536 0 : if (GNUNET_OK !=
537 0 : TALER_age_commitment_attest (acp,
538 0 : pch->min_age,
539 : &d->attest))
540 : {
541 0 : GNUNET_break (0);
542 0 : GNUNET_array_grow (pch->deposits,
543 : pch->num_deposits,
544 : 0);
545 0 : GNUNET_free (pch->base_url);
546 0 : GNUNET_free (pch);
547 0 : return NULL;
548 : }
549 : }
550 11 : d->contribution = deposit->amount;
551 11 : d->h_denom_pub = deposit->h_denom_pub;
552 11 : TALER_denom_sig_copy (&d->denom_sig,
553 : &deposit->denom_sig);
554 11 : GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
555 : &d->coin_pub.eddsa_pub);
556 11 : TALER_wallet_purse_deposit_sign (
557 : url,
558 11 : &pch->purse_pub,
559 : &deposit->amount,
560 11 : &d->h_denom_pub,
561 11 : &d->ahac,
562 : &deposit->coin_priv,
563 : &d->coin_sig);
564 : }
565 11 : pch->keys = TALER_EXCHANGE_keys_incref (keys);
566 11 : return pch;
567 : }
568 :
569 :
570 : enum GNUNET_GenericReturnValue
571 11 : TALER_EXCHANGE_post_purses_create_set_options_ (
572 : struct TALER_EXCHANGE_PostPursesCreateHandle *ppch,
573 : unsigned int num_options,
574 : const struct TALER_EXCHANGE_PostPursesCreateOptionValue options[])
575 : {
576 22 : for (unsigned int i = 0; i < num_options; i++)
577 : {
578 22 : const struct TALER_EXCHANGE_PostPursesCreateOptionValue *opt = &options[i];
579 :
580 22 : switch (opt->option)
581 : {
582 11 : case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_END:
583 11 : return GNUNET_OK;
584 11 : case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_UPLOAD_CONTRACT:
585 11 : ppch->options.upload_contract = true;
586 11 : break;
587 : }
588 : }
589 0 : return GNUNET_OK;
590 : }
591 :
592 :
593 : enum TALER_ErrorCode
594 11 : TALER_EXCHANGE_post_purses_create_start (
595 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch,
596 : TALER_EXCHANGE_PostPursesCreateCallback cb,
597 : TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls)
598 : {
599 : char arg_str[sizeof (pch->purse_pub) * 2 + 32];
600 : CURL *eh;
601 : json_t *deposit_arr;
602 : json_t *body;
603 :
604 11 : pch->cb = cb;
605 11 : pch->cb_cls = cb_cls;
606 11 : deposit_arr = json_array ();
607 11 : GNUNET_assert (NULL != deposit_arr);
608 :
609 22 : for (unsigned int i = 0; i<pch->num_deposits; i++)
610 : {
611 11 : struct Deposit *d = &pch->deposits[i];
612 11 : struct TALER_AgeCommitmentHashP *aghp = NULL;
613 11 : struct TALER_AgeAttestationP *attestp = NULL;
614 : json_t *jdeposit;
615 :
616 11 : if (d->have_age)
617 : {
618 0 : aghp = &d->ahac;
619 0 : attestp = &d->attest;
620 : }
621 11 : jdeposit = GNUNET_JSON_PACK (
622 : GNUNET_JSON_pack_allow_null (
623 : GNUNET_JSON_pack_data_auto ("h_age_commitment",
624 : aghp)),
625 : GNUNET_JSON_pack_allow_null (
626 : GNUNET_JSON_pack_data_auto ("age_attestation",
627 : attestp)),
628 : TALER_JSON_pack_amount ("amount",
629 : &d->contribution),
630 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
631 : &d->h_denom_pub),
632 : TALER_JSON_pack_denom_sig ("ub_sig",
633 : &d->denom_sig),
634 : GNUNET_JSON_pack_data_auto ("coin_sig",
635 : &d->coin_sig),
636 : GNUNET_JSON_pack_data_auto ("coin_pub",
637 : &d->coin_pub));
638 11 : GNUNET_assert (0 ==
639 : json_array_append_new (deposit_arr,
640 : jdeposit));
641 :
642 : }
643 :
644 11 : if (pch->options.upload_contract)
645 : {
646 11 : TALER_CRYPTO_contract_encrypt_for_merge (
647 11 : &pch->purse_pub,
648 11 : &pch->contract_priv,
649 11 : &pch->merge_priv,
650 11 : pch->contract_terms,
651 : &pch->econtract.econtract,
652 : &pch->econtract.econtract_size);
653 11 : GNUNET_CRYPTO_ecdhe_key_get_public (
654 11 : &pch->contract_priv.ecdhe_priv,
655 : &pch->econtract.contract_pub.ecdhe_pub);
656 11 : TALER_wallet_econtract_upload_sign (
657 11 : pch->econtract.econtract,
658 : pch->econtract.econtract_size,
659 11 : &pch->econtract.contract_pub,
660 11 : &pch->purse_priv,
661 : &pch->econtract.econtract_sig);
662 : }
663 11 : TALER_wallet_purse_create_sign (pch->purse_expiration,
664 11 : &pch->h_contract_terms,
665 11 : &pch->merge_pub,
666 : pch->min_age,
667 11 : &pch->purse_value_after_fees,
668 11 : &pch->purse_priv,
669 : &pch->purse_sig);
670 :
671 11 : body = GNUNET_JSON_PACK (
672 : TALER_JSON_pack_amount ("amount",
673 : &pch->purse_value_after_fees),
674 : GNUNET_JSON_pack_uint64 ("min_age",
675 : pch->min_age),
676 : GNUNET_JSON_pack_allow_null (
677 : TALER_JSON_pack_econtract ("econtract",
678 : pch->options.upload_contract
679 : ? &pch->econtract
680 : : NULL)),
681 : GNUNET_JSON_pack_data_auto ("purse_sig",
682 : &pch->purse_sig),
683 : GNUNET_JSON_pack_data_auto ("merge_pub",
684 : &pch->merge_pub),
685 : GNUNET_JSON_pack_data_auto ("h_contract_terms",
686 : &pch->h_contract_terms),
687 : GNUNET_JSON_pack_timestamp ("purse_expiration",
688 : pch->purse_expiration),
689 : GNUNET_JSON_pack_array_steal ("deposits",
690 : deposit_arr));
691 11 : if (NULL == body)
692 : {
693 0 : GNUNET_break (0);
694 0 : return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
695 : }
696 :
697 : {
698 : char pub_str[sizeof (pch->purse_pub) * 2];
699 : char *end;
700 :
701 11 : end = GNUNET_STRINGS_data_to_string (
702 11 : &pch->purse_pub,
703 : sizeof (pch->purse_pub),
704 : pub_str,
705 : sizeof (pub_str));
706 11 : *end = '\0';
707 11 : GNUNET_snprintf (arg_str,
708 : sizeof (arg_str),
709 : "purses/%s/create",
710 : pub_str);
711 : }
712 11 : pch->url = TALER_url_join (pch->base_url,
713 : arg_str,
714 : NULL);
715 11 : if (NULL == pch->url)
716 : {
717 0 : GNUNET_break (0);
718 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
719 : }
720 11 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
721 : "URL for purse create with deposit: `%s'\n",
722 : pch->url);
723 11 : eh = TALER_EXCHANGE_curl_easy_get_ (pch->url);
724 22 : if ( (NULL == eh) ||
725 : (GNUNET_OK !=
726 11 : TALER_curl_easy_post (&pch->post_ctx,
727 : eh,
728 : body)) )
729 : {
730 0 : GNUNET_break (0);
731 0 : if (NULL != eh)
732 0 : curl_easy_cleanup (eh);
733 0 : json_decref (body);
734 0 : return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
735 : }
736 11 : json_decref (body);
737 22 : pch->job = GNUNET_CURL_job_add2 (pch->ctx,
738 : eh,
739 11 : pch->post_ctx.headers,
740 : &handle_purse_create_deposit_finished,
741 : pch);
742 11 : if (NULL == pch->job)
743 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
744 11 : return TALER_EC_NONE;
745 : }
746 :
747 :
748 : void
749 11 : TALER_EXCHANGE_post_purses_create_cancel (
750 : struct TALER_EXCHANGE_PostPursesCreateHandle *pch)
751 : {
752 11 : if (NULL != pch->job)
753 : {
754 0 : GNUNET_CURL_job_cancel (pch->job);
755 0 : pch->job = NULL;
756 : }
757 11 : json_decref (pch->contract_terms);
758 11 : GNUNET_free (pch->econtract.econtract);
759 11 : GNUNET_free (pch->base_url);
760 11 : GNUNET_free (pch->url);
761 :
762 22 : for (unsigned int i = 0; i<pch->num_deposits; i++)
763 : {
764 11 : struct Deposit *d = &pch->deposits[i];
765 :
766 11 : TALER_denom_sig_free (&d->denom_sig);
767 : }
768 11 : GNUNET_array_grow (pch->deposits,
769 : pch->num_deposits,
770 : 0);
771 11 : TALER_EXCHANGE_keys_decref (pch->keys);
772 11 : TALER_curl_easy_post_finished (&pch->post_ctx);
773 11 : GNUNET_free (pch);
774 11 : }
775 :
776 :
777 : /* end of exchange_api_post-purses-PURSE_PUB-create.c */
|