diff options
Diffstat (limited to 'src/lib/exchange_api_common.c')
-rw-r--r-- | src/lib/exchange_api_common.c | 1418 |
1 files changed, 543 insertions, 875 deletions
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index da1400b9d..bd731ad37 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015-2020 Taler Systems SA + Copyright (C) 2015-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -22,381 +22,252 @@ #include "platform.h" #include "taler_json_lib.h" #include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_common.h" #include "exchange_api_handle.h" #include "taler_signatures.h" -/** - * Parse history given in JSON format and return it in binary - * format. - * - * @param exchange connection to the exchange we can use - * @param history JSON array with the history - * @param reserve_pub public key of the reserve to inspect - * @param currency currency we expect the balance to be in - * @param[out] balance final balance - * @param history_length number of entries in @a history - * @param[out] rhistory array of length @a history_length, set to the - * parsed history entries - * @return #GNUNET_OK if history was valid and @a rhistory and @a balance - * were set, - * #GNUNET_SYSERR if there was a protocol violation in @a history - */ -int -TALER_EXCHANGE_parse_reserve_history ( - struct TALER_EXCHANGE_Handle *exchange, - const json_t *history, - const struct TALER_ReservePublicKeyP *reserve_pub, - const char *currency, - struct TALER_Amount *balance, - unsigned int history_length, - struct TALER_EXCHANGE_ReserveHistory *rhistory) +const struct TALER_EXCHANGE_SigningPublicKey * +TALER_EXCHANGE_get_signing_key_info ( + const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_ExchangePublicKeyP *exchange_pub) { - struct GNUNET_HashCode uuid[history_length]; - unsigned int uuid_off; - struct TALER_Amount total_in; - struct TALER_Amount total_out; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - &total_in)); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - &total_out)); - uuid_off = 0; - for (unsigned int off = 0; off<history_length; off++) + for (unsigned int i = 0; i<keys->num_sign_keys; i++) { - struct TALER_EXCHANGE_ReserveHistory *rh = &rhistory[off]; - json_t *transaction; - struct TALER_Amount amount; - const char *type; - struct GNUNET_JSON_Specification hist_spec[] = { - GNUNET_JSON_spec_string ("type", - &type), - TALER_JSON_spec_amount_any ("amount", - &amount), - /* 'wire' and 'signature' are optional depending on 'type'! */ - GNUNET_JSON_spec_end () - }; + const struct TALER_EXCHANGE_SigningPublicKey *spk + = &keys->sign_keys[i]; - transaction = json_array_get (history, - off); - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - hist_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - rhistory[off].amount = amount; - if (GNUNET_YES != - TALER_amount_cmp_currency (&amount, - &total_in)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 == strcasecmp (type, - "CREDIT")) - { - const char *wire_url; - uint64_t wire_reference; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_JSON_Specification withdraw_spec[] = { - GNUNET_JSON_spec_uint64 ("wire_reference", - &wire_reference), - TALER_JSON_spec_absolute_time ("timestamp", - ×tamp), - GNUNET_JSON_spec_string ("sender_account_url", - &wire_url), - GNUNET_JSON_spec_end () - }; + if (0 == GNUNET_memcmp (exchange_pub, + &spk->key)) + return spk; + } + return NULL; +} - rh->type = TALER_EXCHANGE_RTT_CREDIT; - if (0 > - TALER_amount_add (&total_in, - &total_in, - &amount)) - { - /* overflow in history already!? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - withdraw_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - rh->details.in_details.sender_url = GNUNET_strdup (wire_url); - rh->details.in_details.wire_reference = wire_reference; - rh->details.in_details.timestamp = timestamp; - /* end type==DEPOSIT */ - } - else if (0 == strcasecmp (type, - "WITHDRAW")) - { - struct TALER_ReserveSignatureP sig; - struct TALER_WithdrawRequestPS withdraw_purpose; - struct TALER_Amount withdraw_fee; - struct GNUNET_JSON_Specification withdraw_spec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_sig", - &sig), - TALER_JSON_spec_amount_any ("withdraw_fee", - &withdraw_fee), - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &withdraw_purpose.h_denomination_pub), - GNUNET_JSON_spec_fixed_auto ("h_coin_envelope", - &withdraw_purpose.h_coin_envelope), - GNUNET_JSON_spec_end () - }; - rh->type = TALER_EXCHANGE_RTT_WITHDRAWAL; - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - withdraw_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - withdraw_purpose.purpose.size - = htonl (sizeof (withdraw_purpose)); - withdraw_purpose.purpose.purpose - = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); - withdraw_purpose.reserve_pub = *reserve_pub; - TALER_amount_hton (&withdraw_purpose.amount_with_fee, - &amount); - /* Check that the signature is a valid withdraw request */ - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, - &withdraw_purpose, - &sig.eddsa_signature, - &reserve_pub->eddsa_pub)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - /* check that withdraw fee matches expectations! */ - { - const struct TALER_EXCHANGE_Keys *key_state; - const struct TALER_EXCHANGE_DenomPublicKey *dki; - - key_state = TALER_EXCHANGE_get_keys (exchange); - dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state, - &withdraw_purpose. - h_denomination_pub); - if ( (GNUNET_YES != - TALER_amount_cmp_currency (&withdraw_fee, - &dki->fee_withdraw)) || - (0 != - TALER_amount_cmp (&withdraw_fee, - &dki->fee_withdraw)) ) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - rh->details.withdraw.fee = withdraw_fee; - } - rh->details.withdraw.out_authorization_sig - = json_object_get (transaction, - "signature"); - /* Check check that the same withdraw transaction - isn't listed twice by the exchange. We use the - "uuid" array to remember the hashes of all - purposes, and compare the hashes to find - duplicates. */// - GNUNET_CRYPTO_hash (&withdraw_purpose, - ntohl (withdraw_purpose.purpose.size), - &uuid[uuid_off]); - for (unsigned int i = 0; i<uuid_off; i++) - { - if (0 == GNUNET_memcmp (&uuid[uuid_off], - &uuid[i])) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - } - uuid_off++; +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_purse_create_conflict_ ( + const struct TALER_PurseContractSignatureP *cpurse_sig, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const json_t *proof) +{ + struct TALER_Amount amount; + uint32_t min_age; + struct GNUNET_TIME_Timestamp purse_expiration; + struct TALER_PurseContractSignatureP purse_sig; + struct TALER_PrivateContractHashP h_contract_terms; + struct TALER_PurseMergePublicKeyP merge_pub; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount_any ("amount", + &amount), + GNUNET_JSON_spec_uint32 ("min_age", + &min_age), + GNUNET_JSON_spec_timestamp ("purse_expiration", + &purse_expiration), + GNUNET_JSON_spec_fixed_auto ("purse_sig", + &purse_sig), + GNUNET_JSON_spec_fixed_auto ("h_contract_terms", + &h_contract_terms), + GNUNET_JSON_spec_fixed_auto ("merge_pub", + &merge_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (proof, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_wallet_purse_create_verify (purse_expiration, + &h_contract_terms, + &merge_pub, + min_age, + &amount, + purse_pub, + &purse_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == + GNUNET_memcmp (&purse_sig, + cpurse_sig)) + { + /* Must be the SAME data, not a conflict! */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} - if (0 > - TALER_amount_add (&total_out, - &total_out, - &amount)) - { - /* overflow in history already!? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - GNUNET_JSON_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - /* end type==WITHDRAW */ - } - else if (0 == strcasecmp (type, - "RECOUP")) - { - struct TALER_RecoupConfirmationPS pc; - struct GNUNET_TIME_Absolute timestamp; - const struct TALER_EXCHANGE_Keys *key_state; - struct GNUNET_JSON_Specification recoup_spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &pc.coin_pub), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &rh->details.recoup_details.exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &rh->details.recoup_details.exchange_pub), - TALER_JSON_spec_absolute_time_nbo ("timestamp", - &pc.timestamp), - GNUNET_JSON_spec_end () - }; - rh->type = TALER_EXCHANGE_RTT_RECOUP; - rh->amount = amount; - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - recoup_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - rh->details.recoup_details.coin_pub = pc.coin_pub; - TALER_amount_hton (&pc.recoup_amount, - &amount); - pc.purpose.size = htonl (sizeof (pc)); - pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP); - pc.reserve_pub = *reserve_pub; - timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp); - rh->details.recoup_details.timestamp = timestamp; - - key_state = TALER_EXCHANGE_get_keys (exchange); - if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (key_state, - &rh->details. - recoup_details.exchange_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP, - &pc, - &rh->details.recoup_details.exchange_sig.eddsa_signature, - &rh->details.recoup_details.exchange_pub.eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 > - TALER_amount_add (&total_in, - &total_in, - &rh->amount)) - { - /* overflow in history already!? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* end type==RECOUP */ - } - else if (0 == strcasecmp (type, - "CLOSING")) - { - const struct TALER_EXCHANGE_Keys *key_state; - struct TALER_ReserveCloseConfirmationPS rcc; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_JSON_Specification closing_spec[] = { - GNUNET_JSON_spec_string ( - "receiver_account_details", - &rh->details.close_details.receiver_account_details), - GNUNET_JSON_spec_fixed_auto ("wtid", - &rh->details.close_details.wtid), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &rh->details.close_details.exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &rh->details.close_details.exchange_pub), - TALER_JSON_spec_amount_any_nbo ("closing_fee", - &rcc.closing_fee), - TALER_JSON_spec_absolute_time_nbo ("timestamp", - &rcc.timestamp), - GNUNET_JSON_spec_end () - }; +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_purse_merge_conflict_ ( + const struct TALER_PurseMergeSignatureP *cmerge_sig, + const struct TALER_PurseMergePublicKeyP *merge_pub, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const char *exchange_url, + const json_t *proof) +{ + struct TALER_PurseMergeSignatureP merge_sig; + struct GNUNET_TIME_Timestamp merge_timestamp; + const char *partner_url = NULL; + struct TALER_ReservePublicKeyP reserve_pub; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("partner_url", + &partner_url), + NULL), + GNUNET_JSON_spec_timestamp ("merge_timestamp", + &merge_timestamp), + GNUNET_JSON_spec_fixed_auto ("merge_sig", + &merge_sig), + GNUNET_JSON_spec_fixed_auto ("reserve_pub", + &reserve_pub), + GNUNET_JSON_spec_end () + }; + char *payto_uri; + + if (GNUNET_OK != + GNUNET_JSON_parse (proof, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (NULL == partner_url) + partner_url = exchange_url; + payto_uri = TALER_reserve_make_payto (partner_url, + &reserve_pub); + if (GNUNET_OK != + TALER_wallet_purse_merge_verify ( + payto_uri, + merge_timestamp, + purse_pub, + merge_pub, + &merge_sig)) + { + GNUNET_break_op (0); + GNUNET_free (payto_uri); + return GNUNET_SYSERR; + } + GNUNET_free (payto_uri); + if (0 == + GNUNET_memcmp (&merge_sig, + cmerge_sig)) + { + /* Must be the SAME data, not a conflict! */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} - rh->type = TALER_EXCHANGE_RTT_CLOSE; - rh->amount = amount; - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - closing_spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - TALER_amount_hton (&rcc.closing_amount, - &amount); - GNUNET_CRYPTO_hash ( - rh->details.close_details.receiver_account_details, - strlen (rh->details.close_details.receiver_account_details) + 1, - &rcc.h_wire); - rcc.wtid = rh->details.close_details.wtid; - rcc.purpose.size = htonl (sizeof (rcc)); - rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); - rcc.reserve_pub = *reserve_pub; - timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp); - rh->details.close_details.timestamp = timestamp; - TALER_amount_ntoh (&rh->details.close_details.fee, - &rcc.closing_fee); - key_state = TALER_EXCHANGE_get_keys (exchange); - if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (key_state, - &rh->details.close_details. - exchange_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED, - &rcc, - &rh->details.close_details.exchange_sig.eddsa_signature, - &rh->details.close_details.exchange_pub.eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 > - TALER_amount_add (&total_out, - &total_out, - &rh->amount)) - { - /* overflow in history already!? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* end type==CLOSING */ - } - else - { - /* unexpected 'type', protocol incompatibility, complain! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_purse_coin_conflict_ ( + const struct TALER_PurseContractPublicKeyP *purse_pub, + const char *exchange_url, + const json_t *proof, + struct TALER_DenominationHashP *h_denom_pub, + struct TALER_AgeCommitmentHash *phac, + struct TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_CoinSpendSignatureP *coin_sig) +{ + const char *partner_url = NULL; + struct TALER_Amount amount; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_denom_pub", + h_denom_pub), + GNUNET_JSON_spec_fixed_auto ("h_age_commitment", + phac), + GNUNET_JSON_spec_fixed_auto ("coin_sig", + coin_sig), + GNUNET_JSON_spec_fixed_auto ("coin_pub", + coin_pub), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("partner_url", + &partner_url), + NULL), + TALER_JSON_spec_amount_any ("amount", + &amount), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (proof, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; } + if (NULL == partner_url) + partner_url = exchange_url; + if (GNUNET_OK != + TALER_wallet_purse_deposit_verify ( + partner_url, + purse_pub, + &amount, + h_denom_pub, + phac, + coin_pub, + coin_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} - /* check balance = total_in - total_out < withdraw-amount */ - if (0 > - TALER_amount_subtract (balance, - &total_in, - &total_out)) + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_purse_econtract_conflict_ ( + const struct TALER_PurseContractSignatureP *ccontract_sig, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const json_t *proof) +{ + struct TALER_ContractDiffiePublicP contract_pub; + struct TALER_PurseContractSignatureP contract_sig; + struct GNUNET_HashCode h_econtract; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_econtract", + &h_econtract), + GNUNET_JSON_spec_fixed_auto ("econtract_sig", + &contract_sig), + GNUNET_JSON_spec_fixed_auto ("contract_pub", + &contract_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (proof, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_wallet_econtract_upload_verify2 ( + &h_econtract, + &contract_pub, + purse_pub, + &contract_sig)) { - /* total_in < total_out, why did the exchange ever allow this!? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == + GNUNET_memcmp (&contract_sig, + ccontract_sig)) + { + /* Must be the SAME data, not a conflict! */ GNUNET_break_op (0); return GNUNET_SYSERR; } @@ -404,581 +275,378 @@ TALER_EXCHANGE_parse_reserve_history ( } -/** - * Free memory (potentially) allocated by #TALER_EXCHANGE_parse_reserve_history(). - * - * @param rhistory result to free - * @param len number of entries in @a rhistory - */ -void -TALER_EXCHANGE_free_reserve_history ( - struct TALER_EXCHANGE_ReserveHistory *rhistory, - unsigned int len) +// FIXME: should be used... +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_denomination_conflict_ ( + const json_t *proof, + const struct TALER_DenominationHashP *ch_denom_pub) { - for (unsigned int i = 0; i<len; i++) + struct TALER_DenominationHashP h_denom_pub; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_denom_pub", + &h_denom_pub), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (proof, + spec, + NULL, NULL)) { - switch (rhistory[i].type) - { - case TALER_EXCHANGE_RTT_CREDIT: - GNUNET_free (rhistory[i].details.in_details.sender_url); - break; - case TALER_EXCHANGE_RTT_WITHDRAWAL: - break; - case TALER_EXCHANGE_RTT_RECOUP: - break; - case TALER_EXCHANGE_RTT_CLOSE: - break; - } + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == + GNUNET_memcmp (ch_denom_pub, + &h_denom_pub)) + { + GNUNET_break_op (0); + return GNUNET_OK; } - GNUNET_free (rhistory); + /* indeed, proof with different denomination key provided */ + return GNUNET_OK; } -/** - * Verify a coins transaction history as returned by the exchange. - * - * @param dk fee structure for the coin, NULL to skip verifying fees - * @param currency expected currency for the coin - * @param coin_pub public key of the coin - * @param history history of the coin in json encoding - * @param[out] h_denom_pub set to the hash of the coin's denomination (if available) - * @param[out] total how much of the coin has been spent according to @a history - * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not - */ -int -TALER_EXCHANGE_verify_coin_history ( - const struct TALER_EXCHANGE_DenomPublicKey *dk, - const char *currency, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - json_t *history, - struct GNUNET_HashCode *h_denom_pub, - struct TALER_Amount *total) +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_min_denomination_ ( + const struct TALER_EXCHANGE_Keys *keys, + struct TALER_Amount *min) { - size_t len; - struct TALER_Amount rtotal; - struct TALER_Amount fee; + bool have_min = false; + for (unsigned int i = 0; i<keys->num_denom_keys; i++) + { + const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i]; - if (NULL == history) + if (! have_min) + { + *min = dk->value; + have_min = true; + continue; + } + if (1 != TALER_amount_cmp (min, + &dk->value)) + continue; + *min = dk->value; + } + if (! have_min) { - GNUNET_break_op (0); + GNUNET_break (0); return GNUNET_SYSERR; } - len = json_array_size (history); - if (0 == len) + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_verify_deposit_signature_ ( + const struct TALER_EXCHANGE_DepositContractDetail *dcd, + const struct TALER_ExtensionPolicyHashP *ech, + const struct TALER_MerchantWireHashP *h_wire, + const struct TALER_EXCHANGE_CoinDepositDetail *cdd, + const struct TALER_EXCHANGE_DenomPublicKey *dki) +{ + if (GNUNET_OK != + TALER_wallet_deposit_verify (&cdd->amount, + &dki->fees.deposit, + h_wire, + &dcd->h_contract_terms, + &dcd->wallet_data_hash, + &cdd->h_age_commitment, + ech, + &cdd->h_denom_pub, + dcd->wallet_timestamp, + &dcd->merchant_pub, + dcd->refund_deadline, + &cdd->coin_pub, + &cdd->coin_sig)) { GNUNET_break_op (0); + TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n"); + TALER_LOG_DEBUG ("... amount_with_fee was %s\n", + TALER_amount2s (&cdd->amount)); + TALER_LOG_DEBUG ("... deposit_fee was %s\n", + TALER_amount2s (&dki->fees.deposit)); return GNUNET_SYSERR; } - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - total)); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (currency, - &rtotal)); - for (size_t off = 0; off<len; off++) + + /* check coin signature */ { - int add; - json_t *transaction; - struct TALER_Amount amount; - const char *type; - struct GNUNET_JSON_Specification spec_glob[] = { - TALER_JSON_spec_amount_any ("amount", - &amount), - GNUNET_JSON_spec_string ("type", - &type), - GNUNET_JSON_spec_end () + struct TALER_CoinPublicInfo coin_info = { + .coin_pub = cdd->coin_pub, + .denom_pub_hash = cdd->h_denom_pub, + .denom_sig = cdd->denom_sig, + .h_age_commitment = cdd->h_age_commitment, }; - transaction = json_array_get (history, - off); - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec_glob, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } if (GNUNET_YES != - TALER_amount_cmp_currency (&amount, - &rtotal)) + TALER_test_coin_valid (&coin_info, + &dki->key)) { GNUNET_break_op (0); + TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); return GNUNET_SYSERR; } - add = GNUNET_SYSERR; - if (0 == strcasecmp (type, - "DEPOSIT")) - { - struct TALER_DepositRequestPS dr = { - .purpose.size = htonl (sizeof (dr)), - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT), - .coin_pub = *coin_pub - }; - struct TALER_CoinSpendSignatureP sig; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &dr.h_contract_terms), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &dr.h_wire), - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &dr.h_denom_pub), - TALER_JSON_spec_absolute_time_nbo ("timestamp", - &dr.wallet_timestamp), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_absolute_time_nbo ("refund_deadline", - &dr.refund_deadline)), - TALER_JSON_spec_amount_any_nbo ("deposit_fee", - &dr.deposit_fee), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &dr.merchant), - GNUNET_JSON_spec_end () - }; + } - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - TALER_amount_hton (&dr.amount_with_fee, - &amount); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT, - &dr, - &sig.eddsa_signature, - &coin_pub->eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - *h_denom_pub = dr.h_denom_pub; - if (NULL != dk) - { - /* check that deposit fee matches our expectations from /keys! */ - TALER_amount_ntoh (&fee, - &dr.deposit_fee); - if ( (GNUNET_YES != - TALER_amount_cmp_currency (&fee, - &dk->fee_deposit)) || - (0 != - TALER_amount_cmp (&fee, - &dk->fee_deposit)) ) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - add = GNUNET_YES; - } - else if (0 == strcasecmp (type, - "MELT")) - { - struct TALER_RefreshMeltCoinAffirmationPS rm; - struct TALER_CoinSpendSignatureP sig; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("rc", - &rm.rc), - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &rm.h_denom_pub), - TALER_JSON_spec_amount_any_nbo ("melt_fee", - &rm.melt_fee), - GNUNET_JSON_spec_end () - }; + /* Check coin does make a contribution */ + if (0 < TALER_amount_cmp (&dki->fees.deposit, + &cdd->amount)) + { + GNUNET_break_op (0); + TALER_LOG_WARNING ("Deposit amount smaller than fee\n"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - rm.purpose.size = htonl (sizeof (rm)); - rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); - TALER_amount_hton (&rm.amount_with_fee, - &amount); - rm.coin_pub = *coin_pub; - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, - &rm, - &sig.eddsa_signature, - &coin_pub->eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - *h_denom_pub = rm.h_denom_pub; - if (NULL != dk) - { - /* check that melt fee matches our expectations from /keys! */ - TALER_amount_ntoh (&fee, - &rm.melt_fee); - if ( (GNUNET_YES != - TALER_amount_cmp_currency (&fee, - &dk->fee_refresh)) || - (0 != - TALER_amount_cmp (&fee, - &dk->fee_refresh)) ) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - add = GNUNET_YES; - } - else if (0 == strcasecmp (type, - "REFUND")) - { - struct TALER_MerchantSignatureP sig; - struct TALER_Amount refund_fee; - struct TALER_Amount sig_amount; - struct TALER_RefundRequestPS rr = { - .purpose.size = htonl (sizeof (rr)), - .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND), - .coin_pub = *coin_pub - }; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("refund_fee", - &refund_fee), - GNUNET_JSON_spec_fixed_auto ("merchant_sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &rr.h_contract_terms), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &rr.merchant), - GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rr.rtransaction_id), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 > - TALER_amount_add (&sig_amount, - &refund_fee, - &amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - TALER_amount_hton (&rr.refund_amount, - &sig_amount); - rr.rtransaction_id = GNUNET_htonll (rr.rtransaction_id); - TALER_amount_hton (&rr.refund_amount, - &sig_amount); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND, - &rr, - &sig.eddsa_sig, - &rr.merchant.eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* NOTE: theoretically, we could also check that the given - merchant_pub and h_contract_terms appear in the - history under deposits. However, there is really no benefit - for the exchange to lie here, so not checking is probably OK - (an auditor ought to check, though). Then again, we similarly - had no reason to check the merchant's signature (other than a - well-formendess check). */// - - /* check that refund fee matches our expectations from /keys! */ - if (NULL != dk) - { - if ( (GNUNET_YES != - TALER_amount_cmp_currency (&refund_fee, - &dk->fee_refund)) || - (0 != - TALER_amount_cmp (&refund_fee, - &dk->fee_refund)) ) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - add = GNUNET_NO; - } - else if (0 == strcasecmp (type, - "RECOUP")) - { - struct TALER_RecoupConfirmationPS pc = { - .purpose.size = htonl (sizeof (pc)), - .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP), - .coin_pub = *coin_pub - }; - struct TALER_RecoupRequestPS rr = { - .purpose.size = htonl (sizeof (pc)), - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), - .coin_pub = *coin_pub - }; - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; - struct TALER_CoinSpendSignatureP coin_sig; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any_nbo ("amount", - &pc.recoup_amount), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), - GNUNET_JSON_spec_fixed_auto ("reserve_pub", - &pc.reserve_pub), - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin_sig), - GNUNET_JSON_spec_fixed_auto ("coin_blind", - &rr.coin_blind), - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &rr.h_denom_pub), - TALER_JSON_spec_absolute_time_nbo ("timestamp", - &pc.timestamp), - GNUNET_JSON_spec_end () - }; +/** + * Parse account restriction in @a jrest into @a rest. + * + * @param jresta array of account restrictions in JSON + * @param[out] resta_len set to length of @a resta + * @param[out] resta account restriction array to set + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_restrictions (const json_t *jresta, + unsigned int *resta_len, + struct TALER_EXCHANGE_AccountRestriction **resta) +{ + size_t alen; - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - TALER_amount_hton (&pc.recoup_amount, - &amount); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP, - &pc, - &exchange_sig.eddsa_signature, - &exchange_pub.eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP, - &rr, - &coin_sig.eddsa_signature, - &coin_pub->eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - *h_denom_pub = rr.h_denom_pub; - add = GNUNET_YES; + if (! json_is_array (jresta)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + alen = json_array_size (jresta); + if (0 == alen) + { + /* no restrictions, perfectly OK */ + *resta = NULL; + return GNUNET_OK; + } + *resta_len = (unsigned int) alen; + GNUNET_assert (alen == *resta_len); + *resta = GNUNET_new_array (*resta_len, + struct TALER_EXCHANGE_AccountRestriction); + for (unsigned int i = 0; i<*resta_len; i++) + { + const json_t *jr = json_array_get (jresta, + i); + struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i]; + const char *type = json_string_value (json_object_get (jr, + "type")); + + if (NULL == type) + { + GNUNET_break (0); + goto fail; } - else if (0 == strcasecmp (type, - "RECOUP-REFRESH")) + if (0 == strcmp (type, + "deny")) { - struct TALER_RecoupRefreshConfirmationPS pc = { - .purpose.size = htonl (sizeof (pc)), - .purpose.purpose = htonl ( - TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH), - .coin_pub = *coin_pub - }; - struct TALER_RecoupRequestPS rr = { - .purpose.size = htonl (sizeof (pc)), - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), - .coin_pub = *coin_pub - }; - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; - struct TALER_CoinSpendSignatureP coin_sig; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any_nbo ("amount", - &pc.recoup_amount), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin_sig), - GNUNET_JSON_spec_fixed_auto ("old_coin_pub", - &pc.old_coin_pub), - GNUNET_JSON_spec_fixed_auto ("coin_blind", - &rr.coin_blind), - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &rr.h_denom_pub), - TALER_JSON_spec_absolute_time_nbo ("timestamp", - &pc.timestamp), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - TALER_amount_hton (&pc.recoup_amount, - &amount); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH, - &pc, - &exchange_sig.eddsa_signature, - &exchange_pub.eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP, - &rr, - &coin_sig.eddsa_signature, - &coin_pub->eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - *h_denom_pub = rr.h_denom_pub; - add = GNUNET_YES; + ar->type = TALER_EXCHANGE_AR_DENY; + continue; } - else if (0 == strcasecmp (type, - "OLD-COIN-RECOUP")) + if (0 == strcmp (type, + "regex")) { - struct TALER_RecoupRefreshConfirmationPS pc = { - .purpose.size = htonl (sizeof (pc)), - .purpose.purpose = htonl ( - TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH), - .old_coin_pub = *coin_pub - }; - struct TALER_ExchangePublicKeyP exchange_pub; - struct TALER_ExchangeSignatureP exchange_sig; + const char *regex; + const char *hint; struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any_nbo ("amount", - &pc.recoup_amount), - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &exchange_pub), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &pc.coin_pub), - TALER_JSON_spec_absolute_time_nbo ("timestamp", - &pc.timestamp), + GNUNET_JSON_spec_string ( + "payto_regex", + ®ex), + GNUNET_JSON_spec_string ( + "human_hint", + &hint), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ( + "human_hint_i18n", + &ar->details.regex.human_hint_i18n), + NULL), GNUNET_JSON_spec_end () }; if (GNUNET_OK != - GNUNET_JSON_parse (transaction, + GNUNET_JSON_parse (jr, spec, NULL, NULL)) { + /* bogus reply */ GNUNET_break_op (0); - return GNUNET_SYSERR; + goto fail; } - TALER_amount_hton (&pc.recoup_amount, - &amount); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH, - &pc, - &exchange_sig.eddsa_signature, - &exchange_pub.eddsa_pub)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - add = GNUNET_YES; + ar->type = TALER_EXCHANGE_AR_REGEX; + ar->details.regex.posix_egrep = GNUNET_strdup (regex); + ar->details.regex.human_hint = GNUNET_strdup (hint); + continue; } - else + /* unsupported type */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +fail: + GNUNET_free (*resta); + *resta_len = 0; + return GNUNET_SYSERR; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_parse_accounts ( + const struct TALER_MasterPublicKeyP *master_pub, + const json_t *accounts, + unsigned int was_length, + struct TALER_EXCHANGE_WireAccount was[static was_length]) +{ + memset (was, + 0, + sizeof (struct TALER_EXCHANGE_WireAccount) * was_length); + GNUNET_assert (was_length == + json_array_size (accounts)); + for (unsigned int i = 0; + i<was_length; + i++) + { + struct TALER_EXCHANGE_WireAccount *wa = &was[i]; + const char *payto_uri; + const char *conversion_url = NULL; + const char *bank_label = NULL; + int64_t priority = 0; + const json_t *credit_restrictions; + const json_t *debit_restrictions; + struct GNUNET_JSON_Specification spec_account[] = { + TALER_JSON_spec_payto_uri ("payto_uri", + &payto_uri), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("conversion_url", + &conversion_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_int64 ("priority", + &priority), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("bank_label", + &bank_label), + NULL), + GNUNET_JSON_spec_array_const ("credit_restrictions", + &credit_restrictions), + GNUNET_JSON_spec_array_const ("debit_restrictions", + &debit_restrictions), + GNUNET_JSON_spec_fixed_auto ("master_sig", + &wa->master_sig), + GNUNET_JSON_spec_end () + }; + json_t *account; + + account = json_array_get (accounts, + i); + if (GNUNET_OK != + GNUNET_JSON_parse (account, + spec_account, + NULL, NULL)) { - /* signature not supported, new version on server? */ + /* bogus reply */ GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected type `%s' in response\n", - type); - GNUNET_assert (GNUNET_SYSERR == add); return GNUNET_SYSERR; } - - if (GNUNET_YES == add) + if ( (NULL != master_pub) && + (GNUNET_OK != + TALER_exchange_wire_signature_check ( + payto_uri, + conversion_url, + debit_restrictions, + credit_restrictions, + master_pub, + &wa->master_sig)) ) { - /* This amount should be added to the total */ - if (0 > - TALER_amount_add (total, - total, - &amount)) - { - /* overflow in history already!? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + /* bogus reply */ + GNUNET_break_op (0); + return GNUNET_SYSERR; } - else + if ( (GNUNET_OK != + parse_restrictions (credit_restrictions, + &wa->credit_restrictions_length, + &wa->credit_restrictions)) || + (GNUNET_OK != + parse_restrictions (debit_restrictions, + &wa->debit_restrictions_length, + &wa->debit_restrictions)) ) { - /* This amount should be subtracted from the total. - - However, for the implementation, we first *add* up all of - these negative amounts, as we might get refunds before - deposits from a semi-evil exchange. Then, at the end, we do - the subtraction by calculating "total = total - rtotal" */GNUNET_assert (GNUNET_NO == add); - if (0 > - TALER_amount_add (&rtotal, - &rtotal, - &amount)) - { - /* overflow in refund history? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - + /* bogus reply */ + GNUNET_break_op (0); + return GNUNET_SYSERR; } - } - - - /* Finally, subtract 'rtotal' from total to handle the subtractions */ - if (0 > - TALER_amount_subtract (total, - total, - &rtotal)) - { - /* underflow in history? inconceivable! Bad exchange! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - + wa->payto_uri = GNUNET_strdup (payto_uri); + wa->priority = priority; + if (NULL != conversion_url) + wa->conversion_url = GNUNET_strdup (conversion_url); + if (NULL != bank_label) + wa->bank_label = GNUNET_strdup (bank_label); + } /* end 'for all accounts */ return GNUNET_OK; } /** - * Obtain meta data about an exchange (online) signing - * key. + * Free array of account restrictions. * - * @param keys from where to obtain the meta data - * @param exchange_pub public key to lookup - * @return NULL on error (@a exchange_pub not known) + * @param ar_len length of @a ar + * @param[in] ar array to free contents of (but not @a ar itself) */ -const struct TALER_EXCHANGE_SigningPublicKey * -TALER_EXCHANGE_get_signing_key_info ( - const struct TALER_EXCHANGE_Keys *keys, - const struct TALER_ExchangePublicKeyP *exchange_pub) +static void +free_restrictions (unsigned int ar_len, + struct TALER_EXCHANGE_AccountRestriction ar[static ar_len]) { - for (unsigned int i = 0; i<keys->num_sign_keys; i++) + for (unsigned int i = 0; i<ar_len; i++) { - const struct TALER_EXCHANGE_SigningPublicKey *spk - = &keys->sign_keys[i]; + struct TALER_EXCHANGE_AccountRestriction *a = &ar[i]; + switch (a->type) + { + case TALER_EXCHANGE_AR_INVALID: + GNUNET_break (0); + break; + case TALER_EXCHANGE_AR_DENY: + break; + case TALER_EXCHANGE_AR_REGEX: + GNUNET_free (ar->details.regex.posix_egrep); + GNUNET_free (ar->details.regex.human_hint); + json_decref (ar->details.regex.human_hint_i18n); + break; + } + } +} - if (0 == GNUNET_memcmp (exchange_pub, - &spk->key)) - return spk; + +void +TALER_EXCHANGE_free_accounts ( + unsigned int was_len, + struct TALER_EXCHANGE_WireAccount was[static was_len]) +{ + for (unsigned int i = 0; i<was_len; i++) + { + struct TALER_EXCHANGE_WireAccount *wa = &was[i]; + + GNUNET_free (wa->payto_uri); + GNUNET_free (wa->conversion_url); + GNUNET_free (wa->bank_label); + free_restrictions (wa->credit_restrictions_length, + wa->credit_restrictions); + GNUNET_array_grow (wa->credit_restrictions, + wa->credit_restrictions_length, + 0); + free_restrictions (wa->debit_restrictions_length, + wa->debit_restrictions); + GNUNET_array_grow (wa->debit_restrictions, + wa->debit_restrictions_length, + 0); } - return NULL; } |