merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 34f4337eae99966311f276b59982bd101f9b7b1c
parent 47aec436a6c8a2e7ca13313235b6694ec18702ac
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Wed, 25 Dec 2024 20:59:22 +0100

Merge branch 'master' into dev/bohdan-potuzhnyi/donau-integration

Diffstat:
Mconfigure.ac | 2+-
Mcontrib/ci/jobs/0-codespell/job.sh | 1+
Mcontrib/merchant-spa.lock | 2+-
Mcontrib/uncrustify_precommit | 48+++++++++++++++++++++++++++++++++++++++++-------
Mdebian/changelog | 6++++++
Mdebian/control | 10+++++-----
Mdoc/doxygen/taler.doxy | 2+-
Msrc/backend/taler-merchant-httpd_contract.c | 8+++++++-
Msrc/backend/taler-merchant-httpd_contract.h | 110++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/backend/taler-merchant-httpd_exchanges.c | 82++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 662+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 1500++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/backenddb/pg_get_kyc_limits.c | 4++++
Msrc/backenddb/pg_get_kyc_limits.h | 2++
Msrc/include/Makefile.am | 1+
Msrc/include/taler_merchant_testing_lib.h | 4++--
Msrc/include/taler_merchantdb_plugin.h | 739++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/merchant-tools/taler-merchant-benchmark.c | 5+++--
Msrc/testing/Makefile.am | 1-
Msrc/testing/test_kyc_api.c | 72+++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/testing/test_kyc_api.conf | 7++++++-
Msrc/testing/test_merchant_api.c | 8++++----
Msrc/testing/test_merchant_api.conf | 2+-
Msrc/testing/test_merchant_order_creation.sh | 4++--
Msrc/testing/testing_api_cmd_pay_order.c | 371+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/testing/testing_api_cmd_post_orders.c | 167++++++++++++++++++++++++++++++-------------------------------------------------
26 files changed, 2303 insertions(+), 1517 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -18,7 +18,7 @@ # This configure file is in the public domain AC_PREREQ([2.69]) -AC_INIT([taler-merchant],[0.14.0],[taler-bug@gnunet.org]) +AC_INIT([taler-merchant],[0.14.1],[taler-bug@gnunet.org]) AC_CONFIG_SRCDIR([src/backend/taler-merchant-httpd.c]) AC_CONFIG_HEADERS([taler_merchant_config.h]) # support for non-recursive builds diff --git a/contrib/ci/jobs/0-codespell/job.sh b/contrib/ci/jobs/0-codespell/job.sh @@ -20,6 +20,7 @@ ChangeLog configure* config.status config.guess +config.sub depcomp */contrib/* */contrib/hellos/** diff --git a/contrib/merchant-spa.lock b/contrib/merchant-spa.lock @@ -1 +1 @@ -0.13.8-dev.1 +0.14.0-dev.3 diff --git a/contrib/uncrustify_precommit b/contrib/uncrustify_precommit @@ -1,15 +1,18 @@ -#!/bin/sh +#!/bin/bash # use as .git/hooks/pre-commit - exec 1>&2 RET=0 -# Note: we exclude src/mustach from our indentation rules as -# this is the virtually unchanged copylib of mustach -changed=$(git diff --cached --name-only) | grep -v mustach +changed=$(git diff --cached --name-only | grep -v mustach) crustified="" +# If nothing (important) has changed, return here +[ -z "$changed" ] && exit 0 + +( echo "$changed" | grep -q '\.[ch] *$*') && \ + echo "Checking formatting with uncrustify..." + for f in $changed; do if echo $f | grep \\.[c,h]\$ > /dev/null @@ -31,7 +34,38 @@ done if [ $RET = 1 ]; then echo "Run" - echo "uncrustify --no-backup -c uncrustify.cfg ${crustified}" + echo "uncrustify --replace -c uncrustify.cfg ${crustified}" echo "before committing." + exit $RET +fi + +# Make sure we have no stupid spelling error +if (which codespell > /dev/null) +then + export REPORT=$(mktemp /tmp/codespellXXXXXX) + ( set -o pipefail; + echo "Checking for spelling errors with codespell..." + contrib/ci/jobs/0-codespell/job.sh src 2> ${REPORT}; + ) || { echo "Please fix the code spell errors in ${REPORT} first"; exit 2; } +else + echo "No codespell installed, skipping spell check." + echo "** Please consider installing codespell! **" fi -exit $RET + + +# Make sure doxygen is happy with our annotations +if (which doxygen > /dev/null) +then + export REPORT=$(mktemp /tmp/doxygenXXXXXX) + [ -f doc/doxygen/Makefile ] && \ + ( set -o pipefail; + echo "Checking that doxygen is happy..." + cd doc/doxygen; + make fast 2>&1 | tee ${REPORT} | (grep error:; exit 0); + ) || { echo "Please fix the errors reported by doxygen in ${REPORT} first"; exit 3; } +else + echo "No doxygen installed, skipping check." + echo "** Please consider installing doxygen! **" +fi + +echo "Commit is all clear!" diff --git a/debian/changelog b/debian/changelog @@ -1,3 +1,9 @@ +taler-merchant (0.14.1) unstable; urgency=low + + * Release version 0.14.1 + + -- Christian Grothoff <grothoff@taler.net> Fri, 13 Dec 2024 16:35:43 +0200 + taler-merchant (0.14.0-1) unstable; urgency=low * Fix installation of taler-merchant-reconciliation and taler-merchant-exchangekeyupdate service files. diff --git a/debian/control b/debian/control @@ -8,8 +8,8 @@ Build-Depends: autopoint, debhelper-compat (= 12), gettext, - libgnunet-dev (>=0.22), - libtalerexchange-dev (>=0.14.0), + libgnunet-dev (>=0.23.1), + libtalerexchange-dev (>=0.14.1), libpq-dev (>=15.0), po-debconf, libqrencode-dev, @@ -48,7 +48,7 @@ Pre-Depends: ${misc:Pre-Depends} Depends: libtalermerchant (= ${binary:Version}), - libtalerexchange (>= 0.14.0), + libtalerexchange (>= 0.14.1), adduser, lsb-base, netbase, @@ -69,8 +69,8 @@ Package: libtalermerchant-dev Section: libdevel Architecture: any Depends: - libtalerexchange-dev (>= 0.14.0), - libgnunet-dev (>=0.22), + libtalerexchange-dev (>= 0.14.1), + libgnunet-dev (>=0.22.3), ${misc:Depends}, ${shlibs:Depends} Description: libraries to talk to a GNU Taler merchant (development). diff --git a/doc/doxygen/taler.doxy b/doc/doxygen/taler.doxy @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "GNU Taler: Merchant" -PROJECT_NUMBER = 0.14.0 +PROJECT_NUMBER = 0.14.1 PROJECT_LOGO = logo.svg OUTPUT_DIRECTORY = . CREATE_SUBDIRS = YES diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c @@ -111,9 +111,16 @@ parse_choices (void *cls, for (unsigned int i = 0; i<*choices_len; i++) { + struct TALER_MerchantContractChoice *choice = &(*choices)[i]; const json_t *jinputs; const json_t *joutputs; struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount_any ("amount", + &choice->amount), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ("max_fee", + &choice->max_fee), + NULL), GNUNET_JSON_spec_array_const ("inputs", &jinputs), GNUNET_JSON_spec_array_const ("outputs", @@ -122,7 +129,6 @@ parse_choices (void *cls, }; const char *error_name; unsigned int error_line; - struct TALER_MerchantContractChoice *choice = &(*choices)[i]; if (GNUNET_OK != GNUNET_JSON_parse (json_array_get (root, i), diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h @@ -231,6 +231,9 @@ struct TALER_MerchantContractOutput */ unsigned int count; + // FIXME: add support for clients picking a validity + // period in the future for output tokens! + } token; } details; @@ -242,6 +245,18 @@ struct TALER_MerchantContractOutput */ struct TALER_MerchantContractChoice { + + /** + * Amount to be paid for this choice. + */ + struct TALER_Amount amount; + + /** + * Maximum fee the merchant is willing to pay for this choice. + * Set to an invalid amount to use instance defaults (zero or STEFAN). + */ + struct TALER_Amount max_fee; + /** * List of inputs the wallet must provision (all of them) to satisfy the * conditions for the contract. @@ -379,10 +394,9 @@ struct TALER_MerchantContractTokenFamily } details; }; + /** - * Struct to hold contract terms in v0 and v1 format. v0 contracts are modelled - * as a v1 contract with a single choice and no inputs and outputs. Use the - * version field to explicitly differentiate between v0 and v1 contracts. + * Struct to hold contract terms. */ struct TALER_MerchantContract { @@ -435,15 +449,8 @@ struct TALER_MerchantContract * Jurisdiction of the business */ json_t *jurisdiction; - } merchant; - /** - * Price to be paid for the transaction. Could be 0. The price is in addition - * to other instruments, such as rations and tokens. - * The exchange will subtract deposit fees from that amount - * before transferring it to the merchant. - */ - struct TALER_Amount brutto; + } merchant; /** * Summary of the contract. @@ -530,44 +537,81 @@ struct TALER_MerchantContract enum TALER_MerchantContractVersion version; /** - * Array of possible specific contracts the wallet/customer may choose - * from by selecting the respective index when signing the deposit - * confirmation. + * Details depending on the @e version. */ - struct TALER_MerchantContractChoice *choices; + union + { - /** - * Length of the @e choices array. - */ - unsigned int choices_len; + /** + * Details for v0 contracts. + */ + struct + { - /** - * Array of token authorities. - */ - struct TALER_MerchantContractTokenFamily *token_authorities; + /** + * Price to be paid for the transaction. Could be 0. The price is in addition + * to other instruments, such as rations and tokens. + * The exchange will subtract deposit fees from that amount + * before transferring it to the merchant. + */ + struct TALER_Amount brutto; - /** - * Length of the @e token_authorities array. - */ - unsigned int token_authorities_len; + /** + * Maximum fee as given by the client request. + */ + struct TALER_Amount max_fee; - /** - * Maximum fee as given by the client request. - */ - struct TALER_Amount max_fee; + } v0; + + /** + * Details for v1 contracts. + */ + struct + { + + /** + * Array of possible specific contracts the wallet/customer may choose + * from by selecting the respective index when signing the deposit + * confirmation. + */ + struct TALER_MerchantContractChoice *choices; + + /** + * Length of the @e choices array. + */ + unsigned int choices_len; + + /** + * Array of token authorities. + */ + struct TALER_MerchantContractTokenFamily *token_authorities; + + /** + * Length of the @e token_authorities array. + */ + unsigned int token_authorities_len; + + } v1; + + } details; + + // FIXME: Add exchanges array? - // TODO: Add exchanges array }; + enum TALER_MerchantContractInputType TMH_contract_input_type_from_string (const char *str); + enum TALER_MerchantContractOutputType TMH_contract_output_type_from_string (const char *str); + const char * TMH_string_from_contract_input_type (enum TALER_MerchantContractInputType t); + const char * TMH_string_from_contract_output_type (enum TALER_MerchantContractOutputType t); @@ -589,12 +633,14 @@ TMH_serialize_contract (const struct TALER_MerchantContract *contract, json_t *exchanges, json_t **out); + enum GNUNET_GenericReturnValue TMH_serialize_contract_v0 (const struct TALER_MerchantContract *contract, const struct TMH_MerchantInstance *instance, json_t *exchanges, json_t **out); + enum GNUNET_GenericReturnValue TMH_serialize_contract_v1 (const struct TALER_MerchantContract *contract, const struct TMH_MerchantInstance *instance, diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c @@ -679,6 +679,7 @@ TMH_exchange_check_debit ( bool have_kyc = false; struct TALER_Amount kyc_limit; bool unlimited = true; + bool no_access_token = true; if (NULL == keys) return GNUNET_SYSERR; @@ -686,7 +687,7 @@ TMH_exchange_check_debit ( max_amount->currency)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Currency missmatch: exchange %s uses %s, we need %s\n", + "Currency mismatch: exchange %s uses %s, we need %s\n", exchange->url, keys->currency, max_amount->currency); @@ -717,6 +718,7 @@ TMH_exchange_check_debit ( account at this exchange, if we have any, apply them */ } GNUNET_free (np.normalized_payto); + if (keys->kyc_enabled) { bool kyc_ok = false; json_t *jlimits = NULL; @@ -727,6 +729,7 @@ TMH_exchange_check_debit ( instance_id, exchange->url, &kyc_ok, + &no_access_token, &jlimits); GNUNET_break (qs >= 0); GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -797,45 +800,52 @@ TMH_exchange_check_debit ( TALER_amount_min (max_amount, max_amount, &kyc_limit); - /* apply both deposit and transaction limits */ - if ( (! have_kyc) && - (TALER_EXCHANGE_keys_evaluate_zero_limits ( - keys, - TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || - TALER_EXCHANGE_keys_evaluate_zero_limits ( - keys, - TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) + if (keys->kyc_enabled) { - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero ( - max_amount->currency, - max_amount)); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Evaluating default limits of %s\n", - exchange->url); - TALER_EXCHANGE_keys_evaluate_hard_limits ( - keys, - TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, - max_amount); - TALER_EXCHANGE_keys_evaluate_hard_limits ( - keys, - TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION, - max_amount); - if (TALER_EXCHANGE_keys_evaluate_zero_limits ( - keys, - TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || - TALER_EXCHANGE_keys_evaluate_zero_limits ( - keys, - TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) + /* apply both deposit and transaction limits */ + if ( (no_access_token) || + ( (! have_kyc) && + (TALER_EXCHANGE_keys_evaluate_zero_limits ( + keys, + TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || + TALER_EXCHANGE_keys_evaluate_zero_limits ( + keys, + TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Operation is zero-limited by default\n"); + "KYC requirements of %s not satisfied\n", + exchange->url); GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (max_amount->currency, - max_amount)); + TALER_amount_set_zero ( + max_amount->currency, + max_amount)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Evaluating default limits of %s\n", + exchange->url); + TALER_EXCHANGE_keys_evaluate_hard_limits ( + keys, + TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, + max_amount); + TALER_EXCHANGE_keys_evaluate_hard_limits ( + keys, + TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION, + max_amount); + if (TALER_EXCHANGE_keys_evaluate_zero_limits ( + keys, + TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || + TALER_EXCHANGE_keys_evaluate_zero_limits ( + keys, + TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Operation is zero-limited by default\n"); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (max_amount->currency, + max_amount)); + } } } return account_ok ? GNUNET_YES : GNUNET_NO; diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -107,7 +107,7 @@ enum PayPhase PP_VALIDATE_TOKENS, /** - * Contract has been paid. + * Check if contract has been paid. */ PP_CONTRACT_PAID, @@ -365,6 +365,8 @@ struct ExchangeGroup */ struct PayContext { + // FIXME: group more entries by phase that initializes them, + // like we do for 'validate_tokens'. /** * Stored in a DLL. @@ -404,16 +406,6 @@ struct PayContext struct TokenEnvelope *token_envelopes; /** - * Array with @e choices_len choices from the contract terms. - */ - struct TALER_MerchantContractChoice *choices; - - /** - * Array with @e token_families_len token families from the contract terms. - */ - struct TALER_MerchantContractTokenFamily *token_families; - - /** * MHD connection to return to */ struct MHD_Connection *connection; @@ -498,6 +490,7 @@ struct PayContext */ struct TALER_PrivateContractHashP h_contract_terms; + /** * "h_wire" from @e contract_terms. Used to identify * the instance's wire transfer method. @@ -505,19 +498,83 @@ struct PayContext struct TALER_MerchantWireHashP h_wire; /** - * Maximum fee the merchant is willing to pay, from @e root. - * Note that IF the total fee of the exchange is higher, that is - * acceptable to the merchant if the customer is willing to - * pay the difference - * (i.e. amount - max_fee <= actual_amount - actual_fee). + * Version of the contract terms. */ - struct TALER_Amount max_fee; + enum TALER_MerchantContractVersion version; /** - * Amount from @e root. This is the amount the merchant expects - * to make, minus @e max_fee. + * @e version dependent details from our contract. */ - struct TALER_Amount amount; + union + { + + struct + { + /** + * Maximum fee the merchant is willing to pay, from @e root. + * Note that IF the total fee of the exchange is higher, that is + * acceptable to the merchant if the customer is willing to + * pay the difference + * (i.e. amount - max_fee <= actual_amount - actual_fee). + */ + struct TALER_Amount max_fee; + + /** + * Amount from @e root. This is the amount the merchant expects + * to make, minus @e max_fee. + */ + struct TALER_Amount amount; + } v0; + + struct + { + + /** + * Array with @e choices_len choices from the contract terms. + */ + struct TALER_MerchantContractChoice *choices; + + /** + * Array with @e token_families_len token families from the contract terms. + */ + struct TALER_MerchantContractTokenFamily *token_families; + + /** + * Length of the @e choices array. + */ + unsigned int choices_len; + + /** + * Length of the @e token_families array. + */ + unsigned int token_families_len; + + } v1; + + } details; + + /** + * Results from the phase_validate_tokens() + */ + struct + { + + /** + * Maximum fee the merchant is willing to pay, from @e root. + * Note that IF the total fee of the exchange is higher, that is + * acceptable to the merchant if the customer is willing to + * pay the difference + * (i.e. amount - max_fee <= actual_amount - actual_fee). + */ + struct TALER_Amount max_fee; + + /** + * Amount from @e root. This is the amount the merchant expects + * to make, minus @e max_fee. + */ + struct TALER_Amount brutto; + + } validate_tokens; /** * Considering all the coins with the "found_in_db" flag @@ -600,16 +657,6 @@ struct PayContext unsigned int output_tokens_len; /** - * Length of the @e choices array. - */ - unsigned int choices_len; - - /** - * Length of the @e token_families array. - */ - unsigned int token_families_len; - - /** * Number of exchanges involved in the payment. Length * of the @e eg array. */ @@ -879,7 +926,7 @@ batch_deposit_transaction (const struct ExchangeGroup *eg, uint32_t off = 0; GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &total_without_fees)); for (size_t i = 0; i<pc->coins_cnt; i++) { @@ -1691,7 +1738,7 @@ phase_success_response (struct PayContext *pc) ? NULL : TALER_build_pos_confirmation (pc->pos_key, pc->pos_alg, - &pc->amount, + &pc->validate_tokens.brutto, pc->timestamp); token_sigs = (0 >= pc->output_tokens_len) ? NULL @@ -1903,9 +1950,9 @@ check_payment_sufficient (struct PayContext *pc) struct TALER_Amount total_needed; if (0 == pc->coins_cnt) - return TALER_amount_is_zero (&pc->amount); + return TALER_amount_is_zero (&pc->validate_tokens.brutto); GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &total_wire_fee)); for (unsigned int i = 0; i < pc->num_exchanges; i++) { @@ -1942,10 +1989,10 @@ check_payment_sufficient (struct PayContext *pc) * amount with fee / and wire fee, for all the coins. */ GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &acc_fee)); GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &acc_amount)); for (size_t i = 0; i<pc->coins_cnt; i++) { @@ -2013,7 +2060,7 @@ check_payment_sufficient (struct PayContext *pc) TALER_amount2s (&total_wire_fee)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deposit fee limit for merchant: %s\n", - TALER_amount2s (&pc->max_fee)); + TALER_amount2s (&pc->validate_tokens.max_fee)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Total refunded amount: %s\n", TALER_amount2s (&pc->total_refunded)); @@ -2049,7 +2096,7 @@ check_payment_sufficient (struct PayContext *pc) "Overflow adding up amounts")); return false; } - if (-1 == TALER_amount_cmp (&pc->max_fee, + if (-1 == TALER_amount_cmp (&pc->validate_tokens.max_fee, &acc_fee)) { /** @@ -2063,12 +2110,12 @@ check_payment_sufficient (struct PayContext *pc) GNUNET_assert (TALER_AAR_RESULT_POSITIVE == TALER_amount_subtract (&excess_fee, &acc_fee, - &pc->max_fee)); + &pc->validate_tokens.max_fee)); /* add that to the total */ if (0 > TALER_amount_add (&total_needed, &excess_fee, - &pc->amount)) + &pc->validate_tokens.brutto)) { GNUNET_break (0); pay_end (pc, @@ -2084,7 +2131,7 @@ check_payment_sufficient (struct PayContext *pc) { /* Fees are fully covered by the merchant, all we require is that the total payment is not below the contract's amount */ - total_needed = pc->amount; + total_needed = pc->validate_tokens.brutto; } /* Do not count refunds towards the payment */ @@ -2123,7 +2170,7 @@ check_payment_sufficient (struct PayContext *pc) return false; } if (-1 < TALER_amount_cmp (&acc_amount, - &pc->amount)) + &pc->validate_tokens.brutto)) { GNUNET_break_op (0); pay_end (pc, @@ -2181,13 +2228,13 @@ phase_execute_pay_transaction (struct PayContext *pc) (used in check_coin_paid(), check_coin_refunded() and check_payment_sufficient()). */ GNUNET_break (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &pc->total_paid)); GNUNET_break (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &pc->total_fees_paid)); GNUNET_break (GNUNET_OK == - TALER_amount_set_zero (pc->amount.currency, + TALER_amount_set_zero (pc->validate_tokens.brutto.currency, &pc->total_refunded)); for (size_t i = 0; i<pc->coins_cnt; i++) pc->dc[i].found_in_db = false; @@ -2281,7 +2328,8 @@ phase_execute_pay_transaction (struct PayContext *pc) TALER_MHD_reply_with_error (pc->connection, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, - pc->amount.currency)); + pc->validate_tokens.brutto.currency)) + ; return; } } @@ -2647,20 +2695,23 @@ sign_token_envelopes (struct PayContext *pc, * @param slug slug to search for * @return NULL if @a slug was not found */ -static struct TALER_MerchantContractTokenFamily * +static const struct TALER_MerchantContractTokenFamily * find_family (const struct PayContext *pc, const char *slug) { - for (unsigned int i = 0; i<pc->token_families_len; i++) + for (unsigned int i = 0; i<pc->details.v1.token_families_len; i++) { - if (0 == strcmp (pc->token_families[i].slug, + const struct TALER_MerchantContractTokenFamily *tfi + = &pc->details.v1.token_families[i]; + + if (0 == strcmp (tfi->slug, slug)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Token family %s found with %u keys\n", slug, - pc->token_families[i].keys_len); - return &pc->token_families[i]; + tfi->keys_len); + return tfi; } } return NULL; @@ -2678,178 +2729,173 @@ find_family (const struct PayContext *pc, static void phase_validate_tokens (struct PayContext *pc) { - if (NULL == pc->choices || 0 >= pc->choices_len) + switch (pc->version) { + case TALER_MCV_V0: /* No tokens to validate */ pc->phase = PP_PAY_TRANSACTION; - return; - } - - if (pc->choice_index < 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order `%s' has non-empty choices array but" - "request is missing 'choice_index' field\n", - pc->order_id); - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING, - NULL)); - return; - } - - if (pc->choice_index >= pc->choices_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order `%s' has choices array with %u elements but " - "request has 'choice_index' field with value %ld\n", - pc->order_id, - pc->choices_len, - pc->choice_index); - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS, - NULL)); - return; - } - - { - const struct TALER_MerchantContractChoice *selected - = &pc->choices[pc->choice_index]; - - for (unsigned int i = 0; i<selected->inputs_len; i++) + pc->validate_tokens.max_fee = pc->details.v0.max_fee; + pc->validate_tokens.brutto = pc->details.v0.amount; + break; + case TALER_MCV_V1: { - const struct TALER_MerchantContractInput *input - = &selected->inputs[i]; - const struct TALER_MerchantContractTokenFamily *family; + const struct TALER_MerchantContractChoice *selected + = &pc->details.v1.choices[pc->choice_index]; - if (input->type != TALER_MCIT_TOKEN) - { - /* only validate inputs of type token (for now) */ - continue; - } + pc->validate_tokens.max_fee = selected->max_fee; + pc->validate_tokens.brutto = selected->amount; - family = find_family (pc, - input->details.token.token_family_slug); - if (NULL == family) - { - /* this should never happen, since the choices and - token families are validated on insert. */ - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "token family not found in order")); - return; - } - if (GNUNET_NO == - find_valid_input_tokens (pc, - family, - i, - input->details.token.count)) + for (unsigned int i = 0; i<selected->inputs_len; i++) { - /* Error is already scheduled from find_valid_input_token. */ - return; - } - } + const struct TALER_MerchantContractInput *input + = &selected->inputs[i]; + const struct TALER_MerchantContractTokenFamily *family; - GNUNET_array_grow (pc->output_tokens, - pc->output_tokens_len, - selected->outputs_len); - - for (unsigned int i = 0; i<selected->outputs_len; i++) - { - enum GNUNET_DB_QueryStatus qs; - struct TALER_MERCHANTDB_TokenFamilyKeyDetails details; - const struct TALER_MerchantContractOutput *output - = &selected->outputs[i]; - struct TALER_MerchantContractTokenFamily *family; - struct TALER_MerchantContractTokenFamilyKey *key; + if (input->type != TALER_MCIT_TOKEN) + { + /* only validate inputs of type token (for now) */ + continue; + } - if (output->type != TALER_MCOT_TOKEN) - { - /* only validate outputs of type tokens (for now) */ - continue; + family = find_family (pc, + input->details.token.token_family_slug); + if (NULL == family) + { + /* this should never happen, since the choices and + token families are validated on insert. */ + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "token family not found in order")); + return; + } + if (GNUNET_NO == + find_valid_input_tokens (pc, + family, + i, + input->details.token.count)) + { + /* Error is already scheduled from find_valid_input_token. */ + return; + } } - family = find_family (pc, - output->details.token.token_family_slug); - if (NULL == family) - { - /* this should never happen, since the choices and - token families are validated on insert. */ - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "token family not found in order")); - return; - } - if (output->details.token.key_index >= family->keys_len) - { - /* this should never happen, since the choices and - token families are validated on insert. */ - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "key index invalid for token family")); - return; - } - key = &family->keys[output->details.token.key_index]; - qs = TMH_db->lookup_token_family_key ( - TMH_db->cls, - pc->hc->instance->settings.id, - family->slug, - pc->timestamp, - pc->pay_deadline, - &details); - if (qs <= 0) + GNUNET_array_grow (pc->output_tokens, + pc->output_tokens_len, + selected->outputs_len); + + for (unsigned int i = 0; i<selected->outputs_len; i++) { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - "Did not find key for %s at [%llu,%llu]\n", - family->slug, - (unsigned long long) pc->timestamp.abs_time.abs_value_us, - (unsigned long long) pc->pay_deadline.abs_time.abs_value_us); - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL)); - return; - } + enum GNUNET_DB_QueryStatus qs; + struct TALER_MERCHANTDB_TokenFamilyKeyDetails details; + const struct TALER_MerchantContractOutput *output + = &selected->outputs[i]; + const struct TALER_MerchantContractTokenFamily *family; + struct TALER_MerchantContractTokenFamilyKey *key; + + if (output->type != TALER_MCOT_TOKEN) + { + /* only validate outputs of type tokens (for now) */ + continue; + } - GNUNET_assert (NULL != details.priv.private_key); + family = find_family (pc, + output->details.token.token_family_slug); + if (NULL == family) + { + /* this should never happen, since the choices and + token families are validated on insert. */ + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "token family not found in order")); + return; + } + if (output->details.token.key_index >= family->keys_len) + { + /* this should never happen, since the choices and + token families are validated on insert. */ + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "key index invalid for token family")); + return; + } + key = &family->keys[output->details.token.key_index]; + qs = TMH_db->lookup_token_family_key ( + TMH_db->cls, + pc->hc->instance->settings.id, + family->slug, + pc->timestamp, + pc->pay_deadline, + &details); + if (qs <= 0) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + "Did not find key for %s at [%llu,%llu]\n", + family->slug, + (unsigned long long) pc->timestamp.abs_time.abs_value_us, + (unsigned long long) pc->pay_deadline.abs_time.abs_value_us); + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL)); + return; + } - if (GNUNET_OK != - sign_token_envelopes (pc, - key, - &details.priv, - /* TODO: Use critical field stored in database here instead. */ - details.token_family.kind == - TALER_MERCHANTDB_TFK_Subscription, - i, - output->details.token.count)) - { - /* Error is already scheduled from sign_token_envelopes. */ - return; + GNUNET_assert (NULL != details.priv.private_key); + + if (GNUNET_OK != + sign_token_envelopes (pc, + key, + &details.priv, + /* TODO: Use critical field stored in database here instead. */ + details.token_family.kind == + TALER_MERCHANTDB_TFK_Subscription, + i, + output->details.token.count)) + { + /* Error is already scheduled from sign_token_envelopes. */ + return; + } } + } + } + for (size_t i = 0; i<pc->coins_cnt; i++) + { + const struct DepositConfirmation *dc = &pc->dc[i]; + + if (GNUNET_OK != + TALER_amount_cmp_currency (&dc->cdd.amount, + &pc->validate_tokens.brutto)) + { + GNUNET_break_op (0); + fprintf (stderr, + "HERE (%u): %s != %s\n", + (unsigned int) pc->version, + dc->cdd.amount.currency, + TALER_amount2s (&pc->validate_tokens.brutto)); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + pc->validate_tokens.brutto.currency)); + return; } } @@ -3174,16 +3220,17 @@ phase_check_contract (struct PayContext *pc) /* Get details from contract and check fundamentals */ { const char *fulfillment_url = NULL; + uint64_t version = 0; struct GNUNET_JSON_Specification espec[] = { - TALER_JSON_spec_amount_any ("amount", - &pc->amount), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("version", + &version), + NULL), GNUNET_JSON_spec_mark_optional ( /* This one does not have to be a Web URL */ GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url), NULL), - TALER_JSON_spec_amount_any ("max_fee", - &pc->max_fee), GNUNET_JSON_spec_timestamp ("timestamp", &pc->timestamp), GNUNET_JSON_spec_timestamp ("refund_deadline", @@ -3192,16 +3239,6 @@ phase_check_contract (struct PayContext *pc) &pc->pay_deadline), GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", &pc->wire_transfer_deadline), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_choices ("choices", - &pc->choices, - &pc->choices_len), - NULL), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_token_families ("token_families", - &pc->token_families, - &pc->token_families_len), - NULL), GNUNET_JSON_spec_fixed_auto ("h_wire", &pc->h_wire), GNUNET_JSON_spec_mark_optional ( @@ -3216,8 +3253,6 @@ phase_check_contract (struct PayContext *pc) res = TALER_MHD_parse_internal_json_data (pc->connection, pc->contract_terms, espec); - if (NULL != fulfillment_url) - pc->fulfillment_url = GNUNET_strdup (fulfillment_url); if (GNUNET_YES != res) { GNUNET_break (0); @@ -3227,41 +3262,131 @@ phase_check_contract (struct PayContext *pc) : MHD_NO); return; } - } - - if (GNUNET_OK != - TALER_amount_cmp_currency (&pc->max_fee, - &pc->amount)) - { - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "'max_fee' in database does not match currency of contract price")); - return; - } - - for (size_t i = 0; i<pc->coins_cnt; i++) - { - struct DepositConfirmation *dc = &pc->dc[i]; - - if (GNUNET_OK != - TALER_amount_cmp_currency (&dc->cdd.amount, - &pc->amount)) + if (NULL != fulfillment_url) + pc->fulfillment_url = GNUNET_strdup (fulfillment_url); + switch (version) { - GNUNET_break_op (0); + case 0: + { + struct GNUNET_JSON_Specification v0spec[] = { + TALER_JSON_spec_amount_any ("amount", + &pc->details.v0.amount), + TALER_JSON_spec_amount_any ("max_fee", + &pc->details.v0.max_fee), + GNUNET_JSON_spec_end () + }; + res = TALER_MHD_parse_internal_json_data (pc->connection, + pc->contract_terms, + v0spec); + if (GNUNET_YES != res) + { + GNUNET_break (0); + pay_end (pc, + (GNUNET_NO == res) + ? MHD_YES + : MHD_NO); + return; + } + if (GNUNET_OK != + TALER_amount_cmp_currency (&pc->details.v0.max_fee, + &pc->details.v0.amount)) + { + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "'max_fee' in database does not match currency of contract price")); + return; + } + + if (pc->choice_index > 0) + { + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS, + "contract terms v0 has no choices")); + return; + } + } + pc->version = TALER_MCV_V0; + break; + case 1: + { + struct GNUNET_JSON_Specification v1spec[] = { + TALER_JSON_spec_choices ("choices", + &pc->details.v1.choices, + &pc->details.v1.choices_len), + TALER_JSON_spec_token_families ("token_families", + &pc->details.v1.token_families, + &pc->details.v1.token_families_len), + GNUNET_JSON_spec_end () + }; + res = TALER_MHD_parse_internal_json_data (pc->connection, + pc->contract_terms, + v1spec); + if (GNUNET_YES != res) + { + GNUNET_break (0); + pay_end (pc, + (GNUNET_NO == res) + ? MHD_YES + : MHD_NO); + return; + } + if (pc->choice_index < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order `%s' has non-empty choices array but" + "request is missing 'choice_index' field\n", + pc->order_id); + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING, + NULL)); + return; + } + if (pc->choice_index >= pc->details.v1.choices_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order `%s' has choices array with %u elements but " + "request has 'choice_index' field with value %ld\n", + pc->order_id, + pc->details.v1.choices_len, + pc->choice_index); + GNUNET_break (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS, + NULL)); + return; + } + } + pc->version = TALER_MCV_V1; + break; + default: + GNUNET_break (0); pay_end (pc, TALER_MHD_reply_with_error ( pc->connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, - pc->amount.currency)); + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "contract 'version' in database not supported by this backend") + ); return; } } + if (GNUNET_TIME_timestamp_cmp (pc->wire_transfer_deadline, <, pc->refund_deadline)) @@ -3288,7 +3413,7 @@ phase_check_contract (struct PayContext *pc) return; } - /* Make sure wire method (still) exists for this instance */ +/* Make sure wire method (still) exists for this instance */ { struct TMH_WireMethod *wm; @@ -3753,25 +3878,32 @@ pay_context_cleanup (void *cls) GNUNET_free (eg); } GNUNET_free (pc->egs); - for (unsigned int i = 0; i< pc->token_families_len; i++) + switch (pc->version) { - struct TALER_MerchantContractTokenFamily *tf - = &pc->token_families[i]; - - GNUNET_free (tf->slug); - GNUNET_free (tf->name); - GNUNET_free (tf->description); - json_decref (tf->description_i18n); - for (unsigned int j = 0; j<tf->keys_len; j++) + case TALER_MCV_V0: + break; + case TALER_MCV_V1: + for (unsigned int i = 0; i< pc->details.v1.token_families_len; i++) { - struct TALER_MerchantContractTokenFamilyKey *key - = &tf->keys[j]; + struct TALER_MerchantContractTokenFamily *tf + = &pc->details.v1.token_families[i]; - TALER_token_issue_pub_free (&key->pub); + GNUNET_free (tf->slug); + GNUNET_free (tf->name); + GNUNET_free (tf->description); + json_decref (tf->description_i18n); + for (unsigned int j = 0; j<tf->keys_len; j++) + { + struct TALER_MerchantContractTokenFamilyKey *key + = &tf->keys[j]; + + TALER_token_issue_pub_free (&key->pub); + } + GNUNET_free (tf->keys); } - GNUNET_free (tf->keys); + GNUNET_free (pc->details.v1.token_families); + break; } - GNUNET_free (pc->token_families); if (NULL != pc->response) { MHD_destroy_response (pc->response); diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -247,10 +247,6 @@ struct OrderContext */ struct { - /** - * Version of the contract terms. - */ - enum TALER_MerchantContractVersion version; /** * Our order ID. @@ -295,11 +291,6 @@ struct OrderContext const char *public_reorder_url; /** - * Array of contract choices. Is null for v0 contracts. - */ - const json_t *choices; - - /** * Merchant base URL. */ char *merchant_base_url; @@ -335,17 +326,6 @@ struct OrderContext const json_t *delivery_location; /** - * Gross amount value of the contract. Used to - * compute @e max_stefan_fee. - */ - struct TALER_Amount brutto; - - /** - * Maximum fee as given by the client request. - */ - struct TALER_Amount max_fee; - - /** * Specifies for how long the wallet should try to get an * automatic refund for the purchase. */ @@ -367,6 +347,45 @@ struct OrderContext */ uint32_t minimum_age; + /** + * Version of the contract terms. + */ + enum TALER_MerchantContractVersion version; + + /** + * Details present depending on @e version. + */ + union + { + /** + * Details only present for v0. + */ + struct + { + /** + * Gross amount value of the contract. Used to + * compute @e max_stefan_fee. + */ + struct TALER_Amount brutto; + + /** + * Maximum fee as given by the client request. + */ + struct TALER_Amount max_fee; + } v0; + + /** + * Details only present for v1. + */ + struct + { + /** + * Array of contract choices. Is null for v0 contracts. + */ + const json_t *choices; + } v1; + } details; + } parse_order; /** @@ -445,18 +464,16 @@ struct OrderContext bool exchange_good; /** - * Maximum fee for @e order based on STEFAN curves. - * Used to set @e max_fee if not provided as part of - * @e order. + * Array of maximum amounts that could be paid over all + * available exchanges. Used to determine if this + * order creation requests exceeds legal limits. */ - struct TALER_Amount max_stefan_fee; + struct TALER_Amount *total_exchange_limits; /** - * Maximum amount that could be paid over all - * available exchanges. Used to determine if this - * order creation requests exceeds legal limits. + * Length of the @e total_exchange_limits array. */ - struct TALER_Amount total_exchange_limit; + unsigned int num_total_exchange_limits; /** * How long do we wait at most until giving up on getting keys? @@ -468,6 +485,43 @@ struct OrderContext */ struct GNUNET_SCHEDULER_Task *wakeup_task; + /** + * Details depending on the contract version. + */ + union + { + + /** + * Details for contract v0. + */ + struct + { + /** + * Maximum fee for @e order based on STEFAN curves. + * Used to set @e max_fee if not provided as part of + * @e order. + */ + struct TALER_Amount max_stefan_fee; + + } v0; + + /** + * Details for contract v1. + */ + struct + { + /** + * Maximum fee for @e order based on STEFAN curves by + * contract choice. + * Used to set @e max_fee if not provided as part of + * @e order. + */ + struct TALER_Amount *max_stefan_fees; + + } v1; + + } details; + } set_exchanges; /** @@ -475,10 +529,37 @@ struct OrderContext */ struct { + /** - * Maximum fee + * Details depending on the contract version. */ - struct TALER_Amount max_fee; + union + { + + /** + * Details for contract v0. + */ + struct + { + /** + * Maximum fee + */ + struct TALER_Amount max_fee; + } v0; + + /** + * Details for contract v1. + */ + struct + { + /** + * Maximum fees by contract choice. + */ + struct TALER_Amount *max_fees; + + } v1; + + } details; } set_max_fee; /** @@ -693,6 +774,16 @@ clean_order (void *cls) json_decref (oc->set_exchanges.exchanges); oc->set_exchanges.exchanges = NULL; } + GNUNET_free (oc->set_exchanges.total_exchange_limits); + switch (oc->parse_order.version) + { + case TALER_MCV_V0: + break; + case TALER_MCV_V1: + GNUNET_free (oc->set_max_fee.details.v1.max_fees); + GNUNET_free (oc->set_exchanges.details.v1.max_stefan_fees); + break; + } if (NULL != oc->merge_inventory.products) { json_decref (oc->merge_inventory.products); @@ -887,7 +978,6 @@ execute_transaction (struct OrderContext *oc) } { - enum GNUNET_DB_QueryStatus wqs; json_t *jhook; jhook = GNUNET_JSON_PACK ( @@ -899,21 +989,21 @@ execute_transaction (struct OrderContext *oc) oc->hc->instance->settings.id) ); GNUNET_assert (NULL != jhook); - wqs = TMH_trigger_webhook (oc->hc->instance->settings.id, - "order_created", - jhook); + qs = TMH_trigger_webhook (oc->hc->instance->settings.id, + "order_created", + jhook); json_decref (jhook); - if (0 > wqs) + if (0 > qs) { TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR == wqs) - return wqs; - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == wqs); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); reply_with_error (oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_STORE_FAILED, "failed to trigger webhooks"); - return wqs; + return qs; } } @@ -1791,86 +1881,38 @@ add_output_token_family (struct OrderContext *oc, /** - * Serialize order into @a oc->serialize_order.contract, - * ready to be stored in the database. Upon success, continue - * processing with check_contract(). + * Build JSON array that represents all of the token families + * in the contract. * - * @param[in,out] oc order context + * @param[in] v1-style order + * @return JSON array with token families for the contract */ -static void -serialize_order (struct OrderContext *oc) +static json_t * +output_token_families (struct OrderContext *oc) { - const struct TALER_MERCHANTDB_InstanceSettings *settings = - &oc->hc->instance->settings; - json_t *merchant; json_t *token_families = json_object (); - json_t *choices = json_array (); - - merchant = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("name", - settings->name), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("website", - settings->website)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("email", - settings->email)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("logo", - settings->logo))); - GNUNET_assert (NULL != merchant); - { - json_t *loca; - - /* Handle merchant address */ - loca = settings->address; - if (NULL != loca) - { - loca = json_deep_copy (loca); - GNUNET_assert (NULL != loca); - GNUNET_assert (0 == - json_object_set_new (merchant, - "address", - loca)); - } - } - { - json_t *juri; - - /* Handle merchant jurisdiction */ - juri = settings->jurisdiction; - if (NULL != juri) - { - juri = json_deep_copy (juri); - GNUNET_assert (NULL != juri); - GNUNET_assert (0 == - json_object_set_new (merchant, - "jurisdiction", - juri)); - } - } + GNUNET_assert (NULL != token_families); for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++) { - json_t *keys = json_array (); - struct TALER_MerchantContractTokenFamily *family + const struct TALER_MerchantContractTokenFamily *family = &oc->parse_choices.token_families[i]; + json_t *keys; json_t *jfamily; + keys = json_array (); + GNUNET_assert (NULL != keys); for (unsigned int j = 0; j<family->keys_len; j++) { - struct TALER_MerchantContractTokenFamilyKey key = family->keys[j]; - + const struct TALER_MerchantContractTokenFamilyKey *key + = &family->keys[j]; json_t *jkey = GNUNET_JSON_PACK ( - /* TODO: Remove h_pub. */ - GNUNET_JSON_pack_data_auto ("h_pub", - &key.pub.public_key->pub_key_hash), TALER_JSON_pack_token_pub ("public_key", - &key.pub), + &key->pub), GNUNET_JSON_pack_timestamp ("valid_after", - key.valid_after), + key->valid_after), GNUNET_JSON_pack_timestamp ("valid_before", - key.valid_before) + key->valid_before) ); GNUNET_assert (0 == @@ -1878,7 +1920,7 @@ serialize_order (struct OrderContext *oc) jkey)); } - /* TODO: Add 'details' field. */ + /* FIXME: Add 'details' field. */ jfamily = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("name", family->name), @@ -1893,11 +1935,28 @@ serialize_order (struct OrderContext *oc) family->critical) ); - GNUNET_assert (0 == json_object_set_new (token_families, - family->slug, - jfamily)); + GNUNET_assert (0 == + json_object_set_new (token_families, + family->slug, + jfamily)); } + return token_families; +} + + +/** + * Build JSON array that represents all of the contract choices + * in the contract. + * + * @param[in] v1-style order + * @return JSON array with token families for the contract + */ +static json_t * +output_contract_choices (struct OrderContext *oc) +{ + json_t *choices = json_array (); + GNUNET_assert (NULL != choices); for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) { const struct TALER_MerchantContractChoice *choice @@ -1913,15 +1972,15 @@ serialize_order (struct OrderContext *oc) = &choice->inputs[j]; json_t *jinput; - /* For now, only tokens are supported */ + /* For now, only tokens are supported for inputs */ GNUNET_assert (TALER_MCIT_TOKEN == input->type); jinput = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("kind", - TMH_string_from_contract_input_type (input-> - type)), + TMH_string_from_contract_input_type ( + input->type)), GNUNET_JSON_pack_string ("token_family_slug", input->details.token.token_family_slug), - GNUNET_JSON_pack_int64 ("number", + GNUNET_JSON_pack_int64 ("count", input->details.token.count) ); @@ -1936,19 +1995,47 @@ serialize_order (struct OrderContext *oc) json_t *joutput; /* For now, only tokens are supported */ - GNUNET_assert (TALER_MCOT_TOKEN == output->type); - - joutput = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("kind", - TMH_string_from_contract_output_type (output-> - type)), - GNUNET_JSON_pack_string ("token_family_slug", - output->details.token.token_family_slug), - GNUNET_JSON_pack_int64 ("number", - output->details.token.count), - GNUNET_JSON_pack_int64 ("key_index", - output->details.token.key_index) - ); + switch (output->type) + { + case TALER_MCOT_INVALID: + /* How did we get here? */ + GNUNET_assert (0); + /* mostly to make compiler happy... */ + finalize_order (oc, + MHD_NO); + json_decref (choices); + return NULL; + case TALER_MCOT_TOKEN: + joutput = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("kind", + TMH_string_from_contract_output_type ( + output->type)), + GNUNET_JSON_pack_string ("token_family_slug", + output->details.token.token_family_slug), + GNUNET_JSON_pack_int64 ("count", + output->details.token.count), + GNUNET_JSON_pack_int64 ("key_index", + output->details.token.key_index) + ); + break; + case TALER_MCOT_COIN: + /* Not implemented, how did we get here? */ + GNUNET_break (0); + reply_with_error (oc, + MHD_HTTP_NOT_IMPLEMENTED, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "currency conversion not supported"); + json_decref (choices); + return NULL; + case TALER_MCOT_TAX_RECEIPT: + // FIXME: generate JSON for DONAU here instead of killing + // the connection! + GNUNET_break (0); + finalize_order (oc, + MHD_NO); + json_decref (choices); + return NULL; + } GNUNET_assert (0 == json_array_append_new (outputs, @@ -1958,6 +2045,10 @@ serialize_order (struct OrderContext *oc) { json_t *jchoice = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("amount", + &choice->amount), + TALER_JSON_pack_amount ("max_fee", + &oc->set_max_fee.details.v1.max_fees[i]), GNUNET_JSON_pack_array_incref ("inputs", inputs), GNUNET_JSON_pack_array_incref ("outputs", @@ -1968,6 +2059,67 @@ serialize_order (struct OrderContext *oc) json_array_append_new (choices, jchoice)); } + } /* for all choices */ + return choices; +} + + +/** + * Serialize order into @a oc->serialize_order.contract, + * ready to be stored in the database. Upon success, continue + * processing with check_contract(). + * + * @param[in,out] oc order context + */ +static void +serialize_order (struct OrderContext *oc) +{ + const struct TALER_MERCHANTDB_InstanceSettings *settings = + &oc->hc->instance->settings; + json_t *merchant; + + merchant = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("name", + settings->name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("website", + settings->website)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("email", + settings->email)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("logo", + settings->logo))); + GNUNET_assert (NULL != merchant); + { + json_t *loca; + + /* Handle merchant address */ + loca = settings->address; + if (NULL != loca) + { + loca = json_deep_copy (loca); + GNUNET_assert (NULL != loca); + GNUNET_assert (0 == + json_object_set_new (merchant, + "address", + loca)); + } + } + { + json_t *juri; + + /* Handle merchant jurisdiction */ + juri = settings->jurisdiction; + if (NULL != juri) + { + juri = json_deep_copy (juri); + GNUNET_assert (NULL != juri); + GNUNET_assert (0 == + json_object_set_new (merchant, + "jurisdiction", + juri)); + } } oc->serialize_order.contract = GNUNET_JSON_PACK ( @@ -1976,8 +2128,9 @@ serialize_order (struct OrderContext *oc) GNUNET_JSON_pack_string ("summary", oc->parse_order.summary), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("summary_i18n", - (json_t *) oc->parse_order.summary_i18n)), + GNUNET_JSON_pack_object_incref ( + "summary_i18n", + (json_t *) oc->parse_order.summary_i18n)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("public_reorder_url", oc->parse_order.public_reorder_url)), @@ -1985,10 +2138,9 @@ serialize_order (struct OrderContext *oc) GNUNET_JSON_pack_string ("fulfillment_message", oc->parse_order.fulfillment_message)), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n", - (json_t *) oc->parse_order. - fulfillment_message_i18n)) - , + GNUNET_JSON_pack_object_incref ( + "fulfillment_message_i18n", + (json_t *) oc->parse_order.fulfillment_message_i18n)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("fulfillment_url", oc->parse_order.fulfillment_url)), @@ -2013,9 +2165,9 @@ serialize_order (struct OrderContext *oc) GNUNET_JSON_pack_timestamp ("delivery_date", oc->parse_order.delivery_date)), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("delivery_location", - (json_t *) oc->parse_order. - delivery_location)), + GNUNET_JSON_pack_object_incref ( + "delivery_location", + (json_t *) oc->parse_order.delivery_location)), GNUNET_JSON_pack_string ("merchant_base_url", oc->parse_order.merchant_base_url), GNUNET_JSON_pack_object_steal ("merchant", @@ -2024,43 +2176,61 @@ serialize_order (struct OrderContext *oc) &oc->hc->instance->merchant_pub), GNUNET_JSON_pack_array_incref ("exchanges", oc->set_exchanges.exchanges), - TALER_JSON_pack_amount ("max_fee", - &oc->set_max_fee.max_fee), - TALER_JSON_pack_amount ("amount", - &oc->parse_order.brutto), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("choices", - choices) - ), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_steal ("token_families", - token_families) - ), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("extra", (json_t *) oc->parse_order.extra)) ); + { + json_t *xtra; + + switch (oc->parse_order.version) + { + case TALER_MCV_V0: + xtra = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("max_fee", + &oc->set_max_fee.details.v0.max_fee), + TALER_JSON_pack_amount ("amount", + &oc->parse_order.details.v0.brutto)); + break; + case TALER_MCV_V1: + { + json_t *token_families = output_token_families (oc); + json_t *choices = output_contract_choices (oc); + + if ( (NULL == token_families) || + (NULL == choices) ) + { + GNUNET_break (0); + return; + } + xtra = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("choices", + choices), + GNUNET_JSON_pack_object_steal ("token_families", + token_families)); + break; + } + default: + GNUNET_assert (0); + } + GNUNET_assert (0 == + json_object_update (oc->serialize_order.contract, + xtra)); + json_decref (xtra); + } + + /* Pack does not work here, because it doesn't set zero-values for timestamps */ GNUNET_assert (0 == json_object_set_new (oc->serialize_order.contract, "refund_deadline", GNUNET_JSON_from_timestamp ( oc->parse_order.refund_deadline))); - - GNUNET_log ( - GNUNET_ERROR_TYPE_INFO, - "Refund deadline for contact is %llu\n", - (unsigned long long) oc->parse_order.refund_deadline.abs_time.abs_value_us); - GNUNET_log ( - GNUNET_ERROR_TYPE_INFO, - "Wallet timestamp for contact is %llu\n", - (unsigned long long) oc->parse_order.timestamp.abs_time.abs_value_us); - - /* Pack does not work here, because it sets zero-values for relative times */ /* auto_refund should only be set if it is not 0 */ if (! GNUNET_TIME_relative_is_zero (oc->parse_order.auto_refund)) { + /* Pack does not work here, because it sets zero-values for relative times */ GNUNET_assert (0 == json_object_set_new (oc->serialize_order.contract, "auto_refund", @@ -2073,36 +2243,81 @@ serialize_order (struct OrderContext *oc) /** - * Set max_fee in @a oc based on STEFAN value if - * not yet present. Upon success, continue - * processing with serialize_order(). + * Set @a max_fee in @a oc based on @a max_stefan_fee value if not overridden + * by @a client_fee. If neither is set, set the fee to zero using currency + * from @a brutto. * * @param[in,out] oc order context + * @param brutto brutto amount to compute fee for + * @param client_fee client-given fee override (or invalid) + * @param max_stefan_fee maximum STEFAN fee of any exchange + * @param max_fee set to the maximum stefan fee */ static void -set_max_fee (struct OrderContext *oc) +compute_fee (struct OrderContext *oc, + const struct TALER_Amount *brutto, + const struct TALER_Amount *client_fee, + const struct TALER_Amount *max_stefan_fee, + struct TALER_Amount *max_fee) { - const struct TALER_MERCHANTDB_InstanceSettings *settings = - &oc->hc->instance->settings; + const struct TALER_MERCHANTDB_InstanceSettings *settings + = &oc->hc->instance->settings; - if (GNUNET_OK != - TALER_amount_is_valid (&oc->parse_order.max_fee)) + if (GNUNET_OK == + TALER_amount_is_valid (client_fee)) { - struct TALER_Amount stefan; - - if ( (settings->use_stefan) && - (GNUNET_OK == - TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) ) - stefan = oc->set_exchanges.max_stefan_fee; - else - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (oc->parse_order.brutto.currency, - &stefan)); - oc->set_max_fee.max_fee = stefan; + *max_fee = *client_fee; + return; } - else + if ( (settings->use_stefan) && + (NULL != max_stefan_fee) && + (GNUNET_OK == + TALER_amount_is_valid (max_stefan_fee)) ) + { + *max_fee = *max_stefan_fee; + return; + } + GNUNET_assert ( + GNUNET_OK == + TALER_amount_set_zero (brutto->currency, + max_fee)); +} + + +/** + * Initialize "set_max_fee" in @a oc based on STEFAN value or client + * preference. Upon success, continue processing in next phase. + * + * @param[in,out] oc order context + */ +static void +set_max_fee (struct OrderContext *oc) +{ + switch (oc->parse_order.version) { - oc->set_max_fee.max_fee = oc->parse_order.max_fee; + case TALER_MCV_V0: + compute_fee (oc, + &oc->parse_order.details.v0.brutto, + &oc->parse_order.details.v0.max_fee, + &oc->set_exchanges.details.v0.max_stefan_fee, + &oc->set_max_fee.details.v0.max_fee); + break; + case TALER_MCV_V1: + oc->set_max_fee.details.v1.max_fees + = GNUNET_new_array (oc->parse_choices.choices_len, + struct TALER_Amount); + for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) + compute_fee (oc, + &oc->parse_choices.choices[i].amount, + &oc->parse_choices.choices[i].max_fee, + NULL != oc->set_exchanges.details.v1.max_stefan_fees + ? &oc->set_exchanges.details.v1.max_stefan_fees[i] + : NULL, + &oc->set_max_fee.details.v1.max_fees[i]); + break; + default: + GNUNET_break (0); + break; } oc->phase++; } @@ -2131,63 +2346,103 @@ resume_with_keys (struct OrderContext *oc) /** - * Update MAX STEFAN fees based on @a keys. + * Given a @a brutto amount for exchange with @a keys, set the + * @a stefan_fee. Note that @a stefan_fee is updated to the maximum + * of the input and the computed fee. * - * @param[in,out] oc order context to update - * @param keys keys to derive STEFAN fees from + * @param[in,out] oc order context + * @param brutto some brutto amount the client is to pay + * @param[in,out] stefan_fee set to STEFAN fee to be paid by the merchant */ static void -update_stefan (struct OrderContext *oc, - const struct TALER_EXCHANGE_Keys *keys) +compute_stefan_fee (const struct TALER_EXCHANGE_Keys *keys, + const struct TALER_Amount *brutto, + struct TALER_Amount *stefan_fee) { struct TALER_Amount net; if (GNUNET_SYSERR != TALER_EXCHANGE_keys_stefan_b2n (keys, - &oc->parse_order.brutto, + brutto, &net)) { struct TALER_Amount fee; TALER_EXCHANGE_keys_stefan_round (keys, &net); - if (-1 == TALER_amount_cmp (&oc->parse_order.brutto, + if (-1 == TALER_amount_cmp (brutto, &net)) { /* brutto < netto! */ /* => after rounding, there is no real difference */ - net = oc->parse_order.brutto; + net = *brutto; } GNUNET_assert (0 <= TALER_amount_subtract (&fee, - &oc->parse_order.brutto, + brutto, &net)); if ( (GNUNET_OK != - TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) || - (-1 == TALER_amount_cmp (&oc->set_exchanges.max_stefan_fee, + TALER_amount_is_valid (stefan_fee)) || + (-1 == TALER_amount_cmp (stefan_fee, &fee)) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Updated STEFAN-based fee to %s\n", TALER_amount2s (&fee)); - oc->set_exchanges.max_stefan_fee = fee; + *stefan_fee = fee; } } } /** + * Update MAX STEFAN fees based on @a keys. + * + * @param[in,out] oc order context to update + * @param keys keys to derive STEFAN fees from + */ +static void +update_stefan (struct OrderContext *oc, + const struct TALER_EXCHANGE_Keys *keys) +{ + switch (oc->parse_order.version) + { + case TALER_MCV_V0: + compute_stefan_fee (keys, + &oc->parse_order.details.v0.brutto, + &oc->set_exchanges.details.v0.max_stefan_fee); + break; + case TALER_MCV_V1: + oc->set_exchanges.details.v1.max_stefan_fees + = GNUNET_new_array (oc->parse_choices.choices_len, + struct TALER_Amount); + for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) + if (0 == strcasecmp (keys->currency, + oc->parse_choices.choices[i].amount.currency)) + compute_stefan_fee (keys, + &oc->parse_choices.choices[i].amount, + &oc->set_exchanges.details.v1.max_stefan_fees[i]); + break; + default: + GNUNET_assert (0); + } +} + + +/** * Compute the set of exchanges that would be acceptable * for this order. * * @param cls our `struct OrderContext` * @param url base URL of an exchange (not used) * @param exchange internal handle for the exchange + * @param max_needed maximum amount needed in this currency */ static void get_acceptable (void *cls, const char *url, - const struct TMH_Exchange *exchange) + const struct TMH_Exchange *exchange, + const struct TALER_Amount *max_needed) { struct OrderContext *oc = cls; unsigned int priority = 42; /* make compiler happy */ @@ -2195,7 +2450,7 @@ get_acceptable (void *cls, enum GNUNET_GenericReturnValue res; struct TALER_Amount max_amount; - max_amount = oc->parse_order.brutto; + max_amount = *max_needed; res = TMH_exchange_check_debit ( oc->hc->instance->settings.id, exchange, @@ -2206,8 +2461,7 @@ get_acceptable (void *cls, url, res, TALER_amount2s (&max_amount)); - if ( (! TALER_amount_is_zero (&max_amount)) && - (TALER_amount_is_zero (&max_amount)) ) + if (TALER_amount_is_zero (&max_amount)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Exchange %s deposit limit is zero, skipping it\n", @@ -2237,17 +2491,44 @@ get_acceptable (void *cls, "Exchange %s deposit limit is %s, adding it!\n", url, TALER_amount2s (&max_amount)); - GNUNET_assert (0 <= - TALER_amount_add ( - &oc->set_exchanges.total_exchange_limit, - &oc->set_exchanges.total_exchange_limit, - &max_amount)); - GNUNET_assert (GNUNET_OK == - TALER_amount_min (&oc->set_exchanges.total_exchange_limit, - &oc->set_exchanges.total_exchange_limit, - &oc->parse_order.brutto)); - j_exchange = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("url", + { + bool found = false; + + for (unsigned int i = 0; i<oc->set_exchanges.num_total_exchange_limits; i++) + { + struct TALER_Amount *limit + = &oc->set_exchanges.total_exchange_limits[i]; + + if (GNUNET_OK == + TALER_amount_cmp_currency (limit, + &max_amount)) + { + GNUNET_assert (0 <= + TALER_amount_add (limit, + limit, + &max_amount)); + GNUNET_assert (GNUNET_OK == + TALER_amount_min (limit, + limit, + max_needed)); + found = true; + } + } + if (! found) + { + struct TALER_Amount limit; + + GNUNET_assert (GNUNET_OK == + TALER_amount_min (&limit, + &max_amount, + max_needed)); + GNUNET_array_append (oc->set_exchanges.total_exchange_limits, + oc->set_exchanges.num_total_exchange_limits, + limit); + } + } + j_exchange = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("url", url), GNUNET_JSON_pack_uint64 ("priority", priority), @@ -2293,17 +2574,59 @@ keys_cb ( } else { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got response for %skeys\n", - rx->url); - if ( (settings->use_stefan) && - (GNUNET_OK != - TALER_amount_is_valid (&oc->parse_order.max_fee)) ) - update_stefan (oc, - keys); - get_acceptable (oc, - rx->url, - exchange); + bool currency_ok = false; + struct TALER_Amount max_needed; + + switch (oc->parse_order.version) + { + case TALER_MCV_V0: + if (0 == strcasecmp (keys->currency, + oc->parse_order.details.v0.brutto.currency)) + { + max_needed = oc->parse_order.details.v0.brutto; + currency_ok = true; + } + break; + case TALER_MCV_V1: + for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) + { + const struct TALER_Amount *amount + = &oc->parse_choices.choices[i].amount; + + if (0 == strcasecmp (keys->currency, + amount->currency)) + { + if (currency_ok) + { + TALER_amount_max (&max_needed, + &max_needed, + amount); + } + else + { + max_needed = *amount; + currency_ok = true; + } + } + } + break; + default: + GNUNET_assert (0); + } + if ( (currency_ok) && + (! TALER_amount_is_zero (&max_needed)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got response for %skeys\n", + rx->url); + if (settings->use_stefan) + update_stefan (oc, + keys); + get_acceptable (oc, + rx->url, + exchange, + &max_needed); + } } GNUNET_free (rx->url); GNUNET_free (rx); @@ -2411,6 +2734,40 @@ wakeup_timeout (void *cls) /** + * Check that the @a brutto amount is at or below the exchange + * limits we have for the respective currency. + * + * @param oc order context to check + * @param brutto amount to check + * @param true if the amount is OK, false if it is too high + */ +static bool +check_exchange_limits (const struct OrderContext *oc, + struct TALER_Amount *brutto) +{ + for (unsigned int i = 0; i<oc->set_exchanges.num_total_exchange_limits; i++) + { + const struct TALER_Amount *total_exchange_limit + = &oc->set_exchanges.total_exchange_limits[i]; + + if (GNUNET_OK != + TALER_amount_cmp_currency (brutto, + total_exchange_limit)) + continue; + if (1 != + TALER_amount_cmp (brutto, + total_exchange_limit)) + return true; + } + + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot create order: %s is above the sum of hard limits from supported exchanges\n", + TALER_amount2s (brutto)); + return false; +} + + +/** * Set list of acceptable exchanges in @a oc. Upon success, continue * processing with set_max_fee(). * @@ -2420,12 +2777,32 @@ wakeup_timeout (void *cls) static bool set_exchanges (struct OrderContext *oc) { + bool need_exchange; + if (NULL != oc->set_exchanges.wakeup_task) { GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task); oc->set_exchanges.wakeup_task = NULL; } - if (TALER_amount_is_zero (&oc->parse_order.brutto)) + switch (oc->parse_order.version) + { + case TALER_MCV_V0: + need_exchange = ! TALER_amount_is_zero ( + &oc->parse_order.details.v0.brutto); + break; + case TALER_MCV_V1: + need_exchange = false; + for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) + if (! TALER_amount_is_zero (&oc->parse_choices.choices[i].amount)) + { + need_exchange = true; + break; + } + break; + default: + GNUNET_assert (0); + } + if (! need_exchange) { /* Total amount is zero, so we don't actually need exchanges! */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -2436,21 +2813,13 @@ set_exchanges (struct OrderContext *oc) return false; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order total is %s, trying to find exchanges\n", - TALER_amount2s (&oc->parse_order.brutto)); - /* Note: re-building 'oc->set_exchanges.exchanges' every time here might be a - tad expensive; could likely consider caching the result if it starts to - matter. */ + "Trying to find exchanges\n"); if (NULL == oc->set_exchanges.exchanges) { oc->set_exchanges.keys_timeout = GNUNET_TIME_relative_to_absolute (MAX_KEYS_WAIT); oc->set_exchanges.exchanges = json_array (); GNUNET_assert (NULL != oc->set_exchanges.exchanges); - GNUNET_assert ( - GNUNET_OK == - TALER_amount_set_zero (oc->parse_order.brutto.currency, - &oc->set_exchanges.total_exchange_limit)); TMH_exchange_get_trusted (&get_exchange_keys, oc); } @@ -2506,32 +2875,52 @@ set_exchanges (struct OrderContext *oc) oc->add_payment_details.wm->wire_method); return false; } - if (1 == - TALER_amount_cmp (&oc->parse_order.brutto, - &oc->set_exchanges.total_exchange_limit)) + { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Cannot create order: %s is the sum of hard limits from supported exchanges\n", - TALER_amount2s (&oc->set_exchanges.total_exchange_limit)); - notify_kyc_required (oc); - reply_with_error ( - oc, - MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS, - TALER_amount2s (&oc->set_exchanges.total_exchange_limit)); - return false; + bool ok; + struct TALER_Amount ea; + + switch (oc->parse_order.version) + { + case TALER_MCV_V0: + ea = oc->parse_order.details.v0.brutto; + ok = check_exchange_limits (oc, + &ea); + break; + case TALER_MCV_V1: + ok = true; + for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) + { + ea = oc->parse_choices.choices[i].amount; + if (! check_exchange_limits (oc, + &ea)) + { + ok = false; + break; + } + } + break; + default: + GNUNET_assert (0); + } + + if (! ok) + { + notify_kyc_required (oc); + reply_with_error ( + oc, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS, + TALER_amount2s (&ea)); + return false; + } } + if (! oc->set_exchanges.exchange_good) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating order, but possibly without usable trusted exchanges\n"); } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Can create order: %s is the sum of hard limits from supported exchanges\n", - TALER_amount2s (&oc->set_exchanges.total_exchange_limit)); - } oc->phase++; return false; } @@ -2551,18 +2940,12 @@ parse_order (struct OrderContext *oc) const char *merchant_base_url = NULL; uint64_t version = 0; const json_t *jmerchant = NULL; - /* auto_refund only needs to be type-checked, - * mostly because in GNUnet relative times can't - * be negative. */ - bool no_fee; - const char *oid; + const char *order_id; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint64 ("version", &version), NULL), - TALER_JSON_spec_amount_any ("amount", - &oc->parse_order.brutto), GNUNET_JSON_spec_string ("summary", &oc->parse_order.summary), GNUNET_JSON_spec_mark_optional ( @@ -2575,7 +2958,7 @@ parse_order (struct OrderContext *oc) NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("order_id", - &oid), + &order_id), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("fulfillment_message", @@ -2594,13 +2977,10 @@ parse_order (struct OrderContext *oc) &oc->parse_order.public_reorder_url), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("choices", - &oc->parse_order.choices), - NULL), - GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_web_url ("merchant_base_url", &merchant_base_url), NULL), + /* For sanity check, this field must NOT be present */ GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("merchant", &jmerchant), @@ -2622,10 +3002,6 @@ parse_order (struct OrderContext *oc) &oc->parse_order.wire_deadline), NULL), GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_amount_any ("max_fee", - &oc->parse_order.max_fee), - &no_fee), - GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("delivery_location", &oc->parse_order.delivery_location), NULL), @@ -2661,38 +3037,100 @@ parse_order (struct OrderContext *oc) ret); return; } - if (0 == version) + switch (version) { - oc->parse_order.version = TALER_MCV_V0; - - if (NULL != oc->parse_order.choices) + case 0: { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR, - "choices array must be null for v0 contracts"); - return; - } - } - else if (1 == version) - { - oc->parse_order.version = TALER_MCV_V1; + bool no_fee; + const json_t *choices = NULL; + struct GNUNET_JSON_Specification specv0[] = { + TALER_JSON_spec_amount_any ( + "amount", + &oc->parse_order.details.v0.brutto), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ( + "max_fee", + &oc->parse_order.details.v0.max_fee), + &no_fee), + /* for sanity check, must be *absent*! */ + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("choices", + &choices), + NULL), + GNUNET_JSON_spec_end () + }; - if (! json_is_array (oc->parse_order.choices)) + ret = TALER_MHD_parse_json_data (oc->connection, + oc->parse_request.order, + specv0); + if (GNUNET_OK != ret) + { + GNUNET_break_op (0); + finalize_order2 (oc, + ret); + return; + } + if ( (! no_fee) && + (GNUNET_OK != + TALER_amount_cmp_currency (&oc->parse_order.details.v0.brutto, + &oc->parse_order.details.v0.max_fee)) ) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + "different currencies used for 'max_fee' and 'amount' currency"); + return; + } + if (! TMH_test_exchange_configured_for_currency ( + oc->parse_order.details.v0.brutto.currency)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + // FIXME: use CONFLICT and a different EC! + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + "no trusted exchange for this currency"); + return; + } + if (NULL != choices) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR, + "choices array must be null for v0 contracts"); + return; + } + oc->parse_order.version = TALER_MCV_V0; + break; + } + case 1: { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order.choices is not a valid array"); - return; + struct GNUNET_JSON_Specification specv1[] = { + GNUNET_JSON_spec_array_const ( + "choices", + &oc->parse_order.details.v1.choices), + GNUNET_JSON_spec_end () + }; + + ret = TALER_MHD_parse_json_data (oc->connection, + oc->parse_request.order, + specv1); + if (GNUNET_OK != ret) + { + GNUNET_break_op (0); + finalize_order2 (oc, + ret); + return; + } + oc->parse_order.version = TALER_MCV_V1; + break; } - } - else - { + default: GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); reply_with_error (oc, @@ -2701,35 +3139,11 @@ parse_order (struct OrderContext *oc) "invalid version specified in order, supported are null, '0' or '1'"); return; } - if (! TMH_test_exchange_configured_for_currency ( - oc->parse_order.brutto.currency)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - "no trusted exchange for this currency"); - return; - } - if ( (! no_fee) && - (GNUNET_OK != - TALER_amount_cmp_currency (&oc->parse_order.brutto, - &oc->parse_order.max_fee)) ) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - "different currencies used for 'max_fee' and 'amount' currency"); - return; - } /* Add order_id if it doesn't exist. */ - if (NULL != oid) + if (NULL != order_id) { - oc->parse_order.order_id = GNUNET_strdup (oid); + oc->parse_order.order_id = GNUNET_strdup (order_id); } else { @@ -3013,6 +3427,217 @@ parse_donau_instances (struct OrderContext *oc) #endif /** + * Parse the inputs for a particular choice. + * + * @param[in,out] oc order context + * @param[out] choice to parse inputs for + * @param jinputs array of inputs to parse + * @return #GNUNET_OK on success, #GNUNET_SYSERR + * if an error was encountered (and already handled) + */ +static enum GNUNET_GenericReturnValue +parse_order_inputs (struct OrderContext *oc, + struct TALER_MerchantContractChoice *choice, + const json_t *jinputs) +{ + const json_t *jinput; + size_t idx; + + json_array_foreach ((json_t *) jinputs, idx, jinput) + { + struct TALER_MerchantContractInput input = { + .details.token.count = 1 + }; + const char *kind; + const char *ierror_name; + unsigned int ierror_line; + struct GNUNET_JSON_Specification ispec[] = { + // FIXME: define spec parser for 'kind'... + GNUNET_JSON_spec_string ("kind", + &kind), + GNUNET_JSON_spec_string ("token_family_slug", + &input.details.token.token_family_slug), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("count", + &input.details.token.count), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jinput, + ispec, + &ierror_name, + &ierror_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid input #%u for field %s\n", + (unsigned int) idx, + ierror_name); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + ierror_name); + return GNUNET_SYSERR; + } + + input.type = TMH_contract_input_type_from_string (kind); + if (TALER_MCIT_INVALID == input.type) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Field 'kind' invalid in input #%u\n", + (unsigned int) idx); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "kind"); + return GNUNET_SYSERR; + } + + if (0 == input.details.token.count) + { + /* Ignore inputs with 'number' field set to 0 */ + continue; + } + + if (GNUNET_OK != + add_input_token_family (oc, + input.details.token.token_family_slug)) + { + /* error is already scheduled, return. */ + return GNUNET_SYSERR; + } + + GNUNET_array_append (choice->inputs, + choice->inputs_len, + input); + } + return GNUNET_OK; +} + + +/** + * Parse the outputs for a particular choice. + * + * @param[in,out] oc order context + * @param[out] choice to parse inputs for + * @param joutputs array of outputs to parse + * @return #GNUNET_OK on success, #GNUNET_SYSERR + * if an error was encountered (and already handled) + */ +static enum GNUNET_GenericReturnValue +parse_order_outputs (struct OrderContext *oc, + struct TALER_MerchantContractChoice *choice, + const json_t *joutputs) +{ + const json_t *joutput; + size_t idx; + + json_array_foreach ((json_t *) joutputs, idx, joutput) + { + struct TALER_MerchantContractOutput output = { + .details.token.count = 1 + }; + const char *kind; + const char *ierror_name; + unsigned int ierror_line; + bool nots; + struct GNUNET_TIME_Timestamp valid_at; + struct GNUNET_JSON_Specification ispec[] = { + // FIXME: define spec parser for 'kind'... + GNUNET_JSON_spec_string ("kind", + &kind), + GNUNET_JSON_spec_string ("token_family_slug", + &output.details.token.token_family_slug), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("count", + &output.details.token.count), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("valid_at", + &valid_at), + &nots), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (joutput, + ispec, + &ierror_name, + &ierror_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid output #%u for field %s\n", + (unsigned int) idx, + ierror_name); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + ierror_name); + return GNUNET_SYSERR; + } + if (nots) + { + valid_at = oc->parse_order.pay_deadline; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Looking for output token valid at pay deadline %s\n", + GNUNET_TIME_timestamp2s (valid_at)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Looking for output token valid at %s\n", + GNUNET_TIME_timestamp2s (valid_at)); + } + if (GNUNET_TIME_timestamp_cmp (valid_at, + <, + oc->parse_order.pay_deadline)) + { + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "valid_at before pay_deadline"); + return GNUNET_SYSERR; + } + + output.type = TMH_contract_output_type_from_string (kind); + if (TALER_MCOT_INVALID == output.type) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Field 'kind' invalid in output #%u\n", + (unsigned int) idx); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "kind"); + return GNUNET_SYSERR; + } + + if (0 == output.details.token.count) + { + /* Ignore outputs with 'number' field set to 0. */ + continue; + } + + if (GNUNET_OK != + add_output_token_family (oc, + output.details.token.token_family_slug, + valid_at, + &output.details.token.key_index)) + { + /* Error is already scheduled, return. */ + return GNUNET_SYSERR; + } + + GNUNET_array_append (choice->outputs, + choice->outputs_len, + output); + } + return GNUNET_OK; +} + + +/** * Parse contract choices. Upon success, continue * processing with merge_inventory(). * @@ -3021,22 +3646,40 @@ parse_donau_instances (struct OrderContext *oc) static void parse_choices (struct OrderContext *oc) { - if (NULL == oc->parse_order.choices) + const json_t *choices; + + switch (oc->parse_order.version) { + case TALER_MCV_V0: oc->phase++; return; + case TALER_MCV_V1: + /* handle below */ + break; + default: + GNUNET_assert (0); } + choices = oc->parse_order.details.v1.choices; GNUNET_array_grow (oc->parse_choices.choices, oc->parse_choices.choices_len, - json_array_size (oc->parse_order.choices)); + json_array_size (choices)); for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) { + struct TALER_MerchantContractChoice *choice + = &oc->parse_choices.choices[i]; const char *error_name; unsigned int error_line; const json_t *jinputs; const json_t *joutputs; + bool no_fee; struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount_any ("amount", + &choice->amount), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ("max_fee", + &choice->max_fee), + &no_fee), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_array_const ("inputs", &jinputs), @@ -3049,7 +3692,7 @@ parse_choices (struct OrderContext *oc) }; enum GNUNET_GenericReturnValue ret; - ret = GNUNET_JSON_parse (json_array_get (oc->parse_order.choices, + ret = GNUNET_JSON_parse (json_array_get (choices, i), spec, &error_name, @@ -3066,6 +3709,33 @@ parse_choices (struct OrderContext *oc) "choice"); return; } + if ( (! no_fee) && + (GNUNET_OK != + TALER_amount_cmp_currency (&choice->amount, + &choice->max_fee)) ) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + "different currencies used for 'max_fee' and 'amount' currency"); + return; + } + + if (! TMH_test_exchange_configured_for_currency ( + choice->amount.currency)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + // FIXME: use CONFLICT and a different EC! + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + "no trusted exchange for this currency"); + return; + } + if ( (0 == json_array_size (jinputs)) && (0 == json_array_size (joutputs)) ) @@ -3079,195 +3749,17 @@ parse_choices (struct OrderContext *oc) "choice"); return; } - - { - // TODO: Maybe move to a separate function - const json_t *jinput; - size_t idx; - json_array_foreach ((json_t *) jinputs, idx, jinput) - { - struct TALER_MerchantContractInput input = { - .details.token.count = 1 - }; - const char *kind; - const char *ierror_name; - unsigned int ierror_line; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - &input.details.token.token_family_slug), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &input.details.token.count), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (jinput, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid input #%u for field %s\n", - (unsigned int) idx, - ierror_name); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - ierror_name); - return; - } - - input.type = TMH_contract_input_type_from_string (kind); - - if (TALER_MCIT_INVALID == input.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Field 'kind' invalid in input #%u\n", - (unsigned int) idx); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "kind"); - return; - } - - if (0 == input.details.token.count) - { - /* Ignore inputs with 'number' field set to 0 */ - continue; - } - - if (GNUNET_OK != - add_input_token_family (oc, - input.details.token.token_family_slug)) - { - /* error is already scheduled, return. */ - return; - } - - GNUNET_array_append (oc->parse_choices.choices[i].inputs, - oc->parse_choices.choices[i].inputs_len, - input); - } - } - - { - const json_t *joutput; - size_t idx; - json_array_foreach ((json_t *) joutputs, idx, joutput) - { - struct TALER_MerchantContractOutput output = { - .details.token.count = 1 - }; - const char *kind; - const char *ierror_name; - unsigned int ierror_line; - bool nots; - struct GNUNET_TIME_Timestamp valid_at; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - &output.details.token.token_family_slug), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &output.details.token.count), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("valid_at", - &valid_at), - &nots), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (joutput, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_JSON_parse_free (spec); - GNUNET_JSON_parse_free (ispec); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid output #%u for field %s\n", - (unsigned int) idx, - ierror_name); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - ierror_name); - return; - } - if (nots) - { - valid_at = oc->parse_order.pay_deadline; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Looking for output token valid at pay deadline %s\n", - GNUNET_TIME_timestamp2s (valid_at)); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Looking for output token valid at %s\n", - GNUNET_TIME_timestamp2s (valid_at)); - } - - - if (GNUNET_TIME_timestamp_cmp (valid_at, - <, - oc->parse_order.pay_deadline)) - { - GNUNET_JSON_parse_free (spec); - GNUNET_JSON_parse_free (ispec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "valid_at before pay_deadline"); - return; - } - - output.type = TMH_contract_output_type_from_string (kind); - if (TALER_MCOT_INVALID == output.type) - { - GNUNET_JSON_parse_free (spec); - GNUNET_JSON_parse_free (ispec); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Field 'kind' invalid in output #%u\n", - (unsigned int) idx); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "kind"); - return; - } - - if (0 == output.details.token.count) - { - /* Ignore outputs with 'number' field set to 0. */ - continue; - } - - if (GNUNET_OK != - add_output_token_family (oc, - output.details.token.token_family_slug, - valid_at, - &output.details.token.key_index)) - { - /* Error is already scheduled, return. */ - return; - } - - GNUNET_array_append (oc->parse_choices.choices[i].outputs, - oc->parse_choices.choices[i].outputs_len, - output); - } - } + if (GNUNET_OK != + parse_order_inputs (oc, + choice, + jinputs)) + return; + if (GNUNET_OK != + parse_order_outputs (oc, + choice, + joutputs)) + return; } - oc->phase++; } diff --git a/src/backenddb/pg_get_kyc_limits.c b/src/backenddb/pg_get_kyc_limits.c @@ -32,6 +32,7 @@ TMH_PG_get_kyc_limits ( const char *instance_id, const char *exchange_url, bool *kyc_ok, + bool *no_access_token, json_t **jlimits) { struct PostgresClosure *pg = cls; @@ -44,6 +45,8 @@ TMH_PG_get_kyc_limits ( struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_bool ("kyc_ok", kyc_ok), + GNUNET_PQ_result_spec_bool ("no_access_token", + no_access_token), GNUNET_PQ_result_spec_allow_null ( TALER_PQ_result_spec_json ("jaccount_limits", jlimits), @@ -57,6 +60,7 @@ TMH_PG_get_kyc_limits ( "SELECT" " mk.kyc_ok" ",mk.jaccount_limits" + ",mk.access_token IS NULL AS no_access_token" " FROM merchant_kyc mk" " WHERE mk.exchange_url=$3" " AND mk.account_serial=" diff --git a/src/backenddb/pg_get_kyc_limits.h b/src/backenddb/pg_get_kyc_limits.h @@ -33,6 +33,7 @@ * @param instance_id the instance for which to check * @param exchange_url base URL of the exchange * @param[out] kyc_ok true if no urgent KYC work must be done for this account + * @param[out] no_access_token true if we do not have a valid KYC access token (KYC auth missing) * @param[out] jlimits set to JSON array with AccountLimits, NULL if unknown (and likely defaults apply or KYC auth is urgently needed, see @a auth_ok) * @return database result code */ @@ -43,6 +44,7 @@ TMH_PG_get_kyc_limits ( const char *instance_id, const char *exchange_url, bool *kyc_ok, + bool *no_access_token, json_t **jlimits); #endif diff --git a/src/include/Makefile.am b/src/include/Makefile.am @@ -8,6 +8,7 @@ talerinclude_HEADERS = \ taler_merchant_bank_lib.h \ taler_merchantdb_lib.h \ taler_merchantdb_plugin.h \ + taler_merchant_util.h \ taler_merchant_service.h \ taler_merchant_testing_lib.h diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h @@ -613,7 +613,7 @@ TALER_TESTING_cmd_merchant_post_orders3 ( * @param merchant_url base URL of the merchant serving * the proposal request. * @param http_status expected HTTP status. - * @param token_family_reference label of the POST /tokenfamilies cmd. + * @param token_family_slug slug of the token family to use * @param num_inputs number of input tokens. * @param num_outputs number of output tokens. * @param order_id the name of the order to add. @@ -629,7 +629,7 @@ TALER_TESTING_cmd_merchant_post_orders_choices ( const struct GNUNET_CONFIGURATION_Handle *cfg, const char *merchant_url, unsigned int http_status, - const char *token_family_reference, + const char *token_family_slug, unsigned int num_inputs, unsigned int num_outputs, const char *order_id, diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -1395,7 +1395,7 @@ struct TALER_MERCHANTDB_Plugin * @param cls closure */ enum GNUNET_GenericReturnValue - (*connect)(void *cls); + (*connect)(void *cls); /** * Drop merchant tables. Used for testcases and to reset the DB. @@ -1404,7 +1404,7 @@ struct TALER_MERCHANTDB_Plugin * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ enum GNUNET_GenericReturnValue - (*drop_tables)(void *cls); + (*drop_tables)(void *cls); /** * Initialize merchant tables @@ -1413,7 +1413,7 @@ struct TALER_MERCHANTDB_Plugin * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ enum GNUNET_GenericReturnValue - (*create_tables)(void *cls); + (*create_tables)(void *cls); /** * Register callback to be invoked on events of type @a es. @@ -1476,8 +1476,8 @@ struct TALER_MERCHANTDB_Plugin * @return #GNUNET_OK on success */ enum GNUNET_GenericReturnValue - (*start)(void *cls, - const char *name); + (*start)(void *cls, + const char *name); /** * Start a transaction with isolation level 'read committed'. @@ -1488,8 +1488,8 @@ struct TALER_MERCHANTDB_Plugin * @return #GNUNET_OK on success */ enum GNUNET_GenericReturnValue - (*start_read_committed)(void *cls, - const char *name); + (*start_read_committed)(void *cls, + const char *name); /** * Roll back the current transaction of a database connection. @@ -1506,7 +1506,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*commit)(void *cls); + (*commit)(void *cls); /** * Lookup all of the instances this backend has configured. @@ -1517,10 +1517,10 @@ struct TALER_MERCHANTDB_Plugin * @param cb_cls closure for @a cb */ enum GNUNET_DB_QueryStatus - (*lookup_instances)(void *cls, - bool active_only, - TALER_MERCHANTDB_InstanceCallback cb, - void *cb_cls); + (*lookup_instances)(void *cls, + bool active_only, + TALER_MERCHANTDB_InstanceCallback cb, + void *cb_cls); /** * Lookup one of the instances this backend has configured. @@ -1532,11 +1532,11 @@ struct TALER_MERCHANTDB_Plugin * @param cb_cls closure for @a cb */ enum GNUNET_DB_QueryStatus - (*lookup_instance)(void *cls, - const char *id, - bool active_only, - TALER_MERCHANTDB_InstanceCallback cb, - void *cb_cls); + (*lookup_instance)(void *cls, + const char *id, + bool active_only, + TALER_MERCHANTDB_InstanceCallback cb, + void *cb_cls); /** * Lookup authentication data of an instance. @@ -1546,9 +1546,9 @@ struct TALER_MERCHANTDB_Plugin * @param[out] ias where to store the auth data */ enum GNUNET_DB_QueryStatus - (*lookup_instance_auth)(void *cls, - const char *instance_id, - struct TALER_MERCHANTDB_InstanceAuthSettings *ias); + (*lookup_instance_auth)(void *cls, + const char *instance_id, + struct TALER_MERCHANTDB_InstanceAuthSettings *ias); /** @@ -1561,11 +1561,11 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_instance)(void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_MerchantPrivateKeyP *merchant_priv, - const struct TALER_MERCHANTDB_InstanceSettings *is, - const struct TALER_MERCHANTDB_InstanceAuthSettings *ias); + (*insert_instance)(void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_MerchantPrivateKeyP *merchant_priv, + const struct TALER_MERCHANTDB_InstanceSettings *is, + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias); /** * Insert information about an instance's account into our database. @@ -1575,7 +1575,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_account)( + (*insert_account)( void *cls, const struct TALER_MERCHANTDB_AccountDetails *account_details); @@ -1592,7 +1592,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_login_token)( + (*insert_login_token)( void *cls, const char *id, const struct TALER_MERCHANTDB_LoginTokenP *token, @@ -1612,7 +1612,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_login_token)( + (*select_login_token)( void *cls, const char *id, const struct TALER_MERCHANTDB_LoginTokenP *token, @@ -1629,7 +1629,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*delete_login_token)( + (*delete_login_token)( void *cls, const char *id, const struct TALER_MERCHANTDB_LoginTokenP *token); @@ -1646,7 +1646,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_account)( + (*update_account)( void *cls, const char *id, const struct TALER_MerchantWireHashP *h_wire, @@ -1664,7 +1664,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_accounts)( + (*select_accounts)( void *cls, const char *id, TALER_MERCHANTDB_AccountCallback cb, @@ -1681,7 +1681,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_account)( + (*select_account)( void *cls, const char *id, const struct TALER_MerchantWireHashP *h_wire, @@ -1698,7 +1698,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_account_by_uri)( + (*select_account_by_uri)( void *cls, const char *id, struct TALER_FullPayto payto_uri, @@ -1713,7 +1713,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*delete_instance_private_key)( + (*delete_instance_private_key)( void *cls, const char *merchant_id); @@ -1726,8 +1726,8 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*purge_instance)(void *cls, - const char *merchant_id); + (*purge_instance)(void *cls, + const char *merchant_id); /** * Update information about an instance into our database. @@ -1737,8 +1737,8 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_instance)(void *cls, - const struct TALER_MERCHANTDB_InstanceSettings *is); + (*update_instance)(void *cls, + const struct TALER_MERCHANTDB_InstanceSettings *is); /** * Update information about an instance's authentication settings @@ -1750,7 +1750,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_instance_auth)( + (*update_instance_auth)( void *cls, const char *merchant_id, const struct TALER_MERCHANTDB_InstanceAuthSettings *ias); @@ -1764,9 +1764,9 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*inactivate_account)(void *cls, - const char *merchant_id, - const struct TALER_MerchantWireHashP *h_wire); + (*inactivate_account)(void *cls, + const char *merchant_id, + const struct TALER_MerchantWireHashP *h_wire); /** @@ -1778,9 +1778,9 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*activate_account)(void *cls, - const char *merchant_id, - const struct TALER_MerchantWireHashP *h_wire); + (*activate_account)(void *cls, + const char *merchant_id, + const struct TALER_MerchantWireHashP *h_wire); /** @@ -1797,7 +1797,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*account_kyc_get_status)( + (*account_kyc_get_status)( void *cls, const char *merchant_id, const struct TALER_MerchantWireHashP *h_wire, @@ -1823,7 +1823,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*get_kyc_status)( + (*get_kyc_status)( void *cls, struct TALER_FullPayto merchant_account_uri, const char *instance_id, @@ -1846,16 +1846,18 @@ struct TALER_MERCHANTDB_Plugin * @param instance_id the instance for which to check * @param exchange_url base URL of the exchange * @param[out] kyc_ok true if no urgent KYC work must be done for this account + * @param[out] no_access_token true if we do not have a valid KYC access token (KYC auth missing) * @param[out] jlimits set to JSON array with AccountLimits, NULL if unknown (and likely defaults apply or KYC auth is urgently needed, see @a auth_ok) * @return database result code */ enum GNUNET_DB_QueryStatus - (*get_kyc_limits)( + (*get_kyc_limits)( void *cls, struct TALER_FullPayto merchant_account_uri, const char *instance_id, const char *exchange_url, bool *kyc_ok, + bool *no_access_token, json_t **jlimits); @@ -1876,7 +1878,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*account_kyc_set_status)( + (*account_kyc_set_status)( void *cls, const char *merchant_id, const struct TALER_MerchantWireHashP *h_wire, @@ -1903,7 +1905,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*account_kyc_set_failed) ( + (*account_kyc_set_failed) ( void *cls, const char *merchant_id, const struct TALER_MerchantWireHashP *h_wire, @@ -1926,12 +1928,12 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_products)(void *cls, - const char *instance_id, - uint64_t offset, - int64_t limit, - TALER_MERCHANTDB_ProductsCallback cb, - void *cb_cls); + (*lookup_products)(void *cls, + const char *instance_id, + uint64_t offset, + int64_t limit, + TALER_MERCHANTDB_ProductsCallback cb, + void *cb_cls); /** @@ -1944,10 +1946,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_all_products)(void *cls, - const char *instance_id, - TALER_MERCHANTDB_ProductCallback cb, - void *cb_cls); + (*lookup_all_products)(void *cls, + const char *instance_id, + TALER_MERCHANTDB_ProductCallback cb, + void *cb_cls); /** * Lookup details about a particular product. @@ -1963,12 +1965,12 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_product)(void *cls, - const char *instance_id, - const char *product_id, - struct TALER_MERCHANTDB_ProductDetails *pd, - size_t *num_categories, - uint64_t **categories); + (*lookup_product)(void *cls, + const char *instance_id, + const char *product_id, + struct TALER_MERCHANTDB_ProductDetails *pd, + size_t *num_categories, + uint64_t **categories); /** * Delete information about a product. Note that the transaction must @@ -1981,9 +1983,9 @@ struct TALER_MERCHANTDB_Plugin * if locks prevent deletion OR product unknown */ enum GNUNET_DB_QueryStatus - (*delete_product)(void *cls, - const char *instance_id, - const char *product_id); + (*delete_product)(void *cls, + const char *instance_id, + const char *product_id); /** * Insert details about a particular product. @@ -2001,15 +2003,15 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_product)(void *cls, - const char *instance_id, - const char *product_id, - const struct TALER_MERCHANTDB_ProductDetails *pd, - size_t num_cats, - const uint64_t *cats, - bool *no_instance, - bool *conflict, - ssize_t *no_cat); + (*insert_product)(void *cls, + const char *instance_id, + const char *product_id, + const struct TALER_MERCHANTDB_ProductDetails *pd, + size_t num_cats, + const uint64_t *cats, + bool *no_instance, + bool *conflict, + ssize_t *no_cat); /** * Update details about a particular product. Note that the @@ -2033,18 +2035,18 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_product)(void *cls, - const char *instance_id, - const char *product_id, - const struct TALER_MERCHANTDB_ProductDetails *pd, - size_t num_cats, - const uint64_t *cats, - bool *no_instance, - ssize_t *no_cat, - bool *no_product, - bool *lost_reduced, - bool *sold_reduced, - bool *stocked_reduced); + (*update_product)(void *cls, + const char *instance_id, + const char *product_id, + const struct TALER_MERCHANTDB_ProductDetails *pd, + size_t num_cats, + const uint64_t *cats, + bool *no_instance, + ssize_t *no_cat, + bool *no_product, + bool *lost_reduced, + bool *sold_reduced, + bool *stocked_reduced); /** * Lock stocks of a particular product. Note that the transaction must @@ -2060,12 +2062,12 @@ struct TALER_MERCHANTDB_Plugin * product is unknown OR if there insufficient stocks remaining */ enum GNUNET_DB_QueryStatus - (*lock_product)(void *cls, - const char *instance_id, - const char *product_id, - const struct GNUNET_Uuid *uuid, - uint64_t quantity, - struct GNUNET_TIME_Timestamp expiration_time); + (*lock_product)(void *cls, + const char *instance_id, + const char *product_id, + const struct GNUNET_Uuid *uuid, + uint64_t quantity, + struct GNUNET_TIME_Timestamp expiration_time); /** @@ -2077,7 +2079,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*expire_locks)(void *cls); + (*expire_locks)(void *cls); /** @@ -2092,10 +2094,10 @@ struct TALER_MERCHANTDB_Plugin * if locks prevent deletion OR order unknown */ enum GNUNET_DB_QueryStatus - (*delete_order)(void *cls, - const char *instance_id, - const char *order_id, - bool force); + (*delete_order)(void *cls, + const char *instance_id, + const char *order_id, + bool force); /** @@ -2112,12 +2114,12 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_order)(void *cls, - const char *instance_id, - const char *order_id, - struct TALER_ClaimTokenP *claim_token, - struct TALER_MerchantPostDataHashP *h_post_data, - json_t **contract_terms); + (*lookup_order)(void *cls, + const char *instance_id, + const char *order_id, + struct TALER_ClaimTokenP *claim_token, + struct TALER_MerchantPostDataHashP *h_post_data, + json_t **contract_terms); /** @@ -2131,11 +2133,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_order_summary)(void *cls, - const char *instance_id, - const char *order_id, - struct GNUNET_TIME_Timestamp *timestamp, - uint64_t *order_serial); + (*lookup_order_summary)(void *cls, + const char *instance_id, + const char *order_id, + struct GNUNET_TIME_Timestamp *timestamp, + uint64_t *order_serial); /** @@ -2149,11 +2151,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_orders)(void *cls, - const char *instance_id, - const struct TALER_MERCHANTDB_OrderFilter *of, - TALER_MERCHANTDB_OrdersCallback cb, - void *cb_cls); + (*lookup_orders)(void *cls, + const char *instance_id, + const struct TALER_MERCHANTDB_OrderFilter *of, + TALER_MERCHANTDB_OrdersCallback cb, + void *cb_cls); /** @@ -2172,16 +2174,16 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_order)(void *cls, - const char *instance_id, - const char *order_id, - const char *session_id, - const struct TALER_MerchantPostDataHashP *h_post_data, - struct GNUNET_TIME_Timestamp pay_deadline, - const struct TALER_ClaimTokenP *claim_token, - const json_t *contract_terms, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm); + (*insert_order)(void *cls, + const char *instance_id, + const char *order_id, + const char *session_id, + const struct TALER_MerchantPostDataHashP *h_post_data, + struct GNUNET_TIME_Timestamp pay_deadline, + const struct TALER_ClaimTokenP *claim_token, + const json_t *contract_terms, + const char *pos_key, + enum TALER_MerchantConfirmationAlgorithm pos_algorithm); /** @@ -2195,8 +2197,8 @@ struct TALER_MERCHANTDB_Plugin * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success */ enum GNUNET_DB_QueryStatus - (*unlock_inventory)(void *cls, - const struct GNUNET_Uuid *uuid); + (*unlock_inventory)(void *cls, + const struct GNUNET_Uuid *uuid); /** @@ -2212,11 +2214,11 @@ struct TALER_MERCHANTDB_Plugin * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success */ enum GNUNET_DB_QueryStatus - (*insert_order_lock)(void *cls, - const char *instance_id, - const char *order_id, - const char *product_id, - uint64_t quantity); + (*insert_order_lock)(void *cls, + const char *instance_id, + const char *order_id, + const char *product_id, + uint64_t quantity); /** @@ -2234,7 +2236,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_contract_terms2)( + (*lookup_contract_terms2)( void *cls, const char *instance_id, const char *order_id, @@ -2263,7 +2265,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_contract_terms3)( + (*lookup_contract_terms3)( void *cls, const char *instance_id, const char *order_id, @@ -2289,7 +2291,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_contract_terms)( + (*lookup_contract_terms)( void *cls, const char *instance_id, const char *order_id, @@ -2315,7 +2317,7 @@ struct TALER_MERCHANTDB_Plugin * is malformed */ enum GNUNET_DB_QueryStatus - (*insert_contract_terms)( + (*insert_contract_terms)( void *cls, const char *instance_id, const char *order_id, @@ -2339,10 +2341,10 @@ struct TALER_MERCHANTDB_Plugin * is malformed */ enum GNUNET_DB_QueryStatus - (*update_contract_terms)(void *cls, - const char *instance_id, - const char *order_id, - json_t *contract_terms); + (*update_contract_terms)(void *cls, + const char *instance_id, + const char *order_id, + json_t *contract_terms); /** @@ -2359,10 +2361,10 @@ struct TALER_MERCHANTDB_Plugin * if locks prevent deletion OR order unknown */ enum GNUNET_DB_QueryStatus - (*delete_contract_terms)(void *cls, - const char *instance_id, - const char *order_id, - struct GNUNET_TIME_Relative legal_expiration); + (*delete_contract_terms)(void *cls, + const char *instance_id, + const char *order_id, + struct GNUNET_TIME_Relative legal_expiration); /** @@ -2377,11 +2379,12 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_deposits)(void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - TALER_MERCHANTDB_DepositsCallback cb, - void *cb_cls); + (*lookup_deposits)(void *cls, + const char *instance_id, + const struct TALER_PrivateContractHashP *h_contract_terms + , + TALER_MERCHANTDB_DepositsCallback cb, + void *cb_cls); /** @@ -2396,7 +2399,7 @@ struct TALER_MERCHANTDB_Plugin * @param master_sig signature of @a master_pub over the @a exchange_pub and the dates */ enum GNUNET_DB_QueryStatus - (*insert_exchange_signkey)( + (*insert_exchange_signkey)( void *cls, const struct TALER_MasterPublicKeyP *master_pub, const struct TALER_ExchangePublicKeyP *exchange_pub, @@ -2424,7 +2427,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_deposit_confirmation)( + (*insert_deposit_confirmation)( void *cls, const char *instance_id, struct GNUNET_TIME_Timestamp deposit_timestamp, @@ -2454,7 +2457,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_deposit)( + (*insert_deposit)( void *cls, uint32_t offset, uint64_t deposit_confirmation_serial_id, @@ -2476,11 +2479,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_refunds)(void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - TALER_MERCHANTDB_RefundCallback rc, - void *rc_cls); + (*lookup_refunds)(void *cls, + const char *instance_id, + const struct TALER_PrivateContractHashP *h_contract_terms, + TALER_MERCHANTDB_RefundCallback rc, + void *rc_cls); /** @@ -2493,10 +2496,10 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_spent_tokens_by_order)(void *cls, - uint64_t order_serial, - TALER_MERCHANTDB_UsedTokensCallback cb, - void *cb_cls); + (*lookup_spent_tokens_by_order)(void *cls, + uint64_t order_serial, + TALER_MERCHANTDB_UsedTokensCallback cb, + void *cb_cls); /** @@ -2511,7 +2514,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*mark_contract_paid)( + (*mark_contract_paid)( void *cls, const char *instance_id, const struct TALER_PrivateContractHashP *h_contract_terms, @@ -2534,12 +2537,12 @@ struct TALER_MERCHANTDB_Plugin * regardless of whether it actually increased the refund */ enum GNUNET_DB_QueryStatus - (*refund_coin)(void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - struct GNUNET_TIME_Timestamp refund_timestamp, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const char *reason); + (*refund_coin)(void *cls, + const char *instance_id, + const struct TALER_PrivateContractHashP *h_contract_terms, + struct GNUNET_TIME_Timestamp refund_timestamp, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const char *reason); /** @@ -2553,11 +2556,11 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_order_status)(void *cls, - const char *instance_id, - const char *order_id, - struct TALER_PrivateContractHashP *h_contract_terms, - bool *paid); + (*lookup_order_status)(void *cls, + const char *instance_id, + const char *order_id, + struct TALER_PrivateContractHashP *h_contract_terms, + bool *paid); /** * Retrieve contract terms given its @a order_serial @@ -2571,13 +2574,13 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_order_status_by_serial)(void *cls, - const char *instance_id, - uint64_t order_serial, - char **order_id, - struct TALER_PrivateContractHashP * - h_contract_terms, - bool *paid); + (*lookup_order_status_by_serial)(void *cls, + const char *instance_id, + uint64_t order_serial, + char **order_id, + struct TALER_PrivateContractHashP * + h_contract_terms, + bool *paid); /** @@ -2590,10 +2593,10 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_deposits_by_order)(void *cls, - uint64_t order_serial, - TALER_MERCHANTDB_DepositedCoinsCallback cb, - void *cb_cls); + (*lookup_deposits_by_order)(void *cls, + uint64_t order_serial, + TALER_MERCHANTDB_DepositedCoinsCallback cb, + void *cb_cls); /** @@ -2607,7 +2610,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_transfer_details_by_order)( + (*lookup_transfer_details_by_order)( void *cls, uint64_t order_serial, TALER_MERCHANTDB_OrderTransferDetailsCallback cb, @@ -2627,7 +2630,7 @@ struct TALER_MERCHANTDB_Plugin * @return database transaction status */ enum GNUNET_DB_QueryStatus - (*update_transfer_status)( + (*update_transfer_status)( void *cls, const char *exchange_url, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -2648,7 +2651,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*select_open_transfers)( + (*select_open_transfers)( void *cls, uint64_t limit, TALER_MERCHANTDB_OpenTransferCallback cb, @@ -2664,9 +2667,9 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_deposit_to_transfer)(void *cls, - uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd); + (*insert_deposit_to_transfer)(void *cls, + uint64_t deposit_serial, + const struct TALER_EXCHANGE_DepositData *dd); /** @@ -2677,8 +2680,8 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*mark_order_wired)(void *cls, - uint64_t order_serial); + (*mark_order_wired)(void *cls, + uint64_t order_serial); /** @@ -2704,7 +2707,7 @@ struct TALER_MERCHANTDB_Plugin * what was already refunded (idempotency!) */ enum TALER_MERCHANTDB_RefundStatus - (*increase_refund)( + (*increase_refund)( void *cls, const char *instance_id, const char *order_id, @@ -2725,7 +2728,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_refunds_detailed)( + (*lookup_refunds_detailed)( void *cls, const char *instance_id, const struct TALER_PrivateContractHashP *h_contract_terms, @@ -2742,7 +2745,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_refund_proof)( + (*insert_refund_proof)( void *cls, uint64_t refund_serial, const struct TALER_ExchangeSignatureP *exchange_sig, @@ -2761,7 +2764,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_spent_token)( + (*insert_spent_token)( void *cls, const struct TALER_PrivateContractHashP * h_contract_terms, @@ -2781,7 +2784,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_issued_token)( + (*insert_issued_token)( void *cls, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_TokenIssuePublicKeyHashP *h_issue_pub, @@ -2798,7 +2801,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_refund_proof)( + (*lookup_refund_proof)( void *cls, uint64_t refund_serial, struct TALER_ExchangeSignatureP *exchange_sig, @@ -2819,7 +2822,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_order_by_fulfillment)( + (*lookup_order_by_fulfillment)( void *cls, const char *instance_id, const char *fulfillment_url, @@ -2837,7 +2840,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*update_wirewatch_progress)( + (*update_wirewatch_progress)( void *cls, const char *instance, struct TALER_FullPayto payto_uri, @@ -2853,7 +2856,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*select_wirewatch_accounts)( + (*select_wirewatch_accounts)( void *cls, TALER_MERCHANTDB_WirewatchWorkCallback cb, void *cb_cls); @@ -2873,7 +2876,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_transfer)( + (*insert_transfer)( void *cls, const char *instance_id, const char *exchange_url, @@ -2894,9 +2897,9 @@ struct TALER_MERCHANTDB_Plugin * if deletion is prohibited OR transfer is unknown */ enum GNUNET_DB_QueryStatus - (*delete_transfer)(void *cls, - const char *instance_id, - uint64_t transfer_serial_id); + (*delete_transfer)(void *cls, + const char *instance_id, + uint64_t transfer_serial_id); /** @@ -2910,9 +2913,9 @@ struct TALER_MERCHANTDB_Plugin * if the transfer record exists */ enum GNUNET_DB_QueryStatus - (*check_transfer_exists)(void *cls, - const char *instance_id, - uint64_t transfer_serial_id); + (*check_transfer_exists)(void *cls, + const char *instance_id, + uint64_t transfer_serial_id); /** @@ -2925,10 +2928,10 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_account)(void *cls, - const char *instance_id, - struct TALER_FullPayto payto_uri, - uint64_t *account_serial); + (*lookup_account)(void *cls, + const char *instance_id, + struct TALER_FullPayto payto_uri, + uint64_t *account_serial); /** @@ -2945,7 +2948,7 @@ struct TALER_MERCHANTDB_Plugin * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success */ enum GNUNET_DB_QueryStatus - (*insert_transfer_details)( + (*insert_transfer_details)( void *cls, const char *instance_id, const char *exchange_url, @@ -2969,7 +2972,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*lookup_wire_fee)( + (*lookup_wire_fee)( void *cls, const struct TALER_MasterPublicKeyP *master_pub, const char *wire_method, @@ -2993,7 +2996,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_deposits_by_contract_and_coin)( + (*lookup_deposits_by_contract_and_coin)( void *cls, const char *instance_id, const struct TALER_PrivateContractHashP *h_contract_terms, @@ -3020,7 +3023,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_transfer)( + (*lookup_transfer)( void *cls, const char *instance_id, const char *exchange_url, @@ -3044,7 +3047,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*set_transfer_status_to_confirmed)( + (*set_transfer_status_to_confirmed)( void *cls, const char *instance_id, const char *exchange_url, @@ -3063,7 +3066,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_transfer_summary)( + (*lookup_transfer_summary)( void *cls, const char *exchange_url, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -3082,7 +3085,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_transfer_details)( + (*lookup_transfer_details)( void *cls, const char *exchange_url, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -3107,7 +3110,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_transfers)( + (*lookup_transfers)( void *cls, const char *instance_id, struct TALER_FullPayto payto_uri, @@ -3134,7 +3137,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*store_wire_fee_by_exchange)( + (*store_wire_fee_by_exchange)( void *cls, const struct TALER_MasterPublicKeyP *master_pub, const struct GNUNET_HashCode *h_wire_method, @@ -3152,7 +3155,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*delete_exchange_accounts)( + (*delete_exchange_accounts)( void *cls, const struct TALER_MasterPublicKeyP *master_pub); @@ -3167,7 +3170,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*select_accounts_by_exchange)( + (*select_accounts_by_exchange)( void *cls, const struct TALER_MasterPublicKeyP *master_pub, TALER_MERCHANTDB_ExchangeAccountCallback cb, @@ -3187,7 +3190,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*insert_exchange_account)( + (*insert_exchange_account)( void *cls, const struct TALER_MasterPublicKeyP *master_pub, const struct TALER_FullPayto payto_uri, @@ -3207,10 +3210,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_templates)(void *cls, - const char *instance_id, - TALER_MERCHANTDB_TemplatesCallback cb, - void *cb_cls); + (*lookup_templates)(void *cls, + const char *instance_id, + TALER_MERCHANTDB_TemplatesCallback cb, + void *cb_cls); /** @@ -3224,10 +3227,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_template)(void *cls, - const char *instance_id, - const char *template_id, - struct TALER_MERCHANTDB_TemplateDetails *td); + (*lookup_template)(void *cls, + const char *instance_id, + const char *template_id, + struct TALER_MERCHANTDB_TemplateDetails *td); /** * Delete information about a template. @@ -3239,9 +3242,9 @@ struct TALER_MERCHANTDB_Plugin * if template unknown. */ enum GNUNET_DB_QueryStatus - (*delete_template)(void *cls, - const char *instance_id, - const char *template_id); + (*delete_template)(void *cls, + const char *instance_id, + const char *template_id); /** @@ -3255,11 +3258,11 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_template)(void *cls, - const char *instance_id, - const char *template_id, - uint64_t otp_serial_id, - const struct TALER_MERCHANTDB_TemplateDetails *td); + (*insert_template)(void *cls, + const char *instance_id, + const char *template_id, + uint64_t otp_serial_id, + const struct TALER_MERCHANTDB_TemplateDetails *td); /** @@ -3274,10 +3277,10 @@ struct TALER_MERCHANTDB_Plugin * does not yet exist. */ enum GNUNET_DB_QueryStatus - (*update_template)(void *cls, - const char *instance_id, - const char *template_id, - const struct TALER_MERCHANTDB_TemplateDetails *td); + (*update_template)(void *cls, + const char *instance_id, + const char *template_id, + const struct TALER_MERCHANTDB_TemplateDetails *td); /** @@ -3290,9 +3293,9 @@ struct TALER_MERCHANTDB_Plugin * if template unknown. */ enum GNUNET_DB_QueryStatus - (*delete_otp)(void *cls, - const char *instance_id, - const char *otp_id); + (*delete_otp)(void *cls, + const char *instance_id, + const char *otp_id); /** * Insert details about a particular OTP device. @@ -3304,10 +3307,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_otp)(void *cls, - const char *instance_id, - const char *otp_id, - const struct TALER_MERCHANTDB_OtpDeviceDetails *td); + (*insert_otp)(void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td); /** @@ -3322,10 +3325,10 @@ struct TALER_MERCHANTDB_Plugin * does not yet exist. */ enum GNUNET_DB_QueryStatus - (*update_otp)(void *cls, - const char *instance_id, - const char *otp_id, - const struct TALER_MERCHANTDB_OtpDeviceDetails *td); + (*update_otp)(void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td); /** * Lookup all of the OTP devices the given instance has configured. @@ -3337,10 +3340,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_otp_devices)(void *cls, - const char *instance_id, - TALER_MERCHANTDB_OtpDeviceCallback cb, - void *cb_cls); + (*lookup_otp_devices)(void *cls, + const char *instance_id, + TALER_MERCHANTDB_OtpDeviceCallback cb, + void *cb_cls); /** @@ -3354,10 +3357,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_otp)(void *cls, - const char *instance_id, - const char *otp_id, - struct TALER_MERCHANTDB_OtpDeviceDetails *td); + (*select_otp)(void *cls, + const char *instance_id, + const char *otp_id, + struct TALER_MERCHANTDB_OtpDeviceDetails *td); /** @@ -3369,10 +3372,10 @@ struct TALER_MERCHANTDB_Plugin * @param[out] serial set to the OTP device serial number * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_otp_serial)(void *cls, - const char *instance_id, - const char *otp_id, - uint64_t *serial); + (*select_otp_serial)(void *cls, + const char *instance_id, + const char *otp_id, + uint64_t *serial); /** @@ -3385,9 +3388,9 @@ struct TALER_MERCHANTDB_Plugin * if template unknown. */ enum GNUNET_DB_QueryStatus - (*delete_category)(void *cls, - const char *instance_id, - uint64_t category_id); + (*delete_category)(void *cls, + const char *instance_id, + uint64_t category_id); /** * Insert new product category. @@ -3400,11 +3403,11 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_category)(void *cls, - const char *instance_id, - const char *category_name, - const json_t *category_name_i18n, - uint64_t *category_id); + (*insert_category)(void *cls, + const char *instance_id, + const char *category_name, + const json_t *category_name_i18n, + uint64_t *category_id); /** @@ -3419,11 +3422,11 @@ struct TALER_MERCHANTDB_Plugin * does not yet exist. */ enum GNUNET_DB_QueryStatus - (*update_category)(void *cls, - const char *instance_id, - uint64_t category_id, - const char *category_name, - const json_t *category_name_i18n); + (*update_category)(void *cls, + const char *instance_id, + uint64_t category_id, + const char *category_name, + const json_t *category_name_i18n); /** * Lookup all of the product categories the given instance has configured. @@ -3435,10 +3438,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_categories)(void *cls, - const char *instance_id, - TALER_MERCHANTDB_CategoriesCallback cb, - void *cb_cls); + (*lookup_categories)(void *cls, + const char *instance_id, + TALER_MERCHANTDB_CategoriesCallback cb, + void *cb_cls); /** @@ -3454,12 +3457,12 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_category)(void *cls, - const char *instance_id, - uint64_t category_id, - struct TALER_MERCHANTDB_CategoryDetails *cd, - size_t *num_products, - char **products); + (*select_category)(void *cls, + const char *instance_id, + uint64_t category_id, + struct TALER_MERCHANTDB_CategoryDetails *cd, + size_t *num_products, + char **products); /** @@ -3473,11 +3476,11 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*select_category_by_name)(void *cls, - const char *instance_id, - const char *category_name, - json_t **name_i18n, - uint64_t *category_id); + (*select_category_by_name)(void *cls, + const char *instance_id, + const char *category_name, + json_t **name_i18n, + uint64_t *category_id); /** @@ -3490,10 +3493,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_webhooks)(void *cls, - const char *instance_id, - TALER_MERCHANTDB_WebhooksCallback cb, - void *cb_cls); + (*lookup_webhooks)(void *cls, + const char *instance_id, + TALER_MERCHANTDB_WebhooksCallback cb, + void *cb_cls); /** @@ -3507,10 +3510,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_webhook)(void *cls, - const char *instance_id, - const char *webhook_id, - struct TALER_MERCHANTDB_WebhookDetails *wb); + (*lookup_webhook)(void *cls, + const char *instance_id, + const char *webhook_id, + struct TALER_MERCHANTDB_WebhookDetails *wb); /** * Delete information about a webhook. @@ -3522,9 +3525,9 @@ struct TALER_MERCHANTDB_Plugin * if webhook unknown. */ enum GNUNET_DB_QueryStatus - (*delete_webhook)(void *cls, - const char *instance_id, - const char *webhook_id); + (*delete_webhook)(void *cls, + const char *instance_id, + const char *webhook_id); /** @@ -3537,10 +3540,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_webhook)(void *cls, - const char *instance_id, - const char *webhook_id, - const struct TALER_MERCHANTDB_WebhookDetails *wb); + (*insert_webhook)(void *cls, + const char *instance_id, + const char *webhook_id, + const struct TALER_MERCHANTDB_WebhookDetails *wb); /** @@ -3555,10 +3558,10 @@ struct TALER_MERCHANTDB_Plugin * does not yet exist. */ enum GNUNET_DB_QueryStatus - (*update_webhook)(void *cls, - const char *instance_id, - const char *webhook_id, - const struct TALER_MERCHANTDB_WebhookDetails *wb); + (*update_webhook)(void *cls, + const char *instance_id, + const char *webhook_id, + const struct TALER_MERCHANTDB_WebhookDetails *wb); /** * Lookup webhook by event @@ -3571,11 +3574,11 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_webhook_by_event)(void *cls, - const char *instance_id, - const char *event_type, - TALER_MERCHANTDB_WebhookDetailCallback cb, - void *cb_cls); + (*lookup_webhook_by_event)(void *cls, + const char *instance_id, + const char *event_type, + TALER_MERCHANTDB_WebhookDetailCallback cb, + void *cb_cls); /** * Insert webhook in the pending webhook. @@ -3590,13 +3593,13 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_pending_webhook)(void *cls, - const char *instance_id, - uint64_t webhook_serial, - const char *url, - const char *http_method, - const char *header, - const char *body); + (*insert_pending_webhook)(void *cls, + const char *instance_id, + uint64_t webhook_serial, + const char *url, + const char *http_method, + const char *header, + const char *body); /** * Lookup the webhook that need to be send in priority. These webhooks are not successfully * send. @@ -3607,9 +3610,9 @@ struct TALER_MERCHANTDB_Plugin */ // WHERE next_attempt <= now ORDER BY next_attempt ASC enum GNUNET_DB_QueryStatus - (*lookup_pending_webhooks)(void *cls, - TALER_MERCHANTDB_PendingWebhooksCallback cb, - void *cb_cls); + (*lookup_pending_webhooks)(void *cls, + TALER_MERCHANTDB_PendingWebhooksCallback cb, + void *cb_cls); /** * Lookup future webhook in the pending webhook that need to be send. @@ -3621,9 +3624,9 @@ struct TALER_MERCHANTDB_Plugin */ // ORDER BY next_attempt ASC LIMIT 1 enum GNUNET_DB_QueryStatus - (*lookup_future_webhook)(void *cls, - TALER_MERCHANTDB_PendingWebhooksCallback cb, - void *cb_cls); + (*lookup_future_webhook)(void *cls, + TALER_MERCHANTDB_PendingWebhooksCallback cb, + void *cb_cls); /** * Lookup all the webhooks in the pending webhook. @@ -3638,12 +3641,12 @@ struct TALER_MERCHANTDB_Plugin */ // WHERE webhook_pending_serial > min_row ORDER BY webhook_pending_serial ASC LIMIT max_results enum GNUNET_DB_QueryStatus - (*lookup_all_webhooks)(void *cls, - const char *instance_id, - uint64_t min_row, - uint32_t max_results, - TALER_MERCHANTDB_PendingWebhooksCallback cb, - void *cb_cls); + (*lookup_all_webhooks)(void *cls, + const char *instance_id, + uint64_t min_row, + uint32_t max_results, + TALER_MERCHANTDB_PendingWebhooksCallback cb, + void *cb_cls); /** @@ -3655,9 +3658,9 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_pending_webhook)(void *cls, - uint64_t webhook_pending_serial, - struct GNUNET_TIME_Absolute next_attempt); + (*update_pending_webhook)(void *cls, + uint64_t webhook_pending_serial, + struct GNUNET_TIME_Absolute next_attempt); // maybe add: http status of failure? @@ -3670,8 +3673,8 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*delete_pending_webhook)(void *cls, - uint64_t webhook_pending_serial); + (*delete_pending_webhook)(void *cls, + uint64_t webhook_pending_serial); /** @@ -3683,9 +3686,9 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*select_exchange_keys)(void *cls, - const char *exchange_url, - struct TALER_EXCHANGE_Keys **keys); + (*select_exchange_keys)(void *cls, + const char *exchange_url, + struct TALER_EXCHANGE_Keys **keys); /** @@ -3696,8 +3699,8 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*insert_exchange_keys)(void *cls, - const struct TALER_EXCHANGE_Keys *keys); + (*insert_exchange_keys)(void *cls, + const struct TALER_EXCHANGE_Keys *keys); /** @@ -3710,10 +3713,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_token_families)(void *cls, - const char *instance_id, - TALER_MERCHANTDB_TokenFamiliesCallback cb, - void *cb_cls); + (*lookup_token_families)(void *cls, + const char *instance_id, + TALER_MERCHANTDB_TokenFamiliesCallback cb, + void *cb_cls); /** * Lookup details about a particular token family. @@ -3726,10 +3729,10 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_token_family)(void *cls, - const char *instance_id, - const char *token_family_slug, - struct TALER_MERCHANTDB_TokenFamilyDetails *details); + (*lookup_token_family)(void *cls, + const char *instance_id, + const char *token_family_slug, + struct TALER_MERCHANTDB_TokenFamilyDetails *details); /** * Delete information about a token family. @@ -3740,9 +3743,9 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*delete_token_family)(void *cls, - const char *instance_id, - const char *token_family_slug); + (*delete_token_family)(void *cls, + const char *instance_id, + const char *token_family_slug); /** * Update details about a particular token family. @@ -3755,7 +3758,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_token_family)( + (*update_token_family)( void *cls, const char *instance_id, const char *token_family_slug, @@ -3772,7 +3775,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_token_family)( + (*insert_token_family)( void *cls, const char *instance_id, const char *token_family_slug, @@ -3792,7 +3795,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_token_family_key)( + (*lookup_token_family_key)( void *cls, const char *instance_id, const char *token_family_slug, @@ -3814,7 +3817,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*lookup_token_family_keys)( + (*lookup_token_family_keys)( void *cls, const char *instance_id, const char *token_family_slug, @@ -3839,7 +3842,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*insert_token_family_key)( + (*insert_token_family_key)( void *cls, const char *merchant_id, const char *token_family_slug, @@ -3861,7 +3864,7 @@ struct TALER_MERCHANTDB_Plugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*lookup_pending_deposits)( + (*lookup_pending_deposits)( void *cls, const char *exchange_url, uint64_t limit, @@ -3883,7 +3886,7 @@ struct TALER_MERCHANTDB_Plugin * @return database result code */ enum GNUNET_DB_QueryStatus - (*update_deposit_confirmation_status)( + (*update_deposit_confirmation_status)( void *cls, uint64_t deposit_serial, bool wire_pending, diff --git a/src/merchant-tools/taler-merchant-benchmark.c b/src/merchant-tools/taler-merchant-benchmark.c @@ -539,12 +539,13 @@ main (int argc, } } if (NULL == exchange_bank_section) - exchange_bank_section = "exchange-account-1"; + exchange_bank_section = GNUNET_strdup ("exchange-account-1"); if (NULL == loglev) - loglev = "INFO"; + loglev = GNUNET_strdup ("INFO"); GNUNET_log_setup ("taler-merchant-benchmark", loglev, logfile); + GNUNET_free (loglev); if ( (! ordinary) && (! corner) ) { diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am @@ -241,7 +241,6 @@ EXTRA_DIST = \ test_merchant_api_twisted-rsa.conf \ test_merchant_api_proxy_merchant.conf \ test_merchant_api_proxy_exchange.conf \ - test_merchant_api_home/.local/share/taler/exchange-offline/master.priv \ test_merchant.priv \ test_template.conf \ $(check_SCRIPTS) diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c @@ -191,6 +191,48 @@ run (void *cls, "create-reserve-1", "EUR:0", MHD_HTTP_OK), + + /* KYC: we don't even know the legitimization UUID yet */ + TALER_TESTING_cmd_merchant_kyc_get ( + "kyc-pending-early", + merchant_url, + NULL, + NULL, + EXCHANGE_URL, + TALER_EXCHANGE_KLPT_NONE, + MHD_HTTP_OK, + true), + /* now we get the legi UUID by running taler-merchant-depositcheck */ + TALER_TESTING_cmd_depositcheck ( + "deposit-check", + CONFIG_FILE), + + TALER_TESTING_cmd_merchant_get_instance ( + "get-default-instance", + merchant_url, + NULL, + MHD_HTTP_OK, + "instance-create-default-setup"), + TALER_TESTING_cmd_admin_add_kycauth ( + "merchant-kyc-auth-transfer", + "EUR:0.01", + &cred.ba, + merchant_payto, + "get-default-instance"), + CMD_EXEC_WIREWATCH ( + "import-kyc-account-withdraw"), + + TALER_TESTING_cmd_merchant_kyc_get ( + "kyc-auth-done-check", + merchant_url, + NULL, /* default instance */ + "instance-create-default-account", /* h_wire_ref: which account to query */ + EXCHANGE_URL, + TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, + MHD_HTTP_OK, + true), + + TALER_TESTING_cmd_merchant_post_orders2 ( "create-proposal-1", cred.cfg, @@ -211,20 +253,7 @@ run (void *cls, MHD_HTTP_OK, "create-proposal-1", NULL), - TALER_TESTING_cmd_merchant_get_instance ( - "get-default-instance", - merchant_url, - NULL, - MHD_HTTP_OK, - "instance-create-default-setup"), - TALER_TESTING_cmd_admin_add_kycauth ( - "merchant-kyc-auth-transfer", - "EUR:0.01", - &cred.ba, - merchant_payto, - "get-default-instance"), - CMD_EXEC_WIREWATCH ( - "import-kyc-account-withdraw"), + TALER_TESTING_cmd_merchant_pay_order ( "deposit-simple", merchant_url, @@ -247,21 +276,6 @@ run (void *cls, /* KYC: hence nothing happened at the bank yet: */ TALER_TESTING_cmd_check_bank_empty ( "check_bank_empty-2"), - /* KYC: we don't even know the legitimization UUID yet */ - TALER_TESTING_cmd_merchant_kyc_get ( - "kyc-pending-early", - merchant_url, - NULL, - NULL, - EXCHANGE_URL, - TALER_EXCHANGE_KLPT_NONE, - MHD_HTTP_OK, - true), - /* now we get the legi UUID by running taler-merchant-depositcheck */ - TALER_TESTING_cmd_depositcheck ( - "deposit-check", - CONFIG_FILE), - /* Now we should get a status of pending */ TALER_TESTING_cmd_merchant_kyc_get ( "kyc-pending", merchant_url, diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf @@ -167,10 +167,15 @@ CONTEXT = {} CHECK_NAME = skip # AML program that freezes the account and flags # it for investigation. -PROGRAM = taler-exchange-helper-measure-freeze +PROGRAM = freeze # Context to provide for check and program; empty. CONTEXT = {} +[aml-program-freeze] +DESCRIPTION = "Freeze the account" +COMMAND = taler-exchange-helper-measure-freeze +FALLBACK = manual-freeze + [kyc-rule-aggregate-any] ENABLED = YES EXPOSED = YES diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c @@ -1714,7 +1714,7 @@ run (void *cls, cred.cfg, merchant_url, MHD_HTTP_OK, - "create-upcoming-tokenfamily", + "subscription-upcoming", 0, 1, "5-upcoming-output", @@ -1740,7 +1740,7 @@ run (void *cls, cred.cfg, merchant_url, MHD_HTTP_OK, - "create-tokenfamily", + "subscription-1", 0, 1, "5-output", @@ -1763,7 +1763,7 @@ run (void *cls, cred.cfg, merchant_url, MHD_HTTP_OK, - "create-tokenfamily", + "subscription-1", 1, 1, "5-input-output", @@ -1796,7 +1796,7 @@ run (void *cls, cred.cfg, merchant_url, MHD_HTTP_OK, - "create-tokenfamily", + "subscription-1", 1, 1, "5-input-output-2", diff --git a/src/testing/test_merchant_api.conf b/src/testing/test_merchant_api.conf @@ -49,11 +49,11 @@ CURRENCY = EUR [exchange] CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 PORT = 8081 MASTER_PUBLIC_KEY = KHA6YSPRQV1ZFCF144SY8KJNR588XA8DA0F6510FKJW30DJFJNAG BASE_URL = "http://localhost:8081/" STEFAN_ABS = "EUR:5" +ENABLE_KYC = NO [exchangedb-postgres] CONFIG = "postgres:///talercheck" diff --git a/src/testing/test_merchant_order_creation.sh b/src/testing/test_merchant_order_creation.sh @@ -266,11 +266,11 @@ fi echo " OK" echo "curl 'http://localhost:9966/private/orders' \ - -d '{\"order\":{\"version\":1,\"amount\":\"TESTKUDOS:7\",\"summary\":\"with_subscription\",\"fulfillment_message\":\"Paid successfully\",\"choices\":[{\"inputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}],\"outputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}]}]}}'" + -d '{\"order\":{\"version\":1,\"summary\":\"with_subscription\",\"fulfillment_message\":\"Paid successfully\",\"choices\":[\{\"amount\":\"TESTKUDOS:7","inputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}],\"outputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}]}]}}'" echo -n "Creating v1 order with token family ..." STATUS=$(curl 'http://localhost:9966/private/orders' \ - -d '{"order":{"version":1,"amount":"TESTKUDOS:7","summary":"with_subscription","fulfillment_message":"Paid successfully","choices":[{"inputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}],"outputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}]}]}}' \ + -d '{"order":{"version":1,"summary":"with_subscription","fulfillment_message":"Paid successfully","choices":[{"amount":"TESTKUDOS:7","inputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}],"outputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}]}]}}' \ -w "%{http_code}" -s -o "$LAST_RESPONSE") if [ "$STATUS" != "200" ] diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c @@ -559,8 +559,6 @@ pay_run (void *cls, struct TALER_MerchantWireHashP h_wire; const struct TALER_PrivateContractHashP *h_proposal; struct TALER_Amount max_fee; - const json_t *choices = NULL; - const json_t *token_families = NULL; const char *error_name = NULL; unsigned int error_line = 0; struct TALER_MERCHANT_PayCoin *pay_coins; @@ -598,7 +596,12 @@ pay_run (void *cls, { /* Get information that needs to be put verbatim in the * deposit permission */ + uint64_t version = 0; struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("version", + &version), + NULL), GNUNET_JSON_spec_string ("order_id", &order_id), GNUNET_JSON_spec_timestamp ("refund_deadline", @@ -611,18 +614,6 @@ pay_run (void *cls, &merchant_pub), GNUNET_JSON_spec_fixed_auto ("h_wire", &h_wire), - TALER_JSON_spec_amount_any ("amount", - &ps->total_amount), - TALER_JSON_spec_amount_any ("max_fee", - &max_fee), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("token_families", - &token_families), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("choices", - &choices), - NULL), /* FIXME oec: parse minimum age, use data later? */ GNUNET_JSON_spec_end () }; @@ -645,6 +636,221 @@ pay_run (void *cls, free (js); TALER_TESTING_FAIL (is); } + switch (version) + { + case 0: + { + struct GNUNET_JSON_Specification v0spec[] = { + TALER_JSON_spec_amount_any ("amount", + &ps->total_amount), + TALER_JSON_spec_amount_any ("max_fee", + &max_fee), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (contract_terms, + v0spec, + &error_name, + &error_line)) + { + char *js; + + js = json_dumps (contract_terms, + JSON_INDENT (1)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Parser failed on %s:%u for input `%s'\n", + error_name, + error_line, + js); + free (js); + TALER_TESTING_FAIL (is); + } + } + if (0 < ps->choice_index) + TALER_TESTING_FAIL (is); + break; + case 1: + { + const json_t *choices; + const json_t *token_families; + struct GNUNET_JSON_Specification v1spec[] = { + GNUNET_JSON_spec_object_const ("token_families", + &token_families), + GNUNET_JSON_spec_array_const ("choices", + &choices), + GNUNET_JSON_spec_end () + }; + const json_t *outputs; + json_t *output; + unsigned int output_index; + const json_t *choice; + + if (GNUNET_OK != + GNUNET_JSON_parse (contract_terms, + v1spec, + &error_name, + &error_line)) + { + char *js; + + js = json_dumps (contract_terms, + JSON_INDENT (1)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Parser failed on %s:%u for input `%s'\n", + error_name, + error_line, + js); + free (js); + TALER_TESTING_FAIL (is); + } + + choice = json_array_get (choices, + ps->choice_index); + if (NULL == choice) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No choice found at index %d\n", + ps->choice_index); + TALER_TESTING_FAIL (is); + } + + { + const char *ierror_name = NULL; + unsigned int ierror_line = 0; + + struct GNUNET_JSON_Specification ispec[] = { + TALER_JSON_spec_amount_any ("amount", + &ps->total_amount), + TALER_JSON_spec_amount_any ("max_fee", + &max_fee), + GNUNET_JSON_spec_array_const ("outputs", + &outputs), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (choice, + ispec, + &ierror_name, + &ierror_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Parser failed on %s:%u for input `%s'\n", + ierror_name, + ierror_line, + json_dumps (choice, + JSON_INDENT (2))); + TALER_TESTING_FAIL (is); + } + } + + json_array_foreach (outputs, output_index, output) + { + const char *slug; + const char *kind; + uint32_t key_index; + uint32_t count = 1; + const char *ierror_name = NULL; + unsigned int ierror_line = 0; + + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("kind", + &kind), + GNUNET_JSON_spec_string ("token_family_slug", + &slug), + GNUNET_JSON_spec_uint32 ("key_index", + &key_index), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint32 ("count", + &count), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (output, + ispec, + &ierror_name, + &ierror_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Parser failed on %s:%u for input `%s'\n", + ierror_name, + ierror_line, + json_dumps (output, + JSON_INDENT (2))); + TALER_TESTING_FAIL (is); + } + + if (0 != strcmp ("token", kind)) + { + continue; + } + + GNUNET_array_grow (ps->issued_tokens, + ps->num_issued_tokens, + ps->num_issued_tokens + count); + + for (unsigned int k = 0; k < count; k++) + { + struct TALER_MERCHANT_PrivateTokenDetails *details = + &ps->issued_tokens[ps->num_issued_tokens - count + k]; + + if (GNUNET_OK != + find_token_public_key (token_families, + slug, + key_index, + &details->issue_pub)) + { + TALER_TESTING_FAIL (is); + } + + /* Only RSA is supported for now. */ + GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == + details->issue_pub.public_key->cipher); + + TALER_token_blind_input_copy (&details->blinding_inputs, + TALER_token_blind_input_rsa_singleton () + ); + /* TODO: Where to get details->blinding_inputs from? */ + TALER_token_use_setup_random (&details->master); + TALER_token_use_setup_priv (&details->master, + &details->blinding_inputs, + &details->token_priv); + TALER_token_use_blinding_secret_create (&details->master, + &details->blinding_inputs, + &details->blinding_secret); + GNUNET_CRYPTO_eddsa_key_get_public (&details->token_priv.private_key + , + &details->token_pub.public_key); + GNUNET_CRYPTO_hash (&details->token_pub.public_key, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &details->h_token_pub.hash); + details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign + ( + details->issue_pub.public_key, + &details->blinding_secret, + NULL, /* TODO: Add session nonce to support CS tokens */ + &details->h_token_pub.hash, + sizeof (details->h_token_pub.hash), + details->blinding_inputs.blinding_inputs); + + if (NULL == details->envelope.blinded_pub) + { + GNUNET_break (0); + TALER_TESTING_FAIL (is); + } + } + } + } + + break; + default: + TALER_TESTING_FAIL (is); + } + + } { @@ -688,143 +894,6 @@ pay_run (void *cls, } GNUNET_free (tr); } - if (0 <= ps->choice_index) - { - const json_t *outputs; - json_t *output; - unsigned int output_index; - const json_t *choice = json_array_get (choices, ps->choice_index); - - if (NULL == choice) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No choice found at index %d\n", - ps->choice_index); - TALER_TESTING_FAIL (is); - } - - { - const char *ierror_name = NULL; - unsigned int ierror_line = 0; - - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_array_const ("outputs", - &outputs), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (choice, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Parser failed on %s:%u for input `%s'\n", - ierror_name, - ierror_line, - json_dumps (choice, - JSON_INDENT (2))); - TALER_TESTING_FAIL (is); - } - } - - json_array_foreach (outputs, output_index, output) - { - const char *slug; - const char *kind; - uint32_t key_index; - uint32_t count = 1; - const char *ierror_name = NULL; - unsigned int ierror_line = 0; - - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - &slug), - GNUNET_JSON_spec_uint32 ("key_index", - &key_index), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &count), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (output, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Parser failed on %s:%u for input `%s'\n", - ierror_name, - ierror_line, - json_dumps (output, - JSON_INDENT (2))); - TALER_TESTING_FAIL (is); - } - - if (0 != strcmp ("token", kind)) - { - continue; - } - - GNUNET_array_grow (ps->issued_tokens, - ps->num_issued_tokens, - ps->num_issued_tokens + count); - - for (unsigned int k = 0; k < count; k++) - { - struct TALER_MERCHANT_PrivateTokenDetails *details = - &ps->issued_tokens[ps->num_issued_tokens - count + k]; - - if (GNUNET_OK != - find_token_public_key (token_families, - slug, - key_index, - &details->issue_pub)) - { - TALER_TESTING_FAIL (is); - } - - /* Only RSA is supported for now. */ - GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == - details->issue_pub.public_key->cipher); - - TALER_token_blind_input_copy (&details->blinding_inputs, - TALER_token_blind_input_rsa_singleton ()); - /* TODO: Where to get details->blinding_inputs from? */ - TALER_token_use_setup_random (&details->master); - TALER_token_use_setup_priv (&details->master, - &details->blinding_inputs, - &details->token_priv); - TALER_token_use_blinding_secret_create (&details->master, - &details->blinding_inputs, - &details->blinding_secret); - GNUNET_CRYPTO_eddsa_key_get_public (&details->token_priv.private_key, - &details->token_pub.public_key); - GNUNET_CRYPTO_hash (&details->token_pub.public_key, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &details->h_token_pub.hash); - details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign ( - details->issue_pub.public_key, - &details->blinding_secret, - NULL, /* TODO: Add session nonce to support CS tokens */ - &details->h_token_pub.hash, - sizeof (details->h_token_pub.hash), - details->blinding_inputs.blinding_inputs); - - if (NULL == details->envelope.blinded_pub) - { - GNUNET_break (0); - TALER_TESTING_FAIL (is); - } - } - } - } GNUNET_array_grow (output_tokens, len_output_tokens, diff --git a/src/testing/testing_api_cmd_post_orders.c b/src/testing/testing_api_cmd_post_orders.c @@ -60,23 +60,6 @@ struct OrdersState const char *expected_order_id; /** - * Reference to a POST /tokenfamilies command. Can be NULL. - */ - const char *token_family_reference; - - /** - * How many tokens of the token family created in - * @a token_family_reference are required as inputs. - */ - unsigned int num_inputs; - - /** - * How many tokens of the token family created in - * @a token_family_reference should be issued as outputs. - */ - unsigned int num_outputs; - - /** * Contract terms obtained from the backend. */ json_t *contract_terms; @@ -87,11 +70,6 @@ struct OrdersState json_t *order_terms; /** - * Choices array with inputs and outputs for v1 order. - */ - json_t *choices; - - /** * Contract terms hash code. */ struct TALER_PrivateContractHashP h_contract_terms; @@ -589,48 +567,6 @@ orders_run2 (void *cls, /** - * Constructs the json for a the choices of an order request. - * - * @param input_slug the name of the token family to use for input, can be NULL - * @param output_slug the name of the token family to use for the output, can be NULL. - * @param input_count number of token inputs to require - * @param output_count number of tokens to output - * @param input_valid_after validity date for the input token. - * @param output_valid_after validity date for the output token. - * @param[out] choices where to write the json string. - */ -static void -make_choices_json ( - const char *input_slug, - const char *output_slug, - uint16_t input_count, - uint16_t output_count, - struct GNUNET_TIME_Timestamp input_valid_after, - struct GNUNET_TIME_Timestamp output_valid_after, - json_t **choices) -{ - /* FIXME: ugly code should return c, use GNUNET_JSON_PACK() for more type-safety */ - json_t *c; - - c = json_pack ("[{s:o, s:o}]", - "inputs", json_pack ("[{s:s, s:i, s:s, s:o}]", - "kind", "token", - "count", input_count, - "token_family_slug", input_slug, - "valid_after", GNUNET_JSON_from_timestamp - (input_valid_after)), - "outputs", json_pack ("[{s:s, s:i, s:s, s:o}]", - "kind", "token", - "count", output_count, - "token_family_slug", output_slug, - "valid_after", GNUNET_JSON_from_timestamp - (output_valid_after))); - - *choices = c; -} - - -/** * Run a "orders" CMD. * * @param cls closure. @@ -644,7 +580,6 @@ orders_run3 (void *cls, { struct OrdersState *ps = cls; struct GNUNET_TIME_Absolute now; - const char *slug; ps->is = is; now = GNUNET_TIME_absolute_get_monotonic (ps->cfg); @@ -663,37 +598,6 @@ orders_run3 (void *cls, GNUNET_free (order_id); } - { - const struct TALER_TESTING_Command *token_family_cmd; - token_family_cmd = - TALER_TESTING_interpreter_lookup_command (is, - ps->token_family_reference); - if (NULL == token_family_cmd) - TALER_TESTING_FAIL (is); - if (GNUNET_OK != - TALER_TESTING_get_trait_token_family_slug (token_family_cmd, - &slug)) - TALER_TESTING_FAIL (is); - } - make_choices_json (slug, slug, - ps->num_inputs, - ps->num_outputs, - GNUNET_TIME_absolute_to_timestamp (now), - GNUNET_TIME_absolute_to_timestamp (now), - &ps->choices); - - GNUNET_assert (0 == - json_object_set_new (ps->order_terms, - "choices", - ps->choices) - ); - GNUNET_assert (0 == - json_object_set_new (ps->order_terms, - "version", - json_integer (1)) - ); - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &ps->nonce, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); @@ -770,7 +674,7 @@ mark_forgettable (void *cls, * @param order_id the name of the order to add, can be NULL. * @param refund_deadline the deadline for refunds on this order. * @param pay_deadline the deadline for payment on this order. - * @param amount the amount this order is for. + * @param amount the amount this order is for, NULL for v1 orders * @param[out] order where to write the json string. */ static void @@ -786,7 +690,7 @@ make_order_json (const char *order_id, /* Include required fields and some dummy objects to test forgetting. */ contract_terms = json_pack ( - "{s:s, s:s?, s:s, s:s, s:o, s:o, s:s, s:[{s:s}, {s:s}, {s:s}]}", + "{s:s, s:s?, s:s?, s:s, s:o, s:o, s:s, s:[{s:s}, {s:s}, {s:s}]}", "summary", "merchant-lib testcase", "order_id", order_id, "amount", amount, @@ -981,7 +885,7 @@ TALER_TESTING_cmd_merchant_post_orders_choices ( const struct GNUNET_CONFIGURATION_Handle *cfg, const char *merchant_url, unsigned int http_status, - const char *token_family_reference, + const char *token_family_slug, unsigned int num_inputs, unsigned int num_outputs, const char *order_id, @@ -990,18 +894,75 @@ TALER_TESTING_cmd_merchant_post_orders_choices ( const char *amount) { struct OrdersState *ps; + struct TALER_Amount brutto; + json_t *choice; + json_t *choices; + json_t *inputs; + json_t *outputs; ps = GNUNET_new (struct OrdersState); ps->cfg = cfg; make_order_json (order_id, refund_deadline, pay_deadline, - amount, + NULL, &ps->order_terms); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (amount, + &brutto)); + inputs = json_array (); + GNUNET_assert (NULL != inputs); + GNUNET_assert (0 == + json_array_append_new ( + inputs, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("kind", + "token"), + GNUNET_JSON_pack_uint64 ("count", + num_inputs), + GNUNET_JSON_pack_string ("token_family_slug", + token_family_slug) + ))); + outputs = json_array (); + GNUNET_assert (NULL != outputs); + GNUNET_assert (0 == + json_array_append_new ( + outputs, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("kind", + "token"), + GNUNET_JSON_pack_uint64 ("count", + num_outputs), + GNUNET_JSON_pack_string ("token_family_slug", + token_family_slug) + ))); + choice + = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("amount", + &brutto), + GNUNET_JSON_pack_array_steal ("inputs", + inputs), + GNUNET_JSON_pack_array_steal ("outputs", + outputs)); + choices = json_array (); + GNUNET_assert (NULL != choices); + GNUNET_assert (0 == + json_array_append_new ( + choices, + choice)); + GNUNET_assert (0 == + json_object_set_new (ps->order_terms, + "choices", + choices) + ); + GNUNET_assert (0 == + json_object_set_new (ps->order_terms, + "version", + json_integer (1)) + ); + + ps->http_status = http_status; - ps->token_family_reference = token_family_reference; - ps->num_inputs = num_inputs; - ps->num_outputs = num_outputs; ps->expected_order_id = order_id; ps->merchant_url = merchant_url; ps->with_claim = true;