exchange_api_get-deposits-H_WIRE-MERCHANT_PUB-H_CONTRACT_TERMS-COIN_PUB.c (14636B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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_get-deposits-H_WIRE-MERCHANT_PUB-H_CONTRACT_TERMS-COIN_PUB.c 19 * @brief Implementation of the GET /deposits/... request 20 * @author Christian Grothoff 21 */ 22 #include <jansson.h> 23 #include <microhttpd.h> /* just for HTTP status codes */ 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_json_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "taler/taler_json_lib.h" 28 #include "exchange_api_handle.h" 29 #include "taler/taler_signatures.h" 30 #include "exchange_api_curl_defaults.h" 31 32 33 /** 34 * @brief A GET /deposits/... Handle 35 */ 36 struct TALER_EXCHANGE_GetDepositsHandle 37 { 38 39 /** 40 * Base URL of the exchange. 41 */ 42 char *base_url; 43 44 /** 45 * The url for this request. 46 */ 47 char *url; 48 49 /** 50 * The keys of this request handle will use. 51 */ 52 struct TALER_EXCHANGE_Keys *keys; 53 54 /** 55 * Handle for the request. 56 */ 57 struct GNUNET_CURL_Job *job; 58 59 /** 60 * Function to call with the result. 61 */ 62 TALER_EXCHANGE_GetDepositsCallback cb; 63 64 /** 65 * Closure for @e cb. 66 */ 67 TALER_EXCHANGE_GET_DEPOSITS_RESULT_CLOSURE *cb_cls; 68 69 /** 70 * CURL context to use. 71 */ 72 struct GNUNET_CURL_Context *ctx; 73 74 /** 75 * Private key of the merchant. 76 */ 77 struct TALER_MerchantPrivateKeyP merchant_priv; 78 79 /** 80 * Hash over the wiring information of the merchant. 81 */ 82 struct TALER_MerchantWireHashP h_wire; 83 84 /** 85 * Hash over the contract for which this deposit is made. 86 */ 87 struct TALER_PrivateContractHashP h_contract_terms; 88 89 /** 90 * The coin's public key. 91 */ 92 struct TALER_CoinSpendPublicKeyP coin_pub; 93 94 /** 95 * Options set for this request. 96 */ 97 struct 98 { 99 /** 100 * How long to wait for the wire transfer, enabling long polling. 101 * Default: zero (no long polling). 102 */ 103 struct GNUNET_TIME_Relative timeout; 104 105 /** 106 * Long-poll target type. Defaults to #TALER_DGLPT_NONE (0). 107 */ 108 enum TALER_DepositGetLongPollTarget lpt; 109 } options; 110 111 }; 112 113 114 /** 115 * Function called when we're done processing the 116 * HTTP GET /deposits/... request. 117 * 118 * @param cls the `struct TALER_EXCHANGE_GetDepositsHandle` 119 * @param response_code HTTP response code, 0 on error 120 * @param response parsed JSON result, NULL on error 121 */ 122 static void 123 handle_deposit_wtid_finished (void *cls, 124 long response_code, 125 const void *response) 126 { 127 struct TALER_EXCHANGE_GetDepositsHandle *gdh = cls; 128 const json_t *j = response; 129 struct TALER_EXCHANGE_GetDepositsResponse dr = { 130 .hr.reply = j, 131 .hr.http_status = (unsigned int) response_code 132 }; 133 134 gdh->job = NULL; 135 switch (response_code) 136 { 137 case 0: 138 dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 139 break; 140 case MHD_HTTP_OK: 141 { 142 struct GNUNET_JSON_Specification spec[] = { 143 GNUNET_JSON_spec_fixed_auto ( 144 "wtid", 145 &dr.details.ok.wtid), 146 GNUNET_JSON_spec_timestamp ( 147 "execution_time", 148 &dr.details.ok.execution_time), 149 TALER_JSON_spec_amount_any ( 150 "coin_contribution", 151 &dr.details.ok.coin_contribution), 152 GNUNET_JSON_spec_fixed_auto ( 153 "exchange_sig", 154 &dr.details.ok.exchange_sig), 155 GNUNET_JSON_spec_fixed_auto ( 156 "exchange_pub", 157 &dr.details.ok.exchange_pub), 158 GNUNET_JSON_spec_end () 159 }; 160 161 GNUNET_assert (NULL != gdh->keys); 162 if (GNUNET_OK != 163 GNUNET_JSON_parse (j, 164 spec, 165 NULL, NULL)) 166 { 167 GNUNET_break_op (0); 168 dr.hr.http_status = 0; 169 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 170 break; 171 } 172 if (GNUNET_OK != 173 TALER_EXCHANGE_test_signing_key (gdh->keys, 174 &dr.details.ok.exchange_pub)) 175 { 176 GNUNET_break_op (0); 177 dr.hr.http_status = 0; 178 dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE; 179 break; 180 } 181 if (GNUNET_OK != 182 TALER_exchange_online_confirm_wire_verify ( 183 &gdh->h_wire, 184 &gdh->h_contract_terms, 185 &dr.details.ok.wtid, 186 &gdh->coin_pub, 187 dr.details.ok.execution_time, 188 &dr.details.ok.coin_contribution, 189 &dr.details.ok.exchange_pub, 190 &dr.details.ok.exchange_sig)) 191 { 192 GNUNET_break_op (0); 193 dr.hr.http_status = 0; 194 dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE; 195 break; 196 } 197 gdh->cb (gdh->cb_cls, 198 &dr); 199 gdh->cb = NULL; 200 TALER_EXCHANGE_get_deposits_cancel (gdh); 201 return; 202 } 203 case MHD_HTTP_ACCEPTED: 204 { 205 /* Transaction known, but not executed yet */ 206 bool no_legi = false; 207 struct GNUNET_JSON_Specification spec[] = { 208 GNUNET_JSON_spec_timestamp ( 209 "execution_time", 210 &dr.details.accepted.execution_time), 211 GNUNET_JSON_spec_mark_optional ( 212 GNUNET_JSON_spec_fixed_auto ( 213 "account_pub", 214 &dr.details.accepted.account_pub), 215 NULL), 216 GNUNET_JSON_spec_mark_optional ( 217 GNUNET_JSON_spec_uint64 ( 218 "requirement_row", 219 &dr.details.accepted.requirement_row), 220 &no_legi), 221 GNUNET_JSON_spec_bool ( 222 "kyc_ok", 223 &dr.details.accepted.kyc_ok), 224 GNUNET_JSON_spec_end () 225 }; 226 227 if (GNUNET_OK != 228 GNUNET_JSON_parse (j, 229 spec, 230 NULL, NULL)) 231 { 232 GNUNET_break_op (0); 233 dr.hr.http_status = 0; 234 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 235 break; 236 } 237 if (no_legi) 238 dr.details.accepted.requirement_row = 0; 239 gdh->cb (gdh->cb_cls, 240 &dr); 241 gdh->cb = NULL; 242 TALER_EXCHANGE_get_deposits_cancel (gdh); 243 return; 244 } 245 case MHD_HTTP_BAD_REQUEST: 246 dr.hr.ec = TALER_JSON_get_error_code (j); 247 dr.hr.hint = TALER_JSON_get_error_hint (j); 248 /* This should never happen, either us or the exchange is buggy 249 (or API version conflict); just pass JSON reply to the application */ 250 break; 251 case MHD_HTTP_FORBIDDEN: 252 dr.hr.ec = TALER_JSON_get_error_code (j); 253 dr.hr.hint = TALER_JSON_get_error_hint (j); 254 /* Nothing really to verify, exchange says one of the signatures is 255 invalid; as we checked them, this should never happen, we 256 should pass the JSON reply to the application */ 257 break; 258 case MHD_HTTP_NOT_FOUND: 259 dr.hr.ec = TALER_JSON_get_error_code (j); 260 dr.hr.hint = TALER_JSON_get_error_hint (j); 261 /* Exchange does not know about transaction; 262 we should pass the reply to the application */ 263 break; 264 case MHD_HTTP_INTERNAL_SERVER_ERROR: 265 dr.hr.ec = TALER_JSON_get_error_code (j); 266 dr.hr.hint = TALER_JSON_get_error_hint (j); 267 /* Server had an internal issue; we should retry, but this API 268 leaves this to the application */ 269 break; 270 default: 271 /* unexpected response code */ 272 dr.hr.ec = TALER_JSON_get_error_code (j); 273 dr.hr.hint = TALER_JSON_get_error_hint (j); 274 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 275 "Unexpected response code %u/%d for exchange GET deposits\n", 276 (unsigned int) response_code, 277 (int) dr.hr.ec); 278 GNUNET_break_op (0); 279 break; 280 } 281 if (NULL != gdh->cb) 282 gdh->cb (gdh->cb_cls, 283 &dr); 284 TALER_EXCHANGE_get_deposits_cancel (gdh); 285 } 286 287 288 struct TALER_EXCHANGE_GetDepositsHandle * 289 TALER_EXCHANGE_get_deposits_create ( 290 struct GNUNET_CURL_Context *ctx, 291 const char *url, 292 struct TALER_EXCHANGE_Keys *keys, 293 const struct TALER_MerchantPrivateKeyP *merchant_priv, 294 const struct TALER_MerchantWireHashP *h_wire, 295 const struct TALER_PrivateContractHashP *h_contract_terms, 296 const struct TALER_CoinSpendPublicKeyP *coin_pub) 297 { 298 struct TALER_EXCHANGE_GetDepositsHandle *gdh; 299 struct TALER_MerchantPublicKeyP merchant; 300 301 gdh = GNUNET_new (struct TALER_EXCHANGE_GetDepositsHandle); 302 gdh->ctx = ctx; 303 gdh->base_url = GNUNET_strdup (url); 304 gdh->keys = TALER_EXCHANGE_keys_incref (keys); 305 gdh->merchant_priv = *merchant_priv; 306 gdh->h_wire = *h_wire; 307 gdh->h_contract_terms = *h_contract_terms; 308 GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, 309 &merchant.eddsa_pub); 310 gdh->coin_pub = *coin_pub; 311 return gdh; 312 } 313 314 315 enum GNUNET_GenericReturnValue 316 TALER_EXCHANGE_get_deposits_set_options_ ( 317 struct TALER_EXCHANGE_GetDepositsHandle *gdh, 318 unsigned int num_options, 319 const struct TALER_EXCHANGE_GetDepositsOptionValue *options) 320 { 321 for (unsigned int i = 0; i < num_options; i++) 322 { 323 const struct TALER_EXCHANGE_GetDepositsOptionValue *opt = &options[i]; 324 325 switch (opt->option) 326 { 327 case TALER_EXCHANGE_GET_DEPOSITS_OPTION_END: 328 return GNUNET_OK; 329 case TALER_EXCHANGE_GET_DEPOSITS_OPTION_TIMEOUT: 330 gdh->options.timeout = opt->details.timeout; 331 break; 332 case TALER_EXCHANGE_GET_DEPOSITS_OPTION_LONG_POLL_TARGET: 333 gdh->options.lpt = opt->details.lpt; 334 break; 335 } 336 } 337 return GNUNET_OK; 338 } 339 340 341 enum TALER_ErrorCode 342 TALER_EXCHANGE_get_deposits_start ( 343 struct TALER_EXCHANGE_GetDepositsHandle *gdh, 344 TALER_EXCHANGE_GetDepositsCallback cb, 345 TALER_EXCHANGE_GET_DEPOSITS_RESULT_CLOSURE *cb_cls) 346 { 347 struct TALER_MerchantPublicKeyP merchant; 348 struct TALER_MerchantSignatureP merchant_sig; 349 unsigned int tms; 350 CURL *eh; 351 352 if (NULL != gdh->job) 353 { 354 GNUNET_break (0); 355 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 356 } 357 gdh->cb = cb; 358 gdh->cb_cls = cb_cls; 359 tms = (unsigned int) gdh->options.timeout.rel_value_us 360 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; 361 GNUNET_CRYPTO_eddsa_key_get_public (&gdh->merchant_priv.eddsa_priv, 362 &merchant.eddsa_pub); 363 TALER_merchant_deposit_sign (&gdh->h_contract_terms, 364 &gdh->h_wire, 365 &gdh->coin_pub, 366 &gdh->merchant_priv, 367 &merchant_sig); 368 { 369 char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; 370 char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2]; 371 char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2]; 372 char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2]; 373 char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2]; 374 char arg_str[sizeof (whash_str) 375 + sizeof (mpub_str) 376 + sizeof (chash_str) 377 + sizeof (cpub_str) + 48]; 378 char timeout_str[24]; 379 char lpt_str[12]; 380 char *end; 381 382 end = GNUNET_STRINGS_data_to_string (&gdh->h_wire, 383 sizeof (gdh->h_wire), 384 whash_str, 385 sizeof (whash_str)); 386 *end = '\0'; 387 end = GNUNET_STRINGS_data_to_string (&merchant, 388 sizeof (merchant), 389 mpub_str, 390 sizeof (mpub_str)); 391 *end = '\0'; 392 end = GNUNET_STRINGS_data_to_string (&gdh->h_contract_terms, 393 sizeof (gdh->h_contract_terms), 394 chash_str, 395 sizeof (chash_str)); 396 *end = '\0'; 397 end = GNUNET_STRINGS_data_to_string (&gdh->coin_pub, 398 sizeof (gdh->coin_pub), 399 cpub_str, 400 sizeof (cpub_str)); 401 *end = '\0'; 402 end = GNUNET_STRINGS_data_to_string (&merchant_sig, 403 sizeof (merchant_sig), 404 msig_str, 405 sizeof (msig_str)); 406 *end = '\0'; 407 if (0 == tms) 408 { 409 timeout_str[0] = '\0'; 410 } 411 else 412 { 413 GNUNET_snprintf ( 414 timeout_str, 415 sizeof (timeout_str), 416 "%u", 417 tms); 418 } 419 if (TALER_DGLPT_OK == gdh->options.lpt) 420 { 421 lpt_str[0] = '\0'; 422 } 423 else 424 { 425 GNUNET_snprintf ( 426 lpt_str, 427 sizeof (lpt_str), 428 "%u", 429 (unsigned int) gdh->options.lpt); 430 } 431 432 GNUNET_snprintf (arg_str, 433 sizeof (arg_str), 434 "deposits/%s/%s/%s/%s", 435 whash_str, 436 mpub_str, 437 chash_str, 438 cpub_str); 439 gdh->url = TALER_url_join ( 440 gdh->base_url, 441 arg_str, 442 "merchant_sig", msig_str, 443 "timeout_ms", (0 == tms) ? NULL : timeout_str, 444 "lpt", (TALER_DGLPT_NONE == gdh->options.lpt) ? NULL : lpt_str, 445 NULL); 446 } 447 if (NULL == gdh->url) 448 { 449 GNUNET_break (0); 450 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 451 } 452 eh = TALER_EXCHANGE_curl_easy_get_ (gdh->url); 453 if (NULL == eh) 454 { 455 GNUNET_break (0); 456 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 457 } 458 if (0 != tms) 459 { 460 GNUNET_break (CURLE_OK == 461 curl_easy_setopt (eh, 462 CURLOPT_TIMEOUT_MS, 463 (long) (tms + 100L))); 464 } 465 gdh->job = GNUNET_CURL_job_add (gdh->ctx, 466 eh, 467 &handle_deposit_wtid_finished, 468 gdh); 469 if (NULL == gdh->job) 470 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 471 return TALER_EC_NONE; 472 } 473 474 475 void 476 TALER_EXCHANGE_get_deposits_cancel ( 477 struct TALER_EXCHANGE_GetDepositsHandle *gdh) 478 { 479 if (NULL != gdh->job) 480 { 481 GNUNET_CURL_job_cancel (gdh->job); 482 gdh->job = NULL; 483 } 484 GNUNET_free (gdh->url); 485 GNUNET_free (gdh->base_url); 486 TALER_EXCHANGE_keys_decref (gdh->keys); 487 GNUNET_free (gdh); 488 } 489 490 491 /* end of exchange_api_get-deposits-H_WIRE-MERCHANT_PUB-H_CONTRACT_TERMS-COIN_PUB.c */