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