exchange_api_deposits_get.c (12790B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 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_deposits_get.c 19 * @brief Implementation of the /deposits/ GET 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 Deposit Get Handle 37 */ 38 struct TALER_EXCHANGE_DepositGetHandle 39 { 40 41 /** 42 * The keys of the this request handle will use 43 */ 44 struct TALER_EXCHANGE_Keys *keys; 45 46 /** 47 * The url for this request. 48 */ 49 char *url; 50 51 /** 52 * Context for #TEH_curl_easy_post(). Keeps the data that must 53 * persist for Curl to make the upload. 54 */ 55 struct TALER_CURL_PostContext ctx; 56 57 /** 58 * Handle for the request. 59 */ 60 struct GNUNET_CURL_Job *job; 61 62 /** 63 * Function to call with the result. 64 */ 65 TALER_EXCHANGE_DepositGetCallback cb; 66 67 /** 68 * Closure for @a cb. 69 */ 70 void *cb_cls; 71 72 /** 73 * Hash over the wiring information of the merchant. 74 */ 75 struct TALER_MerchantWireHashP h_wire; 76 77 /** 78 * Hash over the contract for which this deposit is made. 79 */ 80 struct TALER_PrivateContractHashP h_contract_terms; 81 82 /** 83 * The coin's public key. This is the value that must have been 84 * signed (blindly) by the Exchange. 85 */ 86 struct TALER_CoinSpendPublicKeyP coin_pub; 87 88 }; 89 90 91 /** 92 * Function called when we're done processing the 93 * HTTP /track/transaction request. 94 * 95 * @param cls the `struct TALER_EXCHANGE_DepositGetHandle` 96 * @param response_code HTTP response code, 0 on error 97 * @param response parsed JSON result, NULL on error 98 */ 99 static void 100 handle_deposit_wtid_finished (void *cls, 101 long response_code, 102 const void *response) 103 { 104 struct TALER_EXCHANGE_DepositGetHandle *dwh = cls; 105 const json_t *j = response; 106 struct TALER_EXCHANGE_GetDepositResponse dr = { 107 .hr.reply = j, 108 .hr.http_status = (unsigned int) response_code 109 }; 110 111 dwh->job = NULL; 112 switch (response_code) 113 { 114 case 0: 115 dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 116 break; 117 case MHD_HTTP_OK: 118 { 119 struct GNUNET_JSON_Specification spec[] = { 120 GNUNET_JSON_spec_fixed_auto ( 121 "wtid", 122 &dr.details.ok.wtid), 123 GNUNET_JSON_spec_timestamp ( 124 "execution_time", 125 &dr.details.ok.execution_time), 126 TALER_JSON_spec_amount_any ( 127 "coin_contribution", 128 &dr.details.ok.coin_contribution), 129 GNUNET_JSON_spec_fixed_auto ( 130 "exchange_sig", 131 &dr.details.ok.exchange_sig), 132 GNUNET_JSON_spec_fixed_auto ( 133 "exchange_pub", 134 &dr.details.ok.exchange_pub), 135 GNUNET_JSON_spec_end () 136 }; 137 const struct TALER_EXCHANGE_Keys *key_state; 138 139 key_state = dwh->keys; 140 GNUNET_assert (NULL != key_state); 141 if (GNUNET_OK != 142 GNUNET_JSON_parse (j, 143 spec, 144 NULL, NULL)) 145 { 146 GNUNET_break_op (0); 147 dr.hr.http_status = 0; 148 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 149 break; 150 } 151 if (GNUNET_OK != 152 TALER_EXCHANGE_test_signing_key (key_state, 153 &dr.details.ok.exchange_pub)) 154 { 155 GNUNET_break_op (0); 156 dr.hr.http_status = 0; 157 dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE; 158 break; 159 } 160 if (GNUNET_OK != 161 TALER_exchange_online_confirm_wire_verify ( 162 &dwh->h_wire, 163 &dwh->h_contract_terms, 164 &dr.details.ok.wtid, 165 &dwh->coin_pub, 166 dr.details.ok.execution_time, 167 &dr.details.ok.coin_contribution, 168 &dr.details.ok.exchange_pub, 169 &dr.details.ok.exchange_sig)) 170 { 171 GNUNET_break_op (0); 172 dr.hr.http_status = 0; 173 dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE; 174 break; 175 } 176 dwh->cb (dwh->cb_cls, 177 &dr); 178 TALER_EXCHANGE_deposits_get_cancel (dwh); 179 return; 180 } 181 case MHD_HTTP_ACCEPTED: 182 { 183 /* Transaction known, but not executed yet */ 184 bool no_legi = false; 185 struct GNUNET_JSON_Specification spec[] = { 186 GNUNET_JSON_spec_timestamp ( 187 "execution_time", 188 &dr.details.accepted.execution_time), 189 GNUNET_JSON_spec_mark_optional ( 190 GNUNET_JSON_spec_fixed_auto ( 191 "account_pub", 192 &dr.details.accepted.account_pub), 193 NULL), 194 GNUNET_JSON_spec_mark_optional ( 195 GNUNET_JSON_spec_uint64 ( 196 "requirement_row", 197 &dr.details.accepted.requirement_row), 198 &no_legi), 199 GNUNET_JSON_spec_bool ( 200 "kyc_ok", 201 &dr.details.accepted.kyc_ok), 202 GNUNET_JSON_spec_end () 203 }; 204 205 if (GNUNET_OK != 206 GNUNET_JSON_parse (j, 207 spec, 208 NULL, NULL)) 209 { 210 GNUNET_break_op (0); 211 dr.hr.http_status = 0; 212 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 213 break; 214 } 215 if (no_legi) 216 dr.details.accepted.requirement_row = 0; 217 dwh->cb (dwh->cb_cls, 218 &dr); 219 TALER_EXCHANGE_deposits_get_cancel (dwh); 220 return; 221 } 222 case MHD_HTTP_BAD_REQUEST: 223 dr.hr.ec = TALER_JSON_get_error_code (j); 224 dr.hr.hint = TALER_JSON_get_error_hint (j); 225 /* This should never happen, either us or the exchange is buggy 226 (or API version conflict); just pass JSON reply to the application */ 227 break; 228 case MHD_HTTP_FORBIDDEN: 229 dr.hr.ec = TALER_JSON_get_error_code (j); 230 dr.hr.hint = TALER_JSON_get_error_hint (j); 231 /* Nothing really to verify, exchange says one of the signatures is 232 invalid; as we checked them, this should never happen, we 233 should pass the JSON reply to the application */ 234 break; 235 case MHD_HTTP_NOT_FOUND: 236 dr.hr.ec = TALER_JSON_get_error_code (j); 237 dr.hr.hint = TALER_JSON_get_error_hint (j); 238 /* Exchange does not know about transaction; 239 we should pass the reply to the application */ 240 break; 241 case MHD_HTTP_INTERNAL_SERVER_ERROR: 242 dr.hr.ec = TALER_JSON_get_error_code (j); 243 dr.hr.hint = TALER_JSON_get_error_hint (j); 244 /* Server had an internal issue; we should retry, but this API 245 leaves this to the application */ 246 break; 247 default: 248 /* unexpected response code */ 249 dr.hr.ec = TALER_JSON_get_error_code (j); 250 dr.hr.hint = TALER_JSON_get_error_hint (j); 251 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 252 "Unexpected response code %u/%d for exchange GET deposits\n", 253 (unsigned int) response_code, 254 (int) dr.hr.ec); 255 GNUNET_break_op (0); 256 break; 257 } 258 dwh->cb (dwh->cb_cls, 259 &dr); 260 TALER_EXCHANGE_deposits_get_cancel (dwh); 261 } 262 263 264 struct TALER_EXCHANGE_DepositGetHandle * 265 TALER_EXCHANGE_deposits_get ( 266 struct GNUNET_CURL_Context *ctx, 267 const char *url, 268 struct TALER_EXCHANGE_Keys *keys, 269 const struct TALER_MerchantPrivateKeyP *merchant_priv, 270 const struct TALER_MerchantWireHashP *h_wire, 271 const struct TALER_PrivateContractHashP *h_contract_terms, 272 const struct TALER_CoinSpendPublicKeyP *coin_pub, 273 struct GNUNET_TIME_Relative timeout, 274 TALER_EXCHANGE_DepositGetCallback cb, 275 void *cb_cls) 276 { 277 struct TALER_MerchantPublicKeyP merchant; 278 struct TALER_MerchantSignatureP merchant_sig; 279 struct TALER_EXCHANGE_DepositGetHandle *dwh; 280 CURL *eh; 281 char arg_str[(sizeof (struct TALER_CoinSpendPublicKeyP) 282 + sizeof (struct TALER_MerchantWireHashP) 283 + sizeof (struct TALER_MerchantPublicKeyP) 284 + sizeof (struct TALER_PrivateContractHashP) 285 + sizeof (struct TALER_MerchantSignatureP)) * 2 + 48]; 286 unsigned int tms 287 = (unsigned int) timeout.rel_value_us 288 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; 289 290 GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, 291 &merchant.eddsa_pub); 292 TALER_merchant_deposit_sign (h_contract_terms, 293 h_wire, 294 coin_pub, 295 merchant_priv, 296 &merchant_sig); 297 { 298 char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2]; 299 char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2]; 300 char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2]; 301 char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2]; 302 char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2]; 303 char timeout_str[24]; 304 char *end; 305 306 end = GNUNET_STRINGS_data_to_string (h_wire, 307 sizeof (*h_wire), 308 whash_str, 309 sizeof (whash_str)); 310 *end = '\0'; 311 end = GNUNET_STRINGS_data_to_string (&merchant, 312 sizeof (merchant), 313 mpub_str, 314 sizeof (mpub_str)); 315 *end = '\0'; 316 end = GNUNET_STRINGS_data_to_string (h_contract_terms, 317 sizeof (*h_contract_terms), 318 chash_str, 319 sizeof (chash_str)); 320 *end = '\0'; 321 end = GNUNET_STRINGS_data_to_string (coin_pub, 322 sizeof (*coin_pub), 323 cpub_str, 324 sizeof (cpub_str)); 325 *end = '\0'; 326 end = GNUNET_STRINGS_data_to_string (&merchant_sig, 327 sizeof (merchant_sig), 328 msig_str, 329 sizeof (msig_str)); 330 *end = '\0'; 331 if (GNUNET_TIME_relative_is_zero (timeout)) 332 { 333 timeout_str[0] = '\0'; 334 } 335 else 336 { 337 GNUNET_snprintf ( 338 timeout_str, 339 sizeof (timeout_str), 340 "%u", 341 tms); 342 } 343 344 GNUNET_snprintf (arg_str, 345 sizeof (arg_str), 346 "deposits/%s/%s/%s/%s?merchant_sig=%s%s%s", 347 whash_str, 348 mpub_str, 349 chash_str, 350 cpub_str, 351 msig_str, 352 0 == tms 353 ? "" 354 : "&timeout_ms=", 355 timeout_str); 356 } 357 358 dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle); 359 dwh->cb = cb; 360 dwh->cb_cls = cb_cls; 361 dwh->url = TALER_url_join (url, 362 arg_str, 363 NULL); 364 if (NULL == dwh->url) 365 { 366 GNUNET_free (dwh); 367 return NULL; 368 } 369 dwh->h_wire = *h_wire; 370 dwh->h_contract_terms = *h_contract_terms; 371 dwh->coin_pub = *coin_pub; 372 eh = TALER_EXCHANGE_curl_easy_get_ (dwh->url); 373 if (NULL == eh) 374 { 375 GNUNET_break (0); 376 GNUNET_free (dwh->url); 377 GNUNET_free (dwh); 378 return NULL; 379 } 380 if (0 != tms) 381 { 382 GNUNET_break (CURLE_OK == 383 curl_easy_setopt (eh, 384 CURLOPT_TIMEOUT_MS, 385 (long) (tms + 100L))); 386 } 387 dwh->job = GNUNET_CURL_job_add (ctx, 388 eh, 389 &handle_deposit_wtid_finished, 390 dwh); 391 dwh->keys = TALER_EXCHANGE_keys_incref (keys); 392 return dwh; 393 } 394 395 396 void 397 TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh) 398 { 399 if (NULL != dwh->job) 400 { 401 GNUNET_CURL_job_cancel (dwh->job); 402 dwh->job = NULL; 403 } 404 GNUNET_free (dwh->url); 405 TALER_curl_easy_post_finished (&dwh->ctx); 406 TALER_EXCHANGE_keys_decref (dwh->keys); 407 GNUNET_free (dwh); 408 } 409 410 411 /* end of exchange_api_deposits_get.c */