exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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           &regex),
    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 */