exchange_api_common.c (22446B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-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_common.c 19 * @brief common functions for the exchange API 20 * @author Christian Grothoff 21 */ 22 #include "taler/taler_json_lib.h" 23 #include <gnunet/gnunet_curl_lib.h> 24 #include "exchange_api_common.h" 25 #include "exchange_api_handle.h" 26 #include "taler/taler_signatures.h" 27 28 29 const struct TALER_EXCHANGE_SigningPublicKey * 30 TALER_EXCHANGE_get_signing_key_info ( 31 const struct TALER_EXCHANGE_Keys *keys, 32 const struct TALER_ExchangePublicKeyP *exchange_pub) 33 { 34 for (unsigned int i = 0; i<keys->num_sign_keys; i++) 35 { 36 const struct TALER_EXCHANGE_SigningPublicKey *spk 37 = &keys->sign_keys[i]; 38 39 if (0 == GNUNET_memcmp (exchange_pub, 40 &spk->key)) 41 return spk; 42 } 43 return NULL; 44 } 45 46 47 enum GNUNET_GenericReturnValue 48 TALER_EXCHANGE_check_purse_create_conflict_ ( 49 const struct TALER_PurseContractSignatureP *cpurse_sig, 50 const struct TALER_PurseContractPublicKeyP *purse_pub, 51 const json_t *proof) 52 { 53 struct TALER_Amount amount; 54 uint32_t min_age; 55 struct GNUNET_TIME_Timestamp purse_expiration; 56 struct TALER_PurseContractSignatureP purse_sig; 57 struct TALER_PrivateContractHashP h_contract_terms; 58 struct TALER_PurseMergePublicKeyP merge_pub; 59 struct GNUNET_JSON_Specification spec[] = { 60 TALER_JSON_spec_amount_any ("amount", 61 &amount), 62 GNUNET_JSON_spec_uint32 ("min_age", 63 &min_age), 64 GNUNET_JSON_spec_timestamp ("purse_expiration", 65 &purse_expiration), 66 GNUNET_JSON_spec_fixed_auto ("purse_sig", 67 &purse_sig), 68 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 69 &h_contract_terms), 70 GNUNET_JSON_spec_fixed_auto ("merge_pub", 71 &merge_pub), 72 GNUNET_JSON_spec_end () 73 }; 74 75 if (GNUNET_OK != 76 GNUNET_JSON_parse (proof, 77 spec, 78 NULL, NULL)) 79 { 80 GNUNET_break_op (0); 81 return GNUNET_SYSERR; 82 } 83 if (GNUNET_OK != 84 TALER_wallet_purse_create_verify (purse_expiration, 85 &h_contract_terms, 86 &merge_pub, 87 min_age, 88 &amount, 89 purse_pub, 90 &purse_sig)) 91 { 92 GNUNET_break_op (0); 93 return GNUNET_SYSERR; 94 } 95 if (0 == 96 GNUNET_memcmp (&purse_sig, 97 cpurse_sig)) 98 { 99 /* Must be the SAME data, not a conflict! */ 100 GNUNET_break_op (0); 101 return GNUNET_SYSERR; 102 } 103 return GNUNET_OK; 104 } 105 106 107 enum GNUNET_GenericReturnValue 108 TALER_EXCHANGE_check_purse_merge_conflict_ ( 109 const struct TALER_PurseMergeSignatureP *cmerge_sig, 110 const struct TALER_PurseMergePublicKeyP *merge_pub, 111 const struct TALER_PurseContractPublicKeyP *purse_pub, 112 const char *exchange_url, 113 const json_t *proof) 114 { 115 struct TALER_PurseMergeSignatureP merge_sig; 116 struct GNUNET_TIME_Timestamp merge_timestamp; 117 const char *partner_url = NULL; 118 struct TALER_ReservePublicKeyP reserve_pub; 119 struct GNUNET_JSON_Specification spec[] = { 120 GNUNET_JSON_spec_mark_optional ( 121 TALER_JSON_spec_web_url ("partner_url", 122 &partner_url), 123 NULL), 124 GNUNET_JSON_spec_timestamp ("merge_timestamp", 125 &merge_timestamp), 126 GNUNET_JSON_spec_fixed_auto ("merge_sig", 127 &merge_sig), 128 GNUNET_JSON_spec_fixed_auto ("reserve_pub", 129 &reserve_pub), 130 GNUNET_JSON_spec_end () 131 }; 132 struct TALER_NormalizedPayto payto_uri; 133 134 if (GNUNET_OK != 135 GNUNET_JSON_parse (proof, 136 spec, 137 NULL, NULL)) 138 { 139 GNUNET_break_op (0); 140 return GNUNET_SYSERR; 141 } 142 if (NULL == partner_url) 143 partner_url = exchange_url; 144 payto_uri = TALER_reserve_make_payto (partner_url, 145 &reserve_pub); 146 if (GNUNET_OK != 147 TALER_wallet_purse_merge_verify ( 148 payto_uri, 149 merge_timestamp, 150 purse_pub, 151 merge_pub, 152 &merge_sig)) 153 { 154 GNUNET_break_op (0); 155 GNUNET_free (payto_uri.normalized_payto); 156 return GNUNET_SYSERR; 157 } 158 GNUNET_free (payto_uri.normalized_payto); 159 if (0 == 160 GNUNET_memcmp (&merge_sig, 161 cmerge_sig)) 162 { 163 /* Must be the SAME data, not a conflict! */ 164 GNUNET_break_op (0); 165 return GNUNET_SYSERR; 166 } 167 return GNUNET_OK; 168 } 169 170 171 enum GNUNET_GenericReturnValue 172 TALER_EXCHANGE_check_purse_coin_conflict_ ( 173 const struct TALER_PurseContractPublicKeyP *purse_pub, 174 const char *exchange_url, 175 const json_t *proof, 176 struct TALER_DenominationHashP *h_denom_pub, 177 struct TALER_AgeCommitmentHashP *phac, 178 struct TALER_CoinSpendPublicKeyP *coin_pub, 179 struct TALER_CoinSpendSignatureP *coin_sig) 180 { 181 const char *partner_url = NULL; 182 struct TALER_Amount amount; 183 struct GNUNET_JSON_Specification spec[] = { 184 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 185 h_denom_pub), 186 GNUNET_JSON_spec_fixed_auto ("h_age_commitment", 187 phac), 188 GNUNET_JSON_spec_fixed_auto ("coin_sig", 189 coin_sig), 190 GNUNET_JSON_spec_fixed_auto ("coin_pub", 191 coin_pub), 192 GNUNET_JSON_spec_mark_optional ( 193 TALER_JSON_spec_web_url ("partner_url", 194 &partner_url), 195 NULL), 196 TALER_JSON_spec_amount_any ("amount", 197 &amount), 198 GNUNET_JSON_spec_end () 199 }; 200 201 if (GNUNET_OK != 202 GNUNET_JSON_parse (proof, 203 spec, 204 NULL, NULL)) 205 { 206 GNUNET_break_op (0); 207 return GNUNET_SYSERR; 208 } 209 if (NULL == partner_url) 210 partner_url = exchange_url; 211 if (GNUNET_OK != 212 TALER_wallet_purse_deposit_verify ( 213 partner_url, 214 purse_pub, 215 &amount, 216 h_denom_pub, 217 phac, 218 coin_pub, 219 coin_sig)) 220 { 221 GNUNET_break_op (0); 222 return GNUNET_SYSERR; 223 } 224 return GNUNET_OK; 225 } 226 227 228 enum GNUNET_GenericReturnValue 229 TALER_EXCHANGE_check_purse_econtract_conflict_ ( 230 const struct TALER_PurseContractSignatureP *ccontract_sig, 231 const struct TALER_PurseContractPublicKeyP *purse_pub, 232 const json_t *proof) 233 { 234 struct TALER_ContractDiffiePublicP contract_pub; 235 struct TALER_PurseContractSignatureP contract_sig; 236 struct GNUNET_HashCode h_econtract; 237 struct GNUNET_JSON_Specification spec[] = { 238 GNUNET_JSON_spec_fixed_auto ("h_econtract", 239 &h_econtract), 240 GNUNET_JSON_spec_fixed_auto ("econtract_sig", 241 &contract_sig), 242 GNUNET_JSON_spec_fixed_auto ("contract_pub", 243 &contract_pub), 244 GNUNET_JSON_spec_end () 245 }; 246 247 if (GNUNET_OK != 248 GNUNET_JSON_parse (proof, 249 spec, 250 NULL, NULL)) 251 { 252 GNUNET_break_op (0); 253 return GNUNET_SYSERR; 254 } 255 if (GNUNET_OK != 256 TALER_wallet_econtract_upload_verify2 ( 257 &h_econtract, 258 &contract_pub, 259 purse_pub, 260 &contract_sig)) 261 { 262 GNUNET_break_op (0); 263 return GNUNET_SYSERR; 264 } 265 if (0 == 266 GNUNET_memcmp (&contract_sig, 267 ccontract_sig)) 268 { 269 /* Must be the SAME data, not a conflict! */ 270 GNUNET_break_op (0); 271 return GNUNET_SYSERR; 272 } 273 return GNUNET_OK; 274 } 275 276 277 // FIXME: should be used... - #9422 278 enum GNUNET_GenericReturnValue 279 TALER_EXCHANGE_check_coin_denomination_conflict_ ( 280 const json_t *proof, 281 const struct TALER_DenominationHashP *ch_denom_pub) 282 { 283 struct TALER_DenominationHashP h_denom_pub; 284 struct GNUNET_JSON_Specification spec[] = { 285 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 286 &h_denom_pub), 287 GNUNET_JSON_spec_end () 288 }; 289 290 if (GNUNET_OK != 291 GNUNET_JSON_parse (proof, 292 spec, 293 NULL, NULL)) 294 { 295 GNUNET_break_op (0); 296 return GNUNET_SYSERR; 297 } 298 if (0 == 299 GNUNET_memcmp (ch_denom_pub, 300 &h_denom_pub)) 301 { 302 GNUNET_break_op (0); 303 return GNUNET_OK; 304 } 305 /* indeed, proof with different denomination key provided */ 306 return GNUNET_OK; 307 } 308 309 310 enum GNUNET_GenericReturnValue 311 TALER_EXCHANGE_get_min_denomination_ ( 312 const struct TALER_EXCHANGE_Keys *keys, 313 struct TALER_Amount *min) 314 { 315 bool have_min = false; 316 for (unsigned int i = 0; i<keys->num_denom_keys; i++) 317 { 318 const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i]; 319 320 if (! have_min) 321 { 322 *min = dk->value; 323 have_min = true; 324 continue; 325 } 326 if (1 != TALER_amount_cmp (min, 327 &dk->value)) 328 continue; 329 *min = dk->value; 330 } 331 if (! have_min) 332 { 333 GNUNET_break (0); 334 return GNUNET_SYSERR; 335 } 336 return GNUNET_OK; 337 } 338 339 340 enum GNUNET_GenericReturnValue 341 TALER_EXCHANGE_verify_deposit_signature_ ( 342 const struct TALER_EXCHANGE_DepositContractDetail *dcd, 343 const struct TALER_ExtensionPolicyHashP *ech, 344 const struct TALER_MerchantWireHashP *h_wire, 345 const struct TALER_EXCHANGE_CoinDepositDetail *cdd, 346 const struct TALER_EXCHANGE_DenomPublicKey *dki) 347 { 348 if (GNUNET_OK != 349 TALER_wallet_deposit_verify (&cdd->amount, 350 &dki->fees.deposit, 351 h_wire, 352 &dcd->h_contract_terms, 353 &dcd->wallet_data_hash, 354 &cdd->h_age_commitment, 355 ech, 356 &cdd->h_denom_pub, 357 dcd->wallet_timestamp, 358 &dcd->merchant_pub, 359 dcd->refund_deadline, 360 &cdd->coin_pub, 361 &cdd->coin_sig)) 362 { 363 GNUNET_break_op (0); 364 TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n"); 365 TALER_LOG_DEBUG ("... amount_with_fee was %s\n", 366 TALER_amount2s (&cdd->amount)); 367 TALER_LOG_DEBUG ("... deposit_fee was %s\n", 368 TALER_amount2s (&dki->fees.deposit)); 369 return GNUNET_SYSERR; 370 } 371 372 /* check coin signature */ 373 { 374 struct TALER_CoinPublicInfo coin_info = { 375 .coin_pub = cdd->coin_pub, 376 .denom_pub_hash = cdd->h_denom_pub, 377 .denom_sig = cdd->denom_sig, 378 .h_age_commitment = cdd->h_age_commitment, 379 }; 380 381 if (GNUNET_YES != 382 TALER_test_coin_valid (&coin_info, 383 &dki->key)) 384 { 385 GNUNET_break_op (0); 386 TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); 387 return GNUNET_SYSERR; 388 } 389 } 390 391 /* Check coin does make a contribution */ 392 if (0 < TALER_amount_cmp (&dki->fees.deposit, 393 &cdd->amount)) 394 { 395 GNUNET_break_op (0); 396 TALER_LOG_WARNING ("Deposit amount smaller than fee\n"); 397 return GNUNET_SYSERR; 398 } 399 return GNUNET_OK; 400 } 401 402 403 /** 404 * Parse account restriction in @a jrest into @a rest. 405 * 406 * @param jresta array of account restrictions in JSON 407 * @param[out] resta_len set to length of @a resta 408 * @param[out] resta account restriction array to set 409 * @return #GNUNET_OK on success 410 */ 411 static enum GNUNET_GenericReturnValue 412 parse_restrictions (const json_t *jresta, 413 unsigned int *resta_len, 414 struct TALER_EXCHANGE_AccountRestriction **resta) 415 { 416 size_t alen; 417 418 if (! json_is_array (jresta)) 419 { 420 GNUNET_break_op (0); 421 return GNUNET_SYSERR; 422 } 423 alen = json_array_size (jresta); 424 if (0 == alen) 425 { 426 /* no restrictions, perfectly OK */ 427 *resta = NULL; 428 return GNUNET_OK; 429 } 430 *resta_len = (unsigned int) alen; 431 GNUNET_assert (alen == *resta_len); 432 *resta = GNUNET_new_array (*resta_len, 433 struct TALER_EXCHANGE_AccountRestriction); 434 for (unsigned int i = 0; i<*resta_len; i++) 435 { 436 const json_t *jr = json_array_get (jresta, 437 i); 438 struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i]; 439 const char *type = json_string_value (json_object_get (jr, 440 "type")); 441 442 if (NULL == type) 443 { 444 GNUNET_break (0); 445 goto fail; 446 } 447 if (0 == strcmp (type, 448 "deny")) 449 { 450 ar->type = TALER_EXCHANGE_AR_DENY; 451 continue; 452 } 453 if (0 == strcmp (type, 454 "regex")) 455 { 456 const char *regex; 457 const char *hint; 458 struct GNUNET_JSON_Specification spec[] = { 459 GNUNET_JSON_spec_string ( 460 "payto_regex", 461 ®ex), 462 GNUNET_JSON_spec_string ( 463 "human_hint", 464 &hint), 465 GNUNET_JSON_spec_mark_optional ( 466 GNUNET_JSON_spec_json ( 467 "human_hint_i18n", 468 &ar->details.regex.human_hint_i18n), 469 NULL), 470 GNUNET_JSON_spec_end () 471 }; 472 473 if (GNUNET_OK != 474 GNUNET_JSON_parse (jr, 475 spec, 476 NULL, NULL)) 477 { 478 /* bogus reply */ 479 GNUNET_break_op (0); 480 goto fail; 481 } 482 ar->type = TALER_EXCHANGE_AR_REGEX; 483 ar->details.regex.posix_egrep = GNUNET_strdup (regex); 484 ar->details.regex.human_hint = GNUNET_strdup (hint); 485 continue; 486 } 487 /* unsupported type */ 488 GNUNET_break (0); 489 return GNUNET_SYSERR; 490 } 491 return GNUNET_OK; 492 fail: 493 GNUNET_free (*resta); 494 *resta_len = 0; 495 return GNUNET_SYSERR; 496 } 497 498 499 enum GNUNET_GenericReturnValue 500 TALER_EXCHANGE_parse_accounts ( 501 const struct TALER_MasterPublicKeyP *master_pub, 502 const json_t *accounts, 503 unsigned int was_length, 504 struct TALER_EXCHANGE_WireAccount was[static was_length]) 505 { 506 memset (was, 507 0, 508 sizeof (struct TALER_EXCHANGE_WireAccount) * was_length); 509 GNUNET_assert (was_length == 510 json_array_size (accounts)); 511 for (unsigned int i = 0; 512 i<was_length; 513 i++) 514 { 515 struct TALER_EXCHANGE_WireAccount *wa = &was[i]; 516 struct TALER_FullPayto payto_uri; 517 const char *conversion_url = NULL; 518 const char *open_banking_gateway = NULL; 519 const char *wire_transfer_gateway = NULL; 520 const char *bank_label = NULL; 521 int64_t priority = 0; 522 const json_t *credit_restrictions; 523 const json_t *debit_restrictions; 524 struct GNUNET_JSON_Specification spec_account[] = { 525 TALER_JSON_spec_full_payto_uri ("payto_uri", 526 &payto_uri), 527 GNUNET_JSON_spec_mark_optional ( 528 TALER_JSON_spec_web_url ("conversion_url", 529 &conversion_url), 530 NULL), 531 GNUNET_JSON_spec_mark_optional ( 532 GNUNET_JSON_spec_string ("open_banking_gateway", 533 &open_banking_gateway), 534 NULL), 535 GNUNET_JSON_spec_mark_optional ( 536 GNUNET_JSON_spec_string ("wire_transfer_gateway", 537 &wire_transfer_gateway), 538 NULL), 539 GNUNET_JSON_spec_mark_optional ( 540 GNUNET_JSON_spec_int64 ("priority", 541 &priority), 542 NULL), 543 GNUNET_JSON_spec_mark_optional ( 544 GNUNET_JSON_spec_string ("bank_label", 545 &bank_label), 546 NULL), 547 GNUNET_JSON_spec_array_const ("credit_restrictions", 548 &credit_restrictions), 549 GNUNET_JSON_spec_array_const ("debit_restrictions", 550 &debit_restrictions), 551 GNUNET_JSON_spec_fixed_auto ("master_sig", 552 &wa->master_sig), 553 GNUNET_JSON_spec_end () 554 }; 555 json_t *account; 556 557 account = json_array_get (accounts, 558 i); 559 if (GNUNET_OK != 560 GNUNET_JSON_parse (account, 561 spec_account, 562 NULL, NULL)) 563 { 564 /* bogus reply */ 565 GNUNET_break_op (0); 566 return GNUNET_SYSERR; 567 } 568 if ( (NULL != master_pub) && 569 (! ( ( (NULL == open_banking_gateway) && 570 (NULL == wire_transfer_gateway) && 571 (GNUNET_OK == 572 TALER_exchange_wire_signature_check32 ( 573 payto_uri, 574 conversion_url, 575 debit_restrictions, 576 credit_restrictions, 577 master_pub, 578 &wa->master_sig)) ) || 579 (GNUNET_OK == 580 TALER_exchange_wire_signature_check ( 581 payto_uri, 582 conversion_url, 583 open_banking_gateway, 584 wire_transfer_gateway, 585 debit_restrictions, 586 credit_restrictions, 587 master_pub, 588 &wa->master_sig)) ) ) ) 589 { 590 /* bogus reply */ 591 GNUNET_break_op (0); 592 return GNUNET_SYSERR; 593 } 594 if ( (GNUNET_OK != 595 parse_restrictions (credit_restrictions, 596 &wa->credit_restrictions_length, 597 &wa->credit_restrictions)) || 598 (GNUNET_OK != 599 parse_restrictions (debit_restrictions, 600 &wa->debit_restrictions_length, 601 &wa->debit_restrictions)) ) 602 { 603 /* bogus reply */ 604 GNUNET_break_op (0); 605 return GNUNET_SYSERR; 606 } 607 wa->fpayto_uri.full_payto 608 = GNUNET_strdup (payto_uri.full_payto); 609 wa->priority = priority; 610 if (NULL != conversion_url) 611 wa->conversion_url = GNUNET_strdup (conversion_url); 612 if (NULL != open_banking_gateway) 613 wa->open_banking_gateway = GNUNET_strdup (open_banking_gateway); 614 if (NULL != wire_transfer_gateway) 615 wa->wire_transfer_gateway = GNUNET_strdup (wire_transfer_gateway); 616 if (NULL != bank_label) 617 wa->bank_label = GNUNET_strdup (bank_label); 618 } /* end 'for all accounts */ 619 return GNUNET_OK; 620 } 621 622 623 /** 624 * Free array of account restrictions. 625 * 626 * @param ar_len length of @a ar 627 * @param[in] ar array to free contents of (but not @a ar itself) 628 */ 629 static void 630 free_restrictions (unsigned int ar_len, 631 struct TALER_EXCHANGE_AccountRestriction ar[static ar_len]) 632 { 633 for (unsigned int i = 0; i<ar_len; i++) 634 { 635 struct TALER_EXCHANGE_AccountRestriction *a = &ar[i]; 636 switch (a->type) 637 { 638 case TALER_EXCHANGE_AR_INVALID: 639 GNUNET_break (0); 640 break; 641 case TALER_EXCHANGE_AR_DENY: 642 break; 643 case TALER_EXCHANGE_AR_REGEX: 644 GNUNET_free (ar->details.regex.posix_egrep); 645 GNUNET_free (ar->details.regex.human_hint); 646 json_decref (ar->details.regex.human_hint_i18n); 647 break; 648 } 649 } 650 } 651 652 653 void 654 TALER_EXCHANGE_free_accounts ( 655 unsigned int was_len, 656 struct TALER_EXCHANGE_WireAccount was[static was_len]) 657 { 658 for (unsigned int i = 0; i<was_len; i++) 659 { 660 struct TALER_EXCHANGE_WireAccount *wa = &was[i]; 661 662 GNUNET_free (wa->fpayto_uri.full_payto); 663 GNUNET_free (wa->conversion_url); 664 GNUNET_free (wa->open_banking_gateway); 665 GNUNET_free (wa->wire_transfer_gateway); 666 GNUNET_free (wa->bank_label); 667 free_restrictions (wa->credit_restrictions_length, 668 wa->credit_restrictions); 669 GNUNET_array_grow (wa->credit_restrictions, 670 wa->credit_restrictions_length, 671 0); 672 free_restrictions (wa->debit_restrictions_length, 673 wa->debit_restrictions); 674 GNUNET_array_grow (wa->debit_restrictions, 675 wa->debit_restrictions_length, 676 0); 677 } 678 } 679 680 681 enum GNUNET_GenericReturnValue 682 TALER_EXCHANGE_keys_test_account_allowed ( 683 const struct TALER_EXCHANGE_Keys *keys, 684 bool check_credit, 685 const struct TALER_NormalizedPayto payto_uri) 686 { 687 /* For all accounts of the exchange */ 688 for (unsigned int i = 0; i<keys->accounts_len; i++) 689 { 690 const struct TALER_EXCHANGE_WireAccount *account 691 = &keys->accounts[i]; 692 693 /* KYC auth transfers are never supported with conversion */ 694 if (NULL != account->conversion_url) 695 continue; 696 /* filter by source account by credit_restrictions */ 697 if (GNUNET_YES != 698 TALER_EXCHANGE_test_account_allowed (account, 699 check_credit, 700 payto_uri)) 701 continue; 702 /* exchange account is allowed, add it */ 703 return true; 704 } 705 return false; 706 } 707 708 709 /** 710 * We received an #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS response code. 711 * Parse the JSON response and initialize the @a uflr object. 712 * 713 * @param[out] uflr data structure to initialize 714 * @param j JSON response to parse 715 * @return #GNUNET_OK on success 716 */ 717 enum GNUNET_GenericReturnValue 718 TALER_EXCHANGE_parse_451 (struct TALER_EXCHANGE_KycNeededRedirect *uflr, 719 const json_t *j) 720 { 721 struct GNUNET_JSON_Specification spec[] = { 722 GNUNET_JSON_spec_fixed_auto ( 723 "h_payto", 724 &uflr->h_payto), 725 GNUNET_JSON_spec_uint64 ( 726 "requirement_row", 727 &uflr->requirement_row), 728 GNUNET_JSON_spec_mark_optional ( 729 GNUNET_JSON_spec_fixed_auto ( 730 "account_pub", 731 &uflr->account_pub), 732 NULL), 733 GNUNET_JSON_spec_mark_optional ( 734 GNUNET_JSON_spec_bool ( 735 "bad_kyc_auth", 736 &uflr->bad_kyc_auth), 737 NULL), 738 GNUNET_JSON_spec_end () 739 }; 740 741 if (GNUNET_OK != 742 GNUNET_JSON_parse (j, 743 spec, 744 NULL, 745 NULL)) 746 { 747 GNUNET_break_op (0); 748 return GNUNET_SYSERR; 749 } 750 return GNUNET_OK; 751 } 752 753 754 /* end of exchange_api_common.c */