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