commit 3abb7c87e299b22e4265f4940164644565a258eb
parent 5ab70d21b9f6ee7e2a5f1bf5fc977b785b46c8bc
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Tue, 11 Feb 2025 15:22:34 +0100
Merge branch 'master' into dev/bohdan-potuzhnyi/donau-integration
Diffstat:
20 files changed, 791 insertions(+), 1218 deletions(-)
diff --git a/configure.ac b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
#
# This file is part of TALER
-# Copyright (C) 2014-2024 Taler Systems SA
+# Copyright (C) 2014-2025 Taler Systems SA
#
# TALER is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
@@ -18,7 +18,7 @@
# This configure file is in the public domain
AC_PREREQ([2.69])
-AC_INIT([taler-merchant],[0.14.4],[taler-bug@gnunet.org])
+AC_INIT([taler-merchant],[0.14.5],[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/debian/changelog b/debian/changelog
@@ -1,3 +1,9 @@
+taler-merchant (0.14.5) unstable; urgency=low
+
+ * Release version 0.14.5
+
+ -- Christian Grothoff <grothoff@taler.net> Mon, 10 Jan 2025 10:35:43 +0200
+
taler-merchant (0.14.4) unstable; urgency=low
* Release 0.14.4.
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.4
+PROJECT_NUMBER = 0.14.5
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
@@ -26,393 +26,6 @@
#include "taler-merchant-httpd_contract.h"
-enum TALER_MERCHANT_ContractInputType
-TMH_contract_input_type_from_string (const char *str)
-{
- /* For now, only 'token' is the only supported option. */
- if (0 == strcmp ("token", str))
- {
- return TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN;
- }
-
- return TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID;
-}
-
-
-enum TALER_MERCHANT_ContractOutputType
-TMH_contract_output_type_from_string (const char *str)
-{
- /* For now, only 'token' is the only supported option. */
- if (0 == strcmp ("token", str))
- {
- return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN;
- }
- return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID;
-}
-
-
-const char *
-TMH_string_from_contract_input_type (enum TALER_MERCHANT_ContractInputType t)
-{
- switch (t)
- {
- case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
- return "token";
-#if FUTURE
- case TALER_MERCHANT_CONTRACT_INPUT_TYPE_COIN:
- return "coin";
-#endif
- default:
- return "invalid";
- }
-}
-
-
-const char *
-TMH_string_from_contract_output_type (enum TALER_MERCHANT_ContractOutputType t)
-{
- switch (t)
- {
- case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
- return "token";
-#if FUTURE
- case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
- return "coin";
-#endif
- case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
- return "donation_receipt";
- default:
- return "invalid";
- }
-}
-
-
-/**
- * Parse given JSON object to choices array.
- *
- * @param cls closure, pointer to array length
- * @param root the json array representing the choices
- * @param[out] ospec where to write the data
- * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
- */
-static enum GNUNET_GenericReturnValue
-parse_choices (void *cls,
- json_t *root,
- struct GNUNET_JSON_Specification *ospec)
-{
- struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr;
- unsigned int *choices_len = cls;
-
- if (! json_is_array (root))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- GNUNET_array_grow (*choices,
- *choices_len,
- json_array_size (root));
-
- for (unsigned int i = 0; i<*choices_len; i++)
- {
- struct TALER_MERCHANT_ContractChoice *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",
- &joutputs),
- GNUNET_JSON_spec_end ()
- };
- const char *error_name;
- unsigned int error_line;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json_array_get (root, i),
- spec,
- &error_name,
- &error_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to parse %s at %u: %s\n",
- spec[error_line].field,
- error_line,
- error_name);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- {
- const json_t *jinput;
- size_t idx;
- json_array_foreach ((json_t *) jinputs, idx, jinput)
- {
- struct TALER_MERCHANT_ContractInput input = {
- .details.token.count = 1
- };
- const char *kind;
- 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 ()
- };
- const char *ierror_name;
- unsigned int ierror_line;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jinput,
- ispec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to parse %s at %u: %s\n",
- ispec[ierror_line].field,
- ierror_line,
- ierror_name);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- input.type = TMH_contract_input_type_from_string (kind);
-
- if (TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID == input.type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Field 'kind' invalid in input #%u\n",
- (unsigned int) idx);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- if (0 == input.details.token.count)
- {
- /* Ignore inputs with 'number' field set to 0 */
- continue;
- }
-
- GNUNET_array_append (choice->inputs,
- choice->inputs_len,
- input);
- }
- }
-
- {
- const json_t *joutput;
- size_t idx;
- json_array_foreach ((json_t *) joutputs, idx, joutput)
- {
- struct TALER_MERCHANT_ContractOutput output = {
- .details.token.count = 1
- };
- const char *kind;
- uint32_t ki;
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("kind",
- &kind),
- GNUNET_JSON_spec_string ("token_family_slug",
- // FIXME...
- (const char **) &output.details.token.
- token_family_slug),
- GNUNET_JSON_spec_uint32 ("key_index",
- &ki),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint32 ("count",
- &output.details.token.count),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- const char *ierror_name;
- unsigned int ierror_line;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (joutput,
- ispec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to parse %s at %u: %s\n",
- ispec[ierror_line].field,
- ierror_line,
- ierror_name);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- output.details.token.key_index = ki;
- output.type = TMH_contract_output_type_from_string (kind);
-
- if (TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID == output.type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Field 'kind' invalid in output #%u\n",
- (unsigned int) idx);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
-
- if (0 == output.details.token.count)
- {
- /* Ignore outputs with 'number' field set to 0 */
- continue;
- }
-
- GNUNET_array_append (choice->outputs,
- choice->outputs_len,
- output);
- }
- }
- }
- return GNUNET_OK;
-}
-
-
-struct GNUNET_JSON_Specification
-TALER_JSON_spec_choices (const char *name,
- struct TALER_MERCHANT_ContractChoice **choices,
- unsigned int *choices_len)
-{
- struct GNUNET_JSON_Specification ret = {
- .cls = (void *) choices_len,
- .parser = &parse_choices,
- .field = name,
- .ptr = choices,
- };
-
- return ret;
-}
-
-
-/**
- * Parse given JSON object to token families array.
- *
- * @param cls closure, pointer to array length
- * @param root the json object representing the token families. The keys are
- * the token family slugs.
- * @param[out] ospec where to write the data
- * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
- */
-static enum GNUNET_GenericReturnValue
-parse_token_families (void *cls,
- json_t *root,
- struct GNUNET_JSON_Specification *ospec)
-{
- struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
- unsigned int *families_len = cls;
- json_t *jfamily;
- const char *slug;
-
- if (! json_is_object (root))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- json_object_foreach (root, slug, jfamily)
- {
- const json_t *keys;
- struct TALER_MERCHANT_ContractTokenFamily family = {
- .slug = GNUNET_strdup (slug)
- };
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_array_const ("keys",
- &keys),
- GNUNET_JSON_spec_bool ("critical",
- &family.critical),
- GNUNET_JSON_spec_end ()
- };
- const char *error_name;
- unsigned int error_line;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jfamily,
- spec,
- &error_name,
- &error_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to parse %s at %u: %s\n",
- spec[error_line].field,
- error_line,
- error_name);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_array_grow (family.keys,
- family.keys_len,
- json_array_size (keys));
-
- for (unsigned int i = 0; i<family.keys_len; i++)
- {
- struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
- struct GNUNET_JSON_Specification key_spec[] = {
- TALER_JSON_spec_token_pub (
- "public_key",
- &key->pub),
- GNUNET_JSON_spec_timestamp (
- "valid_after",
- &key->valid_after),
- GNUNET_JSON_spec_timestamp (
- "valid_before",
- &key->valid_before),
- GNUNET_JSON_spec_end ()
- };
- const char *ierror_name;
- unsigned int ierror_line;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (json_array_get (keys,
- i),
- key_spec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to parse %s at %u: %s\n",
- key_spec[ierror_line].field,
- ierror_line,
- ierror_name);
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- GNUNET_array_append (*families,
- *families_len,
- family);
- }
- return GNUNET_OK;
-}
-
-
-struct GNUNET_JSON_Specification
-TALER_JSON_spec_token_families (
- const char *name,
- struct TALER_MERCHANT_ContractTokenFamily **families,
- unsigned int *families_len)
-{
- struct GNUNET_JSON_Specification ret = {
- .cls = (void *) families_len,
- .parser = &parse_token_families,
- .field = name,
- .ptr = families,
- };
-
- return ret;
-}
-
-
enum GNUNET_GenericReturnValue
TMH_find_token_family_key (
const char *slug,
diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h
@@ -28,85 +28,6 @@
#include <jansson.h>
-enum TALER_MERCHANT_ContractInputType
-TMH_contract_input_type_from_string (const char *str);
-
-
-enum TALER_MERCHANT_ContractOutputType
-TMH_contract_output_type_from_string (const char *str);
-
-
-const char *
-TMH_string_from_contract_input_type (enum TALER_MERCHANT_ContractInputType t);
-
-
-const char *
-TMH_string_from_contract_output_type (enum TALER_MERCHANT_ContractOutputType t);
-
-/**
- * Serialize @a contract to a JSON object, ready to be stored in the database.
- * The @a contract can be of v0 or v1.
- *
- * @param[in] contract contract struct to serialize
- * @param[in] instance merchant instance for this contract
- * @param[in] exchanges JSON array of exchanges
- * @param[out] out serialized contract as JSON object
- * @return #GNUNET_OK on success
- * #GNUNET_NO if @a contract was not valid
- * #GNUNET_SYSERR on failure
- */
-enum GNUNET_GenericReturnValue
-TMH_serialize_contract (const struct TALER_MERCHANT_Contract *contract,
- const struct TMH_MerchantInstance *instance,
- json_t *exchanges,
- json_t **out);
-
-
-enum GNUNET_GenericReturnValue
-TMH_serialize_contract_v0 (const struct TALER_MERCHANT_Contract *contract,
- const struct TMH_MerchantInstance *instance,
- json_t *exchanges,
- json_t **out);
-
-
-enum GNUNET_GenericReturnValue
-TMH_serialize_contract_v1 (const struct TALER_MERCHANT_Contract *contract,
- const struct TMH_MerchantInstance *instance,
- json_t *exchanges,
- json_t **out);
-
-/**
- * Provide specification to parse given JSON array to an array
- * of contract choices.
- *
- * @param name name of the choices field in the JSON
- * @param[out] choices set to the first element of the array
- * @param[out] choices_len set to the length of the @a choices array
- * @return spec for parsing a choices array
- */
-struct GNUNET_JSON_Specification
-TALER_JSON_spec_choices (
- const char *name,
- struct TALER_MERCHANT_ContractChoice **choices,
- unsigned int *choices_len);
-
-
-/**
- * Provide specification to parse given JSON object to an array
- * of token families.
- *
- * @param name name of the token families field in the JSON
- * @param[out] families set to the first element of the array
- * @param[out] families_len set to the length of the @a families
- * @return spec for parsing a token families object
- */
-struct GNUNET_JSON_Specification
-TALER_JSON_spec_token_families (
- const char *name,
- struct TALER_MERCHANT_ContractTokenFamily **families,
- unsigned int *families_len);
-
-
/**
* Find matching token family in @a families based on @a slug. Then use
* @a valid_after to find the matching public key within it.
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014-2024 Taler Systems SA
+ (C) 2014-2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
@@ -89,7 +89,7 @@ enum PayPhase
/**
* Initial phase where the request is parsed.
*/
- PP_INIT = 0,
+ PP_PARSE_PAY = 0,
/**
* Parse wallet data object from the pay request.
@@ -365,8 +365,6 @@ struct ExchangeGroup
*/
struct PayContext
{
- // FIXME: group more entries by phase that initializes them,
- // like we do for 'validate_tokens'.
/**
* Stored in a DLL.
@@ -379,117 +377,171 @@ struct PayContext
struct PayContext *prev;
/**
- * Array with @e num_exchange exchanges we are depositing
- * coins into.
+ * MHD connection to return to
*/
- struct ExchangeGroup **egs;
+ struct MHD_Connection *connection;
/**
- * Array with @e coins_cnt coins we are despositing.
+ * Details about the client's request.
*/
- struct DepositConfirmation *dc;
+ struct TMH_HandlerContext *hc;
/**
- * Array with @e tokens_cnt input tokens passed to this request.
+ * Transaction ID given in @e root.
*/
- struct TokenUseConfirmation *tokens;
+ const char *order_id;
/**
- * Array with @e output_tokens_cnt signed tokens returned in
- * the response to the wallet.
+ * Response to return, NULL if we don't have one yet.
*/
- struct SignedOutputToken *output_tokens;
+ struct MHD_Response *response;
/**
- * Array with @e token_envelopes_cnt (blinded) token envelopes.
+ * HTTP status code to use for the reply, i.e 200 for "OK".
+ * Special value UINT_MAX is used to indicate hard errors
+ * (no reply, return #MHD_NO).
*/
- struct TokenEnvelope *token_envelopes;
+ unsigned int response_code;
/**
- * MHD connection to return to
+ * Payment processing phase we are in.
*/
- struct MHD_Connection *connection;
+ enum PayPhase phase;
/**
- * Details about the client's request.
+ * #GNUNET_NO if the @e connection was not suspended,
+ * #GNUNET_YES if the @e connection was suspended,
+ * #GNUNET_SYSERR if @e connection was resumed to as
+ * part of #MH_force_pc_resume during shutdown.
*/
- struct TMH_HandlerContext *hc;
+ enum GNUNET_GenericReturnValue suspended;
/**
- * What wire method (of the @e mi) was selected by the wallet?
- * Set in #phase_parse_pay().
+ * Results from the phase_parse_pay()
*/
- struct TMH_WireMethod *wm;
+ struct
+ {
- /**
- * Task called when the (suspended) processing for
- * the /pay request times out.
- * Happens when we don't get a response from the exchange.
- */
- struct GNUNET_SCHEDULER_Task *timeout_task;
+ /**
+ * Array with @e num_exchanges exchanges we are depositing
+ * coins into.
+ */
+ struct ExchangeGroup **egs;
- /**
- * Response to return, NULL if we don't have one yet.
- */
- struct MHD_Response *response;
+ /**
+ * Array with @e coins_cnt coins we are despositing.
+ */
+ struct DepositConfirmation *dc;
- /**
- * Index of selected choice in the @e contract_terms choices array.
- */
- int64_t choice_index;
+ /**
+ * Array with @e tokens_cnt input tokens passed to this request.
+ */
+ struct TokenUseConfirmation *tokens;
- /**
- * Our contract (or NULL if not available).
- */
- json_t *contract_terms_json;
+ /**
+ * Optional session id given in @e root.
+ * NULL if not given.
+ */
+ char *session_id;
+ /**
+ * Wallet data json object from the request. Containing additional
+ * wallet data such as the selected choice_index.
+ */
+ const json_t *wallet_data;
- /**
- * Parsed contract terms, NULL when parsing failed.
- */
- struct TALER_MERCHANT_Contract *contract_terms;
+ /**
+ * Number of coins this payment is made of. Length
+ * of the @e dc array.
+ */
+ size_t coins_cnt;
- /**
- * Wallet data json object from the request. Containing additional
- * wallet data such as the selected choice_index.
- */
- const json_t *wallet_data;
+ /**
+ * Number of input tokens passed to this request. Length
+ * of the @e tokens array.
+ */
+ size_t tokens_cnt;
- /**
- * Hash of the canonicalized wallet data json object.
- */
- struct GNUNET_HashCode h_wallet_data;
+ /**
+ * Number of exchanges involved in the payment. Length
+ * of the @e eg array.
+ */
+ unsigned int num_exchanges;
- /**
- * Output commitment hash calculated from the 'tokens_evs' field of the request.
- */
- struct GNUNET_HashCode h_outputs;
+ } parse_pay;
/**
- * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
+ * Results from the phase_wallet_data()
*/
- void *json_parse_context;
+ struct
+ {
- /**
- * Optional session id given in @e root.
- * NULL if not given.
- */
- char *session_id;
+ /**
+ * Array with @e token_envelopes_cnt (blinded) token envelopes.
+ */
+ struct TokenEnvelope *token_envelopes;
- /**
- * Transaction ID given in @e root.
- */
- const char *order_id;
+ /**
+ * Index of selected choice in the @e contract_terms choices array.
+ */
+ int64_t choice_index;
- /**
- * Serial number of this order in the database (set once we did the lookup).
- */
- uint64_t order_serial;
+ /**
+ * Number of token envelopes passed to this request.
+ * Length of the @e token_envelopes array.
+ */
+ size_t token_envelopes_cnt;
+
+ /**
+ * Hash of the canonicalized wallet data json object.
+ */
+ struct GNUNET_HashCode h_wallet_data;
+
+ } parse_wallet_data;
/**
- * Hashed proposal.
+ * Results from the phase_check_contract()
*/
- struct TALER_PrivateContractHashP h_contract_terms;
+ struct
+ {
+
+ /**
+ * Hashed @e contract_terms.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Our contract (or NULL if not available).
+ */
+ json_t *contract_terms_json;
+
+ /**
+ * Parsed contract terms, NULL when parsing failed.
+ */
+ struct TALER_MERCHANT_Contract *contract_terms;
+
+ /**
+ * What wire method (of the @e mi) was selected by the wallet?
+ * Set in #phase_parse_pay().
+ */
+ struct TMH_WireMethod *wm;
+
+ /**
+ * Set to the POS key, if applicable for this order.
+ */
+ char *pos_key;
+
+ /**
+ * Serial number of this order in the database (set once we did the lookup).
+ */
+ uint64_t order_serial;
+
+ /**
+ * Algorithm chosen for generating the confirmation code.
+ */
+ enum TALER_MerchantConfirmationAlgorithm pos_alg;
+
+ } check_contract;
/**
* Results from the phase_validate_tokens()
@@ -512,119 +564,95 @@ struct PayContext
*/
struct TALER_Amount brutto;
- } validate_tokens;
-
- /**
- * Considering all the coins with the "found_in_db" flag
- * set, what is the total amount we were so far paid on
- * this contract?
- */
- struct TALER_Amount total_paid;
-
- /**
- * Considering all the coins with the "found_in_db" flag
- * set, what is the total amount we had to pay in deposit
- * fees so far on this contract?
- */
- struct TALER_Amount total_fees_paid;
+ /**
+ * Array with @e output_tokens_len signed tokens returned in
+ * the response to the wallet.
+ */
+ struct SignedOutputToken *output_tokens;
- /**
- * Considering all the coins with the "found_in_db" flag
- * set, what is the total amount we already refunded?
- */
- struct TALER_Amount total_refunded;
+ /**
+ * Number of output tokens to return in the response.
+ * Length of the @e output_tokens array.
+ */
+ unsigned int output_tokens_len;
- /**
- * Set to the POS key, if applicable for this order.
- */
- char *pos_key;
+ } validate_tokens;
/**
- * Algorithm chosen for generating the confirmation code.
+ * Results from the phase_execute_pay_transaction()
*/
- enum TALER_MerchantConfirmationAlgorithm pos_alg;
+ struct
+ {
- /**
- * Number of coins this payment is made of. Length
- * of the @e dc array.
- */
- size_t coins_cnt;
+ /**
+ * Considering all the coins with the "found_in_db" flag
+ * set, what is the total amount we were so far paid on
+ * this contract?
+ */
+ struct TALER_Amount total_paid;
- /**
- * Number of input tokens passed to this request. Length
- * of the @e tokens array.
- */
- size_t tokens_cnt;
+ /**
+ * Considering all the coins with the "found_in_db" flag
+ * set, what is the total amount we had to pay in deposit
+ * fees so far on this contract?
+ */
+ struct TALER_Amount total_fees_paid;
- /**
- * Number of token envelopes passed to this request.
- * Length of the @e token_envelopes array.
- */
- size_t token_envelopes_cnt;
+ /**
+ * Considering all the coins with the "found_in_db" flag
+ * set, what is the total amount we already refunded?
+ */
+ struct TALER_Amount total_refunded;
- /**
- * Number of output tokens to return in the response.
- * Length of the @e output_tokens array.
- */
- unsigned int output_tokens_len;
+ /**
+ * Number of coin deposits pending.
+ */
+ unsigned int pending;
- /**
- * Number of exchanges involved in the payment. Length
- * of the @e eg array.
- */
- unsigned int num_exchanges;
+ /**
+ * How often have we retried the 'main' transaction?
+ */
+ unsigned int retry_counter;
- /**
- * How often have we retried the 'main' transaction?
- */
- unsigned int retry_counter;
+ /**
+ * Set to true if the deposit currency of a coin
+ * does not match the contract currency.
+ */
+ bool deposit_currency_mismatch;
- /**
- * Number of batch transactions pending.
- */
- unsigned int pending_at_eg;
+ /**
+ * Set to true if the database contains a (bogus)
+ * refund for a different currency.
+ */
+ bool refund_currency_mismatch;
- /**
- * Number of coin deposits pending.
- */
- unsigned int pending;
+ } pay_transaction;
/**
- * HTTP status code to use for the reply, i.e 200 for "OK".
- * Special value UINT_MAX is used to indicate hard errors
- * (no reply, return #MHD_NO).
+ * Results from the phase_batch_deposits()
*/
- unsigned int response_code;
+ struct
+ {
- /**
- * Payment processing phase we are in.
- */
- enum PayPhase phase;
+ /**
+ * Task called when the (suspended) processing for
+ * the /pay request times out.
+ * Happens when we don't get a response from the exchange.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
- /**
- * #GNUNET_NO if the @e connection was not suspended,
- * #GNUNET_YES if the @e connection was suspended,
- * #GNUNET_SYSERR if @e connection was resumed to as
- * part of #MH_force_pc_resume during shutdown.
- */
- enum GNUNET_GenericReturnValue suspended;
+ /**
+ * Number of batch transactions pending.
+ */
+ unsigned int pending_at_eg;
- /**
- * Set to true if the deposit currency of a coin
- * does not match the contract currency.
- */
- bool deposit_currency_mismatch;
+ /**
+ * Did any exchange deny a deposit for legal reasons?
+ */
+ bool got_451;
- /**
- * Set to true if the database contains a (bogus)
- * refund for a different currency.
- */
- bool refund_currency_mismatch;
+ } batch_deposits;
- /**
- * Did any exchange deny a deposit for legal reasons?
- */
- bool got_451;
};
@@ -646,10 +674,10 @@ TMH_force_pc_resume ()
NULL != pc;
pc = pc->next)
{
- if (NULL != pc->timeout_task)
+ if (NULL != pc->batch_deposits.timeout_task)
{
- GNUNET_SCHEDULER_cancel (pc->timeout_task);
- pc->timeout_task = NULL;
+ GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
+ pc->batch_deposits.timeout_task = NULL;
}
if (GNUNET_YES == pc->suspended)
{
@@ -694,28 +722,28 @@ resume_pay_with_response (struct PayContext *pc,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Resuming /pay handling. HTTP status for our reply is %u.\n",
response_code);
- for (unsigned int i = 0; i<pc->num_exchanges; i++)
+ for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
{
- struct ExchangeGroup *eg = pc->egs[i];
+ struct ExchangeGroup *eg = pc->parse_pay.egs[i];
if (NULL != eg->fo)
{
TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
eg->fo = NULL;
- pc->pending_at_eg--;
+ pc->batch_deposits.pending_at_eg--;
}
if (NULL != eg->bdh)
{
TALER_EXCHANGE_batch_deposit_cancel (eg->bdh);
eg->bdh = NULL;
- pc->pending_at_eg--;
+ pc->batch_deposits.pending_at_eg--;
}
}
- GNUNET_assert (0 == pc->pending_at_eg);
- if (NULL != pc->timeout_task)
+ GNUNET_assert (0 == pc->batch_deposits.pending_at_eg);
+ if (NULL != pc->batch_deposits.timeout_task)
{
- GNUNET_SCHEDULER_cancel (pc->timeout_task);
- pc->timeout_task = NULL;
+ GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
+ pc->batch_deposits.timeout_task = NULL;
}
pc->phase = PP_RETURN_RESPONSE;
pay_resume (pc);
@@ -793,13 +821,13 @@ phase_fail_for_legal_reasons (struct PayContext *pc)
{
json_t *exchanges;
- GNUNET_assert (0 == pc->pending);
- GNUNET_assert (pc->got_451);
+ GNUNET_assert (0 == pc->pay_transaction.pending);
+ GNUNET_assert (pc->batch_deposits.got_451);
exchanges = json_array ();
GNUNET_assert (NULL != exchanges);
- for (unsigned int i = 0; i<pc->num_exchanges; i++)
+ for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
{
- struct ExchangeGroup *eg = pc->egs[i];
+ struct ExchangeGroup *eg = pc->parse_pay.egs[i];
GNUNET_assert (NULL == eg->fo);
GNUNET_assert (NULL == eg->bdh);
@@ -840,9 +868,9 @@ batch_deposit_transaction (const struct ExchangeGroup *eg,
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&total_without_fees));
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
struct TALER_Amount amount_without_fees;
/* might want to group deposits by batch more explicitly ... */
@@ -864,21 +892,21 @@ batch_deposit_transaction (const struct ExchangeGroup *eg,
TMH_db->cls,
pc->hc->instance->settings.id,
dr->details.ok.deposit_timestamp,
- &pc->h_contract_terms,
+ &pc->check_contract.h_contract_terms,
eg->exchange_url,
- pc->contract_terms->wire_deadline,
+ pc->check_contract.contract_terms->wire_deadline,
&total_without_fees,
&eg->wire_fee,
- &pc->wm->h_wire,
+ &pc->check_contract.wm->h_wire,
dr->details.ok.exchange_sig,
dr->details.ok.exchange_pub,
&b_dep_serial);
if (qs <= 0)
return qs; /* Entire batch already known or failure, we're done */
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
/* might want to group deposits by batch more explicitly ... */
if (0 != strcmp (eg->exchange_url,
@@ -927,7 +955,7 @@ handle_batch_deposit_ok (struct ExchangeGroup *eg,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Storing successful payment %s (%s) at instance `%s'\n",
pc->hc->infix,
- GNUNET_h2s (&pc->h_contract_terms.hash),
+ GNUNET_h2s (&pc->check_contract.h_contract_terms.hash),
pc->hc->instance->settings.id);
for (unsigned int r = 0; r<MAX_RETRIES; r++)
{
@@ -983,17 +1011,17 @@ handle_batch_deposit_ok (struct ExchangeGroup *eg,
}
/* Transaction is done, mark affected coins as complete as well. */
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
if (0 != strcmp (eg->exchange_url,
- pc->dc[i].exchange_url))
+ pc->parse_pay.dc[i].exchange_url))
continue;
if (dc->found_in_db)
continue;
dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
- pc->pending--;
+ pc->pay_transaction.pending--;
}
}
@@ -1016,8 +1044,8 @@ notify_kyc_required (const struct ExchangeGroup *eg)
char *extra;
hws = GNUNET_STRINGS_data_to_string_alloc (
- &eg->pc->contract_terms->h_wire,
- sizeof (eg->pc->contract_terms->h_wire));
+ &eg->pc->check_contract.contract_terms->h_wire,
+ sizeof (eg->pc->check_contract.contract_terms->h_wire));
GNUNET_asprintf (&extra,
"%s %s",
hws,
@@ -1046,7 +1074,7 @@ batch_deposit_cb (
struct PayContext *pc = eg->pc;
eg->bdh = NULL;
- pc->pending_at_eg--;
+ pc->batch_deposits.pending_at_eg--;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Batch deposit completed with status %u\n",
dr->hr.http_status);
@@ -1056,7 +1084,7 @@ batch_deposit_cb (
case MHD_HTTP_OK:
handle_batch_deposit_ok (eg,
dr);
- if (0 == pc->pending_at_eg)
+ if (0 == pc->batch_deposits.pending_at_eg)
{
pc->phase = PP_PAY_TRANSACTION;
pay_resume (pc);
@@ -1065,20 +1093,20 @@ batch_deposit_cb (
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
notify_kyc_required (eg);
eg->got_451 = true;
- pc->got_451 = true;
- /* update pc->pending */
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ pc->batch_deposits.got_451 = true;
+ /* update pc->pay_transaction.pending */
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
if (0 != strcmp (eg->exchange_url,
- pc->dc[i].exchange_url))
+ pc->parse_pay.dc[i].exchange_url))
continue;
if (dc->found_in_db)
continue;
- pc->pending--;
+ pc->pay_transaction.pending--;
}
- if (0 == pc->pending_at_eg)
+ if (0 == pc->batch_deposits.pending_at_eg)
{
pc->phase = PP_PAY_TRANSACTION;
pay_resume (pc);
@@ -1172,7 +1200,7 @@ process_pay_with_keys (
struct TALER_Amount max_amount;
eg->fo = NULL;
- pc->pending_at_eg--;
+ pc->batch_deposits.pending_at_eg--;
GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Processing payment with exchange %s\n",
@@ -1204,7 +1232,7 @@ process_pay_with_keys (
TMH_exchange_check_debit (
pc->hc->instance->settings.id,
exchange,
- pc->wm,
+ pc->check_contract.wm,
&max_amount))
{
if (eg->tried_force_keys)
@@ -1234,7 +1262,7 @@ process_pay_with_keys (
if (GNUNET_OK !=
TMH_EXCHANGES_lookup_wire_fee (exchange,
- pc->wm->wire_method,
+ pc->check_contract.wm->wire_method,
&eg->wire_fee))
{
if (eg->tried_force_keys)
@@ -1243,7 +1271,7 @@ process_pay_with_keys (
resume_pay_with_error (
pc,
TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED,
- pc->wm->wire_method);
+ pc->check_contract.wm->wire_method);
return;
}
force_keys (eg);
@@ -1256,14 +1284,14 @@ process_pay_with_keys (
/* Initiate /batch-deposit operation for all coins of
the current exchange (!) */
group_size = 0;
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
bool is_age_restricted_denom = false;
if (0 != strcmp (eg->exchange_url,
- pc->dc[i].exchange_url))
+ pc->parse_pay.dc[i].exchange_url))
continue;
if (dc->found_in_db)
continue;
@@ -1318,7 +1346,7 @@ process_pay_with_keys (
is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
if (is_age_restricted_denom &&
- (0 < pc->contract_terms->minimum_age))
+ (0 < pc->check_contract.contract_terms->minimum_age))
{
/* Minimum age given and restricted coin provided: We need to verify the
* minimum age */
@@ -1342,7 +1370,7 @@ process_pay_with_keys (
if (GNUNET_OK !=
TALER_age_commitment_verify (
&dc->age_commitment,
- pc->contract_terms->minimum_age,
+ pc->check_contract.contract_terms->minimum_age,
&dc->minimum_age_sig))
code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
AGE_FAIL:
@@ -1392,8 +1420,8 @@ AGE_FAIL:
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Group size zero, %u batch transactions remain pending\n",
- pc->pending_at_eg);
- if (0 == pc->pending_at_eg)
+ pc->batch_deposits.pending_at_eg);
+ if (0 == pc->batch_deposits.pending_at_eg)
{
pc->phase = PP_PAY_TRANSACTION;
pay_resume (pc);
@@ -1405,24 +1433,24 @@ AGE_FAIL:
{
struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
struct TALER_EXCHANGE_DepositContractDetail dcd = {
- .wire_deadline = pc->contract_terms->wire_deadline,
- .merchant_payto_uri = pc->wm->payto_uri,
- .wire_salt = pc->wm->wire_salt,
- .h_contract_terms = pc->h_contract_terms,
- .wallet_data_hash = pc->h_wallet_data,
- .wallet_timestamp = pc->contract_terms->timestamp,
+ .wire_deadline = pc->check_contract.contract_terms->wire_deadline,
+ .merchant_payto_uri = pc->check_contract.wm->payto_uri,
+ .wire_salt = pc->check_contract.wm->wire_salt,
+ .h_contract_terms = pc->check_contract.h_contract_terms,
+ .wallet_data_hash = pc->parse_wallet_data.h_wallet_data,
+ .wallet_timestamp = pc->check_contract.contract_terms->timestamp,
.merchant_pub = hc->instance->merchant_pub,
- .refund_deadline = pc->contract_terms->refund_deadline
+ .refund_deadline = pc->check_contract.contract_terms->refund_deadline
};
enum TALER_ErrorCode ec;
size_t off = 0;
- TALER_merchant_contract_sign (&pc->h_contract_terms,
+ TALER_merchant_contract_sign (&pc->check_contract.h_contract_terms,
&pc->hc->instance->merchant_priv,
&dcd.merchant_sig);
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
if (dc->found_in_db)
continue;
@@ -1435,6 +1463,8 @@ AGE_FAIL:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Initiating batch deposit with %u coins\n",
group_size);
+ /* Note: the coin signatures over the wallet_data_hash are
+ checked inside of this call */
eg->bdh = TALER_EXCHANGE_batch_deposit (
TMH_curl_ctx,
eg->exchange_url,
@@ -1460,7 +1490,7 @@ AGE_FAIL:
eg->exchange_url)));
return;
}
- pc->pending_at_eg++;
+ pc->batch_deposits.pending_at_eg++;
if (TMH_force_audit)
TALER_EXCHANGE_batch_deposit_force_dc (eg->bdh);
}
@@ -1488,7 +1518,7 @@ force_keys (struct ExchangeGroup *eg)
eg->exchange_url);
return;
}
- pc->pending_at_eg++;
+ pc->batch_deposits.pending_at_eg++;
}
@@ -1502,7 +1532,7 @@ handle_pay_timeout (void *cls)
{
struct PayContext *pc = cls;
- pc->timeout_task = NULL;
+ pc->batch_deposits.timeout_task = NULL;
GNUNET_assert (GNUNET_YES == pc->suspended);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Resuming pay with error after timeout\n");
@@ -1544,17 +1574,17 @@ get_pay_timeout (unsigned int num_coins)
static void
phase_batch_deposits (struct PayContext *pc)
{
- for (unsigned int i = 0; i<pc->num_exchanges; i++)
+ for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
{
- struct ExchangeGroup *eg = pc->egs[i];
+ struct ExchangeGroup *eg = pc->parse_pay.egs[i];
bool have_coins = false;
- for (size_t j = 0; j<pc->coins_cnt; j++)
+ for (size_t j = 0; j<pc->parse_pay.coins_cnt; j++)
{
- struct DepositConfirmation *dc = &pc->dc[j];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[j];
if (0 != strcmp (eg->exchange_url,
- pc->dc[j].exchange_url))
+ dc->exchange_url))
continue;
if (dc->found_in_db)
continue;
@@ -1582,9 +1612,9 @@ phase_batch_deposits (struct PayContext *pc)
eg->exchange_url));
return;
}
- pc->pending_at_eg++;
+ pc->batch_deposits.pending_at_eg++;
}
- if (0 == pc->pending_at_eg)
+ if (0 == pc->batch_deposits.pending_at_eg)
{
pc->phase = PP_PAY_TRANSACTION;
pay_resume (pc);
@@ -1593,9 +1623,9 @@ phase_batch_deposits (struct PayContext *pc)
/* Suspend while we interact with the exchange */
MHD_suspend_connection (pc->connection);
pc->suspended = GNUNET_YES;
- GNUNET_assert (NULL == pc->timeout_task);
- pc->timeout_task
- = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
+ GNUNET_assert (NULL == pc->batch_deposits.timeout_task);
+ pc->batch_deposits.timeout_task
+ = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->parse_pay.coins_cnt),
&handle_pay_timeout,
pc);
}
@@ -1613,7 +1643,7 @@ build_token_sigs (struct PayContext *pc)
json_t *token_sigs = json_array ();
GNUNET_assert (NULL != token_sigs);
- for (unsigned int i = 0; i < pc->output_tokens_len; i++)
+ for (unsigned int i = 0; i < pc->validate_tokens.output_tokens_len; i++)
{
GNUNET_assert (0 ==
json_array_append_new (
@@ -1621,7 +1651,7 @@ build_token_sigs (struct PayContext *pc)
GNUNET_JSON_PACK (
GNUNET_JSON_pack_blinded_sig (
"blind_sig",
- pc->output_tokens[i].sig.signature)
+ pc->validate_tokens.output_tokens[i].sig.signature)
)));
}
return token_sigs;
@@ -1642,17 +1672,18 @@ phase_success_response (struct PayContext *pc)
/* Sign on our end (as the payment did go through, even if it may
have been refunded already) */
- TALER_merchant_pay_sign (&pc->h_contract_terms,
+ TALER_merchant_pay_sign (&pc->check_contract.h_contract_terms,
&pc->hc->instance->merchant_priv,
&sig);
/* Build the response */
- pos_confirmation = (NULL == pc->pos_key)
+ pos_confirmation = (NULL == pc->check_contract.pos_key)
? NULL
- : TALER_build_pos_confirmation (pc->pos_key,
- pc->pos_alg,
+ : TALER_build_pos_confirmation (pc->check_contract.pos_key,
+ pc->check_contract.pos_alg,
&pc->validate_tokens.brutto,
- pc->contract_terms->timestamp);
- token_sigs = (0 >= pc->output_tokens_len)
+ pc->check_contract.contract_terms->timestamp
+ );
+ token_sigs = (0 >= pc->validate_tokens.output_tokens_len)
? NULL
: build_token_sigs (pc);
pay_end (pc,
@@ -1698,8 +1729,8 @@ phase_payment_notification (struct PayContext *pc)
NULL,
0);
}
- if ( (NULL != pc->session_id) &&
- (NULL != pc->contract_terms->fulfillment_url) )
+ if ( (NULL != pc->parse_pay.session_id) &&
+ (NULL != pc->check_contract.contract_terms->fulfillment_url) )
{
struct TMH_SessionEventP session_eh = {
.header.size = htons (sizeof (session_eh)),
@@ -1709,13 +1740,14 @@ phase_payment_notification (struct PayContext *pc)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Notifying clients about session change to %s for %s\n",
- pc->session_id,
- pc->contract_terms->fulfillment_url);
- GNUNET_CRYPTO_hash (pc->session_id,
- strlen (pc->session_id),
+ pc->parse_pay.session_id,
+ pc->check_contract.contract_terms->fulfillment_url);
+ GNUNET_CRYPTO_hash (pc->parse_pay.session_id,
+ strlen (pc->parse_pay.session_id),
&session_eh.h_session_id);
- GNUNET_CRYPTO_hash (pc->contract_terms->fulfillment_url,
- strlen (pc->contract_terms->fulfillment_url),
+ GNUNET_CRYPTO_hash (pc->check_contract.contract_terms->fulfillment_url,
+ strlen (pc->check_contract.contract_terms->
+ fulfillment_url),
&session_eh.h_fulfillment_url);
TMH_db->event_notify (TMH_db->cls,
&session_eh.header,
@@ -1746,9 +1778,9 @@ check_coin_paid (void *cls,
{
struct PayContext *pc = cls;
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
if (dc->found_in_db)
continue; /* processed earlier, skip "expensive" memcmp() */
@@ -1768,29 +1800,29 @@ check_coin_paid (void *cls,
"Deposit of coin `%s' already in our DB.\n",
TALER_B2S (coin_pub));
if ( (GNUNET_OK !=
- TALER_amount_cmp_currency (&pc->total_paid,
+ TALER_amount_cmp_currency (&pc->pay_transaction.total_paid,
amount_with_fee)) ||
(GNUNET_OK !=
- TALER_amount_cmp_currency (&pc->total_fees_paid,
+ TALER_amount_cmp_currency (&pc->pay_transaction.total_fees_paid,
deposit_fee)) )
{
GNUNET_break_op (0);
- pc->deposit_currency_mismatch = true;
+ pc->pay_transaction.deposit_currency_mismatch = true;
break;
}
GNUNET_assert (0 <=
- TALER_amount_add (&pc->total_paid,
- &pc->total_paid,
+ TALER_amount_add (&pc->pay_transaction.total_paid,
+ &pc->pay_transaction.total_paid,
amount_with_fee));
GNUNET_assert (0 <=
- TALER_amount_add (&pc->total_fees_paid,
- &pc->total_fees_paid,
+ TALER_amount_add (&pc->pay_transaction.total_fees_paid,
+ &pc->pay_transaction.total_fees_paid,
deposit_fee));
dc->deposit_fee = *deposit_fee;
dc->refund_fee = *refund_fee;
dc->cdd.amount = *amount_with_fee;
dc->found_in_db = true;
- pc->pending--;
+ pc->pay_transaction.pending--;
}
}
@@ -1820,25 +1852,25 @@ check_coin_refunded (void *cls,
an abort-pay refund (an unusual but possible case), we need
to make sure that existing refunds are accounted for. */
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
/* Get matching coins from results. */
if (0 != GNUNET_memcmp (coin_pub,
&dc->cdd.coin_pub))
continue;
if (GNUNET_OK !=
- TALER_amount_cmp_currency (&pc->total_refunded,
+ TALER_amount_cmp_currency (&pc->pay_transaction.total_refunded,
refund_amount))
{
GNUNET_break (0);
- pc->refund_currency_mismatch = true;
+ pc->pay_transaction.refund_currency_mismatch = true;
break;
}
GNUNET_assert (0 <=
- TALER_amount_add (&pc->total_refunded,
- &pc->total_refunded,
+ TALER_amount_add (&pc->pay_transaction.total_refunded,
+ &pc->pay_transaction.total_refunded,
refund_amount));
break;
}
@@ -1861,16 +1893,16 @@ check_payment_sufficient (struct PayContext *pc)
struct TALER_Amount total_wire_fee;
struct TALER_Amount total_needed;
- if (0 == pc->coins_cnt)
+ if (0 == pc->parse_pay.coins_cnt)
return TALER_amount_is_zero (&pc->validate_tokens.brutto);
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&total_wire_fee));
- for (unsigned int i = 0; i < pc->num_exchanges; i++)
+ for (unsigned int i = 0; i < pc->parse_pay.num_exchanges; i++)
{
if (GNUNET_OK !=
TALER_amount_cmp_currency (&total_wire_fee,
- &pc->egs[i]->wire_fee))
+ &pc->parse_pay.egs[i]->wire_fee))
{
GNUNET_break_op (0);
pay_end (pc,
@@ -1883,7 +1915,7 @@ check_payment_sufficient (struct PayContext *pc)
if (0 >
TALER_amount_add (&total_wire_fee,
&total_wire_fee,
- &pc->egs[i]->wire_fee))
+ &pc->parse_pay.egs[i]->wire_fee))
{
GNUNET_break (0);
pay_end (pc,
@@ -1906,9 +1938,9 @@ check_payment_sufficient (struct PayContext *pc)
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&acc_amount));
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
GNUNET_assert (dc->found_in_db);
if ( (GNUNET_OK !=
@@ -1975,7 +2007,7 @@ check_payment_sufficient (struct PayContext *pc)
TALER_amount2s (&pc->validate_tokens.max_fee));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Total refunded amount: %s\n",
- TALER_amount2s (&pc->total_refunded));
+ TALER_amount2s (&pc->pay_transaction.total_refunded));
/* Now compare exchange wire fee compared to
* what we are willing to pay */
@@ -2049,11 +2081,11 @@ check_payment_sufficient (struct PayContext *pc)
/* Do not count refunds towards the payment */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Subtracting total refunds from paid amount: %s\n",
- TALER_amount2s (&pc->total_refunded));
+ TALER_amount2s (&pc->pay_transaction.total_refunded));
if (0 >
TALER_amount_subtract (&final_amount,
&acc_amount,
- &pc->total_refunded))
+ &pc->pay_transaction.total_refunded))
{
GNUNET_break (0);
pay_end (pc,
@@ -2119,13 +2151,13 @@ phase_execute_pay_transaction (struct PayContext *pc)
struct TMH_HandlerContext *hc = pc->hc;
const char *instance_id = hc->instance->settings.id;
- if (pc->got_451)
+ if (pc->batch_deposits.got_451)
{
pc->phase = PP_FAIL_LEGAL_REASONS;
return;
}
/* Avoid re-trying transactions on soft errors forever! */
- if (pc->retry_counter++ > MAX_RETRIES)
+ if (pc->pay_transaction.retry_counter++ > MAX_RETRIES)
{
GNUNET_break (0);
pay_end (pc,
@@ -2141,16 +2173,16 @@ phase_execute_pay_transaction (struct PayContext *pc)
and check_payment_sufficient()). */
GNUNET_break (GNUNET_OK ==
TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
- &pc->total_paid));
+ &pc->pay_transaction.total_paid));
GNUNET_break (GNUNET_OK ==
TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
- &pc->total_fees_paid));
+ &pc->pay_transaction.total_fees_paid));
GNUNET_break (GNUNET_OK ==
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;
- pc->pending = pc->coins_cnt;
+ &pc->pay_transaction.total_refunded));
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
+ pc->parse_pay.dc[i].found_in_db = false;
+ pc->pay_transaction.pending = pc->parse_pay.coins_cnt;
/* First, try to see if we have all we need already done */
TMH_db->preflight (TMH_db->cls);
@@ -2167,16 +2199,15 @@ phase_execute_pay_transaction (struct PayContext *pc)
return;
}
- for (size_t i = 0; i<pc->tokens_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++)
{
- struct TokenUseConfirmation *tuc = &pc->tokens[i];
-
+ struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
enum GNUNET_DB_QueryStatus qs;
/* Insert used token into database, the unique constraint will
case an error if this token was used before. */
qs = TMH_db->insert_spent_token (TMH_db->cls,
- &pc->h_contract_terms,
+ &pc->check_contract.h_contract_terms,
&tuc->h_issue,
&tuc->pub,
&tuc->sig,
@@ -2215,7 +2246,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
/* Check if some of these coins already succeeded for _this_ contract. */
qs = TMH_db->lookup_deposits (TMH_db->cls,
instance_id,
- &pc->h_contract_terms,
+ &pc->check_contract.h_contract_terms,
&check_coin_paid,
pc);
if (0 > qs)
@@ -2232,7 +2263,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
"lookup deposits"));
return;
}
- if (pc->deposit_currency_mismatch)
+ if (pc->pay_transaction.deposit_currency_mismatch)
{
TMH_db->rollback (TMH_db->cls);
GNUNET_break_op (0);
@@ -2252,7 +2283,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
/* Check if we refunded some of the coins */
qs = TMH_db->lookup_refunds (TMH_db->cls,
instance_id,
- &pc->h_contract_terms,
+ &pc->check_contract.h_contract_terms,
&check_coin_refunded,
pc);
if (0 > qs)
@@ -2269,7 +2300,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
"lookup refunds"));
return;
}
- if (pc->refund_currency_mismatch)
+ if (pc->pay_transaction.refund_currency_mismatch)
{
TMH_db->rollback (TMH_db->cls);
pay_end (pc,
@@ -2282,7 +2313,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
}
/* Check if there are coins that still need to be processed */
- if (0 != pc->pending)
+ if (0 != pc->pay_transaction.pending)
{
/* we made no DB changes, so we can just rollback */
TMH_db->rollback (TMH_db->cls);
@@ -2293,7 +2324,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
return;
}
- /* 0 == pc->pending: all coins processed, let's see if that was enough */
+ /* 0 == pc->pay_transaction.pending: all coins processed, let's see if that was enough */
if (! check_payment_sufficient (pc))
{
/* check_payment_sufficient() will have queued an error already.
@@ -2305,14 +2336,14 @@ phase_execute_pay_transaction (struct PayContext *pc)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Order `%s' (%s) was fully paid\n",
pc->order_id,
- GNUNET_h2s (&pc->h_contract_terms.hash));
+ GNUNET_h2s (&pc->check_contract.h_contract_terms.hash));
{
enum GNUNET_DB_QueryStatus qs;
qs = TMH_db->mark_contract_paid (TMH_db->cls,
instance_id,
- &pc->h_contract_terms,
- pc->session_id);
+ &pc->check_contract.h_contract_terms,
+ pc->parse_pay.session_id);
if (qs < 0)
{
TMH_db->rollback (TMH_db->cls);
@@ -2329,14 +2360,14 @@ phase_execute_pay_transaction (struct PayContext *pc)
}
/* Store signed output tokens in database. */
- for (size_t i = 0; i<pc->output_tokens_len; i++)
+ for (size_t i = 0; i<pc->validate_tokens.output_tokens_len; i++)
{
- struct SignedOutputToken *output = &pc->output_tokens[i];
+ struct SignedOutputToken *output = &pc->validate_tokens.output_tokens[i];
enum GNUNET_DB_QueryStatus qs;
qs = TMH_db->insert_issued_token (TMH_db->cls,
- &pc->h_contract_terms,
+ &pc->check_contract.h_contract_terms,
&output->h_issue,
&output->sig);
@@ -2358,15 +2389,15 @@ phase_execute_pay_transaction (struct PayContext *pc)
TMH_notify_order_change (hc->instance,
TMH_OSF_CLAIMED | TMH_OSF_PAID,
- pc->contract_terms->timestamp,
- pc->order_serial);
+ pc->check_contract.contract_terms->timestamp,
+ pc->check_contract.order_serial);
{
enum GNUNET_DB_QueryStatus qs;
json_t *jhook;
jhook = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_incref ("contract_terms",
- pc->contract_terms_json),
+ pc->check_contract.contract_terms_json),
GNUNET_JSON_pack_string ("order_id",
pc->order_id)
);
@@ -2436,7 +2467,7 @@ find_valid_input_tokens (
for (unsigned int j = 0; j < expected_num; j++)
{
- struct TokenUseConfirmation *tuc = &pc->tokens[index + j];
+ struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[index + j];
const struct TALER_MERCHANT_ContractTokenFamilyKey *key = NULL;
for (unsigned int i=0; i<family->keys_len; i++)
@@ -2449,7 +2480,6 @@ find_valid_input_tokens (
{
continue; /* ki currently not valid */
}
- // FIXME: tuc->h_issue.hash is NOT initialized here!
if (0 ==
GNUNET_memcmp (&ki->pub.public_key->pub_key_hash,
&tuc->h_issue.hash))
@@ -2491,8 +2521,8 @@ find_valid_input_tokens (
}
if (GNUNET_OK !=
- TALER_wallet_token_use_verify (&pc->h_contract_terms,
- &pc->h_wallet_data,
+ TALER_wallet_token_use_verify (&pc->check_contract.h_contract_terms,
+ &pc->parse_wallet_data.h_wallet_data,
&tuc->pub,
&tuc->sig))
{
@@ -2535,12 +2565,35 @@ find_valid_input_tokens (
/**
+ * Check if an output token of the given @a tfk is mandatory, or if
+ * wallets are allowed to simply not support it and still proceed.
+ *
+ * @param tfk token family kind to check
+ * @return true if such outputs are mandatory and wallets must supply
+ * the corresponding blinded input
+ */
+static bool
+test_tfk_mandatory (enum TALER_MERCHANTDB_TokenFamilyKind tfk)
+{
+ switch (tfk)
+ {
+ case TALER_MERCHANTDB_TFK_Discount:
+ return false;
+ case TALER_MERCHANTDB_TFK_Subscription:
+ return true;
+ }
+ GNUNET_break (0);
+ return false;
+}
+
+
+/**
* Sign the tokens provided by the wallet for a particular @a key.
*
* @param[in,out] payment we are processing
* @param key token family data
* @param priv private key to use to sign with
- * @param critical true if the token must exist, if false
+ * @param mandatory true if the token must exist, if false
* and the client did not provide an envelope, that's OK and
* we just also skimp on the signature
* @param index offset in the token envelope array (from other families)
@@ -2552,7 +2605,7 @@ static enum GNUNET_GenericReturnValue
sign_token_envelopes (struct PayContext *pc,
struct TALER_MERCHANT_ContractTokenFamilyKey *key,
struct TALER_TokenIssuePrivateKey *priv,
- bool critical,
+ bool mandatory,
unsigned int index,
unsigned int expected_num)
{
@@ -2561,28 +2614,30 @@ sign_token_envelopes (struct PayContext *pc,
for (unsigned int j = 0; j<expected_num; j++)
{
unsigned int pos = index + j;
- const struct TokenEnvelope *env = &pc->token_envelopes[pos];
- struct SignedOutputToken *output = &pc->output_tokens[pos];
+ const struct TokenEnvelope *env
+ = &pc->parse_wallet_data.token_envelopes[pos];
+ struct SignedOutputToken *output
+ = &pc->validate_tokens.output_tokens[pos];
- if ( (pos >= pc->token_envelopes_cnt) ||
- (pos >= pc->output_tokens_len) )
+ if ( (pos >= pc->parse_wallet_data.token_envelopes_cnt) ||
+ (pos >= pc->validate_tokens.output_tokens_len) )
{
GNUNET_assert (0); /* this should not happen */
return GNUNET_NO;
}
if (NULL == env->blinded_token.blinded_pub)
{
- if (! critical)
+ if (! mandatory)
continue;
- /* critical token families require a token envelope. */
+ /* mandatory token families require a token envelope. */
GNUNET_break_op (0);
pay_end (pc,
TALER_MHD_reply_with_error (
pc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "Token envelope for critical token family missing"));
+ "Token envelope for mandatory token family missing"));
return GNUNET_NO;
}
TALER_token_issue_sign (priv,
@@ -2627,11 +2682,11 @@ find_family (const struct PayContext *pc,
const char *slug)
{
for (unsigned int i = 0;
- i < pc->contract_terms->details.v1.token_authorities_len;
+ i < pc->check_contract.contract_terms->details.v1.token_authorities_len;
i++)
{
const struct TALER_MERCHANT_ContractTokenFamily *tfi
- = &pc->contract_terms->details.v1.token_authorities[i];
+ = &pc->check_contract.contract_terms->details.v1.token_authorities[i];
if (0 == strcmp (tfi->slug,
slug))
@@ -2658,18 +2713,21 @@ find_family (const struct PayContext *pc,
static void
phase_validate_tokens (struct PayContext *pc)
{
- switch (pc->contract_terms->version)
+ switch (pc->check_contract.contract_terms->version)
{
case TALER_MERCHANT_CONTRACT_VERSION_0:
/* No tokens to validate */
pc->phase = PP_PAY_TRANSACTION;
- pc->validate_tokens.max_fee = pc->contract_terms->details.v0.max_fee;
- pc->validate_tokens.brutto = pc->contract_terms->details.v0.brutto;
+ pc->validate_tokens.max_fee
+ = pc->check_contract.contract_terms->details.v0.max_fee;
+ pc->validate_tokens.brutto
+ = pc->check_contract.contract_terms->details.v0.brutto;
break;
case TALER_MERCHANT_CONTRACT_VERSION_1:
{
const struct TALER_MERCHANT_ContractChoice *selected
- = &pc->contract_terms->details.v1.choices[pc->choice_index];
+ = &pc->check_contract.contract_terms->details.v1.choices[
+ pc->parse_wallet_data.choice_index];
pc->validate_tokens.max_fee = selected->max_fee;
pc->validate_tokens.brutto = selected->amount;
@@ -2712,8 +2770,8 @@ phase_validate_tokens (struct PayContext *pc)
}
}
- GNUNET_array_grow (pc->output_tokens,
- pc->output_tokens_len,
+ GNUNET_array_grow (pc->validate_tokens.output_tokens,
+ pc->validate_tokens.output_tokens_len,
selected->outputs_len);
for (unsigned int i = 0; i<selected->outputs_len; i++)
@@ -2764,8 +2822,8 @@ phase_validate_tokens (struct PayContext *pc)
TMH_db->cls,
pc->hc->instance->settings.id,
family->slug,
- pc->contract_terms->timestamp,
- pc->contract_terms->pay_deadline,
+ pc->check_contract.contract_terms->timestamp,
+ pc->check_contract.contract_terms->pay_deadline,
&details);
if (qs <= 0)
{
@@ -2773,9 +2831,11 @@ phase_validate_tokens (struct PayContext *pc)
GNUNET_ERROR_TYPE_ERROR,
"Did not find key for %s at [%llu,%llu]\n",
family->slug,
- (unsigned long long) pc->contract_terms->timestamp.abs_time.
+ (unsigned long long) pc->check_contract.contract_terms->timestamp.
+ abs_time.
abs_value_us,
- (unsigned long long) pc->contract_terms->pay_deadline.abs_time.
+ (unsigned long long) pc->check_contract.contract_terms->pay_deadline
+ .abs_time.
abs_value_us);
GNUNET_break (0);
pay_end (pc,
@@ -2788,16 +2848,14 @@ phase_validate_tokens (struct PayContext *pc)
}
GNUNET_assert (NULL != details.priv.private_key);
-
if (GNUNET_OK !=
- sign_token_envelopes (pc,
- key,
- &details.priv,
- /* FIXME: Use critical field stored in database here instead. */
- details.token_family.kind ==
- TALER_MERCHANTDB_TFK_Subscription,
- i,
- output->details.token.count))
+ sign_token_envelopes (
+ pc,
+ key,
+ &details.priv,
+ test_tfk_mandatory (details.token_family.kind),
+ i,
+ output->details.token.count))
{
/* Error is already scheduled from sign_token_envelopes. */
return;
@@ -2806,9 +2864,9 @@ phase_validate_tokens (struct PayContext *pc)
}
}
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- const struct DepositConfirmation *dc = &pc->dc[i];
+ const struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
if (GNUNET_OK !=
TALER_amount_cmp_currency (&dc->cdd.amount,
@@ -2817,7 +2875,7 @@ phase_validate_tokens (struct PayContext *pc)
GNUNET_break_op (0);
fprintf (stderr,
"HERE (%u): %s != %s\n",
- (unsigned int) pc->contract_terms->version,
+ (unsigned int) pc->check_contract.contract_terms->version,
dc->cdd.amount.currency,
TALER_amount2s (&pc->validate_tokens.brutto));
pay_end (pc,
@@ -2860,9 +2918,9 @@ deposit_paid_check (
{
struct PayContext *pc = cls;
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dci = &pc->dc[i];
+ struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
if ( (0 ==
GNUNET_memcmp (&dci->cdd.coin_pub,
@@ -2896,9 +2954,9 @@ input_tokens_paid_check (
{
struct PayContext *pc = cls;
- for (size_t i = 0; i<pc->tokens_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++)
{
- struct TokenUseConfirmation *tuc = &pc->tokens[i];
+ struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
if ( (0 ==
GNUNET_memcmp (&tuc->pub, use_pub)) &&
@@ -2930,7 +2988,7 @@ phase_contract_paid (struct PayContext *pc)
enum GNUNET_DB_QueryStatus qs;
qs = TMH_db->lookup_deposits_by_order (TMH_db->cls,
- pc->order_serial,
+ pc->check_contract.order_serial,
&deposit_paid_check,
pc);
/* Since orders with choices can have a price of zero,
@@ -2947,9 +3005,9 @@ phase_contract_paid (struct PayContext *pc)
return;
}
}
- for (size_t i = 0; i<pc->coins_cnt && ! unmatched; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt && ! unmatched; i++)
{
- struct DepositConfirmation *dci = &pc->dc[i];
+ struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
if (! dci->matched_in_db)
unmatched = true;
@@ -2958,9 +3016,9 @@ phase_contract_paid (struct PayContext *pc)
{
enum GNUNET_DB_QueryStatus qs;
- /* FIXME: Use h_contract instead of order_serial here? */
+ /* FIXME-Optimization: Maybe use h_contract instead of order_serial here? */
qs = TMH_db->lookup_spent_tokens_by_order (TMH_db->cls,
- pc->order_serial,
+ pc->check_contract.order_serial,
&input_tokens_paid_check,
pc);
@@ -2976,50 +3034,39 @@ phase_contract_paid (struct PayContext *pc)
return;
}
}
- for (size_t i = 0; i<pc->tokens_cnt && ! unmatched; i++)
+ for (size_t i = 0; i<pc->parse_pay.tokens_cnt && ! unmatched; i++)
{
- struct TokenUseConfirmation *tuc = &pc->tokens[i];
+ struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
if (! tuc->found_in_db)
unmatched = true;
}
if (! unmatched)
{
- /* Everything fine, idempotent request */
- struct TALER_MerchantSignatureP sig;
-
+ /* Everything fine, idempotent request, generate response immediately */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Idempotent pay request for order `%s', signing again\n",
pc->order_id);
- TALER_merchant_pay_sign (&pc->h_contract_terms,
- &pc->hc->instance->merchant_priv,
- &sig);
- /* FIXME: Add token_sigs to response body. */
- pay_end (pc,
- TALER_MHD_REPLY_JSON_PACK (
- pc->connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_data_auto ("sig",
- &sig)));
+ pc->phase = PP_SUCCESS_RESPONSE;
return;
}
/* Conflict, double-payment detected! */
- /* FIXME: What should we do with input tokens?
+ /* FIXME-#8674: What should we do with input tokens?
Currently there is no refund for tokens. */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Client attempted to pay extra for already paid order `%s'\n",
pc->order_id);
refunds = json_array ();
GNUNET_assert (NULL != refunds);
- for (size_t i = 0; i<pc->coins_cnt; i++)
+ for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dci = &pc->dc[i];
+ struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
struct TALER_MerchantSignatureP merchant_sig;
if (dci->matched_in_db)
continue;
TALER_merchant_refund_sign (&dci->cdd.coin_pub,
- &pc->h_contract_terms,
+ &pc->check_contract.h_contract_terms,
0, /* rtransaction id */
&dci->cdd.amount,
&pc->hc->instance->merchant_priv,
@@ -3064,27 +3111,25 @@ phase_check_contract (struct PayContext *pc)
enum GNUNET_DB_QueryStatus qs;
bool paid = false;
- if (NULL != pc->contract_terms_json)
+ if (NULL != pc->check_contract.contract_terms_json)
{
- json_decref (pc->contract_terms_json);
- pc->contract_terms_json = NULL;
+ json_decref (pc->check_contract.contract_terms_json);
+ pc->check_contract.contract_terms_json = NULL;
}
-
- if (NULL != pc->contract_terms)
+ if (NULL != pc->check_contract.contract_terms)
{
- TALER_MERCHANT_contract_free (pc->contract_terms);
- pc->contract_terms = NULL;
+ TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
+ pc->check_contract.contract_terms = NULL;
}
-
qs = TMH_db->lookup_contract_terms2 (TMH_db->cls,
pc->hc->instance->settings.id,
pc->order_id,
- &pc->contract_terms_json,
- &pc->order_serial,
+ &pc->check_contract.contract_terms_json,
+ &pc->check_contract.order_serial,
&paid,
NULL,
- &pc->pos_key,
- &pc->pos_alg);
+ &pc->check_contract.pos_key,
+ &pc->check_contract.pos_alg);
if (0 > qs)
{
/* single, read-only SQL statements should never cause
@@ -3111,12 +3156,14 @@ phase_check_contract (struct PayContext *pc)
return;
}
/* hash contract (needed later) */
- json_dumpf (pc->contract_terms_json,
+#if DEBUG
+ json_dumpf (pc->check_contract.contract_terms_json,
stderr,
JSON_INDENT (2));
+#endif
if (GNUNET_OK !=
- TALER_JSON_contract_hash (pc->contract_terms_json,
- &pc->h_contract_terms))
+ TALER_JSON_contract_hash (pc->check_contract.contract_terms_json,
+ &pc->check_contract.h_contract_terms))
{
GNUNET_break (0);
pay_end (pc,
@@ -3138,13 +3185,13 @@ phase_check_contract (struct PayContext *pc)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling payment for order `%s' with contract hash `%s'\n",
pc->order_id,
- GNUNET_h2s (&pc->h_contract_terms.hash));
+ GNUNET_h2s (&pc->check_contract.h_contract_terms.hash));
- pc->contract_terms = TALER_MERCHANT_contract_parse (
- pc->contract_terms_json,
+ pc->check_contract.contract_terms = TALER_MERCHANT_contract_parse (
+ pc->check_contract.contract_terms_json,
true);
- if (NULL == pc->contract_terms)
+ if (NULL == pc->check_contract.contract_terms)
{
/* invalid contract */
GNUNET_break (0);
@@ -3159,11 +3206,11 @@ phase_check_contract (struct PayContext *pc)
/* Get details from contract and check fundamentals */
{
- switch (pc->contract_terms->version)
+ switch (pc->check_contract.contract_terms->version)
{
case TALER_MERCHANT_CONTRACT_VERSION_0:
{
- if (pc->choice_index > 0)
+ if (pc->parse_wallet_data.choice_index > 0)
{
GNUNET_break (0);
pay_end (pc,
@@ -3178,7 +3225,7 @@ phase_check_contract (struct PayContext *pc)
break;
case TALER_MERCHANT_CONTRACT_VERSION_1:
{
- if (pc->choice_index < 0)
+ if (pc->parse_wallet_data.choice_index < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Order `%s' has non-empty choices array but"
@@ -3193,14 +3240,15 @@ phase_check_contract (struct PayContext *pc)
NULL));
return;
}
- if (pc->choice_index >= pc->contract_terms->details.v1.choices_len)
+ if (pc->parse_wallet_data.choice_index >=
+ pc->check_contract.contract_terms->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->contract_terms->details.v1.choices_len,
- pc->choice_index);
+ pc->check_contract.contract_terms->details.v1.choices_len,
+ pc->parse_wallet_data.choice_index);
GNUNET_break (0);
pay_end (pc,
TALER_MHD_reply_with_error (
@@ -3225,10 +3273,11 @@ phase_check_contract (struct PayContext *pc)
}
}
-
- if (GNUNET_TIME_timestamp_cmp (pc->contract_terms->wire_deadline,
+ if (GNUNET_TIME_timestamp_cmp (pc->check_contract.contract_terms->
+ wire_deadline,
<,
- pc->contract_terms->refund_deadline))
+ pc->check_contract.contract_terms->
+ refund_deadline))
{
/* This should already have been checked when creating the order! */
GNUNET_break (0);
@@ -3240,7 +3289,8 @@ phase_check_contract (struct PayContext *pc)
NULL));
return;
}
- if (GNUNET_TIME_absolute_is_past (pc->contract_terms->pay_deadline.abs_time))
+ if (GNUNET_TIME_absolute_is_past (pc->check_contract.contract_terms->
+ pay_deadline.abs_time))
{
/* too late */
pay_end (pc,
@@ -3257,7 +3307,7 @@ phase_check_contract (struct PayContext *pc)
struct TMH_WireMethod *wm;
wm = pc->hc->instance->wm_head;
- while (0 != GNUNET_memcmp (&pc->contract_terms->h_wire,
+ while (0 != GNUNET_memcmp (&pc->check_contract.contract_terms->h_wire,
&wm->h_wire))
wm = wm->next;
if (NULL == wm)
@@ -3271,7 +3321,7 @@ phase_check_contract (struct PayContext *pc)
NULL));
return;
}
- pc->wm = wm;
+ pc->check_contract.wm = wm;
}
pc->phase = PP_VALIDATE_TOKENS;
}
@@ -3291,7 +3341,7 @@ phase_parse_wallet_data (struct PayContext *pc)
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_int64 ("choice_index",
- &pc->choice_index),
+ &pc->parse_wallet_data.choice_index),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_array_const ("tokens_evs",
@@ -3300,8 +3350,8 @@ phase_parse_wallet_data (struct PayContext *pc)
GNUNET_JSON_spec_end ()
};
- pc->choice_index = -1;
- if (NULL == pc->wallet_data)
+ pc->parse_wallet_data.choice_index = -1;
+ if (NULL == pc->parse_pay.wallet_data)
{
pc->phase = PP_CHECK_CONTRACT;
return;
@@ -3310,7 +3360,7 @@ phase_parse_wallet_data (struct PayContext *pc)
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (pc->connection,
- pc->wallet_data,
+ pc->parse_pay.wallet_data,
spec);
if (GNUNET_YES != res)
{
@@ -3323,8 +3373,10 @@ phase_parse_wallet_data (struct PayContext *pc)
}
}
- pc->token_envelopes_cnt = json_array_size (tokens_evs);
- if (pc->token_envelopes_cnt > MAX_TOKEN_ALLOWED_OUTPUTs)
+ pc->parse_wallet_data.token_envelopes_cnt
+ = json_array_size (tokens_evs);
+ if (pc->parse_wallet_data.token_envelopes_cnt >
+ MAX_TOKEN_ALLOWED_OUTPUTs)
{
GNUNET_break_op (0);
pay_end (pc,
@@ -3335,15 +3387,9 @@ phase_parse_wallet_data (struct PayContext *pc)
"'tokens_evs' array too long"));
return;
}
- if (0 < pc->token_envelopes_cnt)
- {
- /* Calculate output commitment to be verified later. */
- TALER_json_hash (tokens_evs,
- &pc->h_outputs);
- }
-
- pc->token_envelopes = GNUNET_new_array (pc->token_envelopes_cnt,
- struct TokenEnvelope);
+ pc->parse_wallet_data.token_envelopes
+ = GNUNET_new_array (pc->parse_wallet_data.token_envelopes_cnt,
+ struct TokenEnvelope);
{
unsigned int tokens_ev_index;
@@ -3353,7 +3399,8 @@ phase_parse_wallet_data (struct PayContext *pc)
tokens_ev_index,
token_ev)
{
- struct TokenEnvelope *ev = &pc->token_envelopes[tokens_ev_index];
+ struct TokenEnvelope *ev
+ = &pc->parse_wallet_data.token_envelopes[tokens_ev_index];
struct GNUNET_JSON_Specification ispec[] = {
TALER_JSON_spec_token_envelope ("token_ev",
&ev->blinded_token),
@@ -3380,7 +3427,8 @@ phase_parse_wallet_data (struct PayContext *pc)
{
if (0 ==
GNUNET_memcmp (ev->blinded_token.blinded_pub,
- pc->token_envelopes[j].blinded_token.blinded_pub))
+ pc->parse_wallet_data.token_envelopes[j].
+ blinded_token.blinded_pub))
{
GNUNET_break_op (0);
pay_end (pc,
@@ -3395,8 +3443,8 @@ phase_parse_wallet_data (struct PayContext *pc)
}
}
- TALER_json_hash (pc->wallet_data,
- &pc->h_wallet_data);
+ TALER_json_hash (pc->parse_pay.wallet_data,
+ &pc->parse_wallet_data.h_wallet_data);
pc->phase = PP_CHECK_CONTRACT;
}
@@ -3423,7 +3471,7 @@ phase_parse_pay (struct PayContext *pc)
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_object_const ("wallet_data",
- &pc->wallet_data),
+ &pc->parse_pay.wallet_data),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_array_const ("tokens",
@@ -3432,7 +3480,7 @@ phase_parse_pay (struct PayContext *pc)
GNUNET_JSON_spec_end ()
};
- GNUNET_assert (PP_INIT == pc->phase);
+ GNUNET_assert (PP_PARSE_PAY == pc->phase);
{
enum GNUNET_GenericReturnValue res;
@@ -3453,16 +3501,16 @@ phase_parse_pay (struct PayContext *pc)
/* copy session ID (if set) */
if (NULL != session_id)
{
- pc->session_id = GNUNET_strdup (session_id);
+ pc->parse_pay.session_id = GNUNET_strdup (session_id);
}
else
{
/* use empty string as default if client didn't specify it */
- pc->session_id = GNUNET_strdup ("");
+ pc->parse_pay.session_id = GNUNET_strdup ("");
}
- pc->coins_cnt = json_array_size (coins);
- if (pc->coins_cnt > MAX_COIN_ALLOWED_COINS)
+ pc->parse_pay.coins_cnt = json_array_size (coins);
+ if (pc->parse_pay.coins_cnt > MAX_COIN_ALLOWED_COINS)
{
GNUNET_break_op (0);
pay_end (pc,
@@ -3474,8 +3522,8 @@ phase_parse_pay (struct PayContext *pc)
return;
}
/* note: 1 coin = 1 deposit confirmation expected */
- pc->dc = GNUNET_new_array (pc->coins_cnt,
- struct DepositConfirmation);
+ pc->parse_pay.dc = GNUNET_new_array (pc->parse_pay.coins_cnt,
+ struct DepositConfirmation);
/* This loop populates the array 'dc' in 'pc' */
{
@@ -3484,7 +3532,7 @@ phase_parse_pay (struct PayContext *pc)
json_array_foreach (coins, coins_index, coin)
{
- struct DepositConfirmation *dc = &pc->dc[coins_index];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[coins_index];
const char *exchange_url;
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig",
@@ -3536,7 +3584,7 @@ phase_parse_pay (struct PayContext *pc)
{
if (0 ==
GNUNET_memcmp (&dc->cdd.coin_pub,
- &pc->dc[j].cdd.coin_pub))
+ &pc->parse_pay.dc[j].cdd.coin_pub))
{
GNUNET_break_op (0);
pay_end (pc,
@@ -3568,13 +3616,13 @@ phase_parse_pay (struct PayContext *pc)
}
/* Setup exchange group */
- for (unsigned int i = 0; i<pc->num_exchanges; i++)
+ for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
{
if (0 ==
- strcmp (pc->egs[i]->exchange_url,
+ strcmp (pc->parse_pay.egs[i]->exchange_url,
exchange_url))
{
- eg = pc->egs[i];
+ eg = pc->parse_pay.egs[i];
break;
}
}
@@ -3584,8 +3632,8 @@ phase_parse_pay (struct PayContext *pc)
eg->pc = pc;
eg->exchange_url = dc->exchange_url;
eg->total = dc->cdd.amount;
- GNUNET_array_append (pc->egs,
- pc->num_exchanges,
+ GNUNET_array_append (pc->parse_pay.egs,
+ pc->parse_pay.num_exchanges,
eg);
}
else
@@ -3608,8 +3656,8 @@ phase_parse_pay (struct PayContext *pc)
}
}
- pc->tokens_cnt = json_array_size (tokens);
- if (pc->tokens_cnt > MAX_TOKEN_ALLOWED_INPUTs)
+ pc->parse_pay.tokens_cnt = json_array_size (tokens);
+ if (pc->parse_pay.tokens_cnt > MAX_TOKEN_ALLOWED_INPUTs)
{
GNUNET_break_op (0);
pay_end (pc,
@@ -3621,8 +3669,8 @@ phase_parse_pay (struct PayContext *pc)
return;
}
- pc->tokens = GNUNET_new_array (pc->tokens_cnt,
- struct TokenUseConfirmation);
+ pc->parse_pay.tokens = GNUNET_new_array (pc->parse_pay.tokens_cnt,
+ struct TokenUseConfirmation);
/* This look populates the array 'tokens' in 'pc' */
{
@@ -3631,7 +3679,7 @@ phase_parse_pay (struct PayContext *pc)
json_array_foreach (tokens, tokens_index, token)
{
- struct TokenUseConfirmation *tuc = &pc->tokens[tokens_index];
+ struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[tokens_index];
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed_auto ("token_sig",
&tuc->sig),
@@ -3662,7 +3710,7 @@ phase_parse_pay (struct PayContext *pc)
{
if (0 ==
GNUNET_memcmp (&tuc->pub,
- &pc->tokens[j].pub))
+ &pc->parse_pay.tokens[j].pub))
{
GNUNET_break_op (0);
pay_end (pc,
@@ -3690,48 +3738,48 @@ pay_context_cleanup (void *cls)
{
struct PayContext *pc = cls;
- if (NULL != pc->timeout_task)
+ if (NULL != pc->batch_deposits.timeout_task)
{
- GNUNET_SCHEDULER_cancel (pc->timeout_task);
- pc->timeout_task = NULL;
+ GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
+ pc->batch_deposits.timeout_task = NULL;
}
- if (NULL != pc->contract_terms_json)
+ if (NULL != pc->check_contract.contract_terms_json)
{
- json_decref (pc->contract_terms_json);
- pc->contract_terms_json = NULL;
+ json_decref (pc->check_contract.contract_terms_json);
+ pc->check_contract.contract_terms_json = NULL;
}
- for (unsigned int i = 0; i<pc->coins_cnt; i++)
+ for (unsigned int i = 0; i<pc->parse_pay.coins_cnt; i++)
{
- struct DepositConfirmation *dc = &pc->dc[i];
+ struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
TALER_denom_sig_free (&dc->cdd.denom_sig);
GNUNET_free (dc->exchange_url);
}
- GNUNET_free (pc->dc);
- for (unsigned int i = 0; i<pc->num_exchanges; i++)
+ GNUNET_free (pc->parse_pay.dc);
+ for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
{
- struct ExchangeGroup *eg = pc->egs[i];
+ struct ExchangeGroup *eg = pc->parse_pay.egs[i];
if (NULL != eg->fo)
TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
GNUNET_free (eg);
}
- GNUNET_free (pc->egs);
- if (NULL != pc->contract_terms)
+ GNUNET_free (pc->parse_pay.egs);
+ if (NULL != pc->check_contract.contract_terms)
{
- TALER_MERCHANT_contract_free (pc->contract_terms);
- pc->contract_terms = NULL;
+ TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
+ pc->check_contract.contract_terms = NULL;
}
if (NULL != pc->response)
{
MHD_destroy_response (pc->response);
pc->response = NULL;
}
- GNUNET_free (pc->session_id);
+ GNUNET_free (pc->parse_pay.session_id);
GNUNET_CONTAINER_DLL_remove (pc_head,
pc_tail,
pc);
- GNUNET_free (pc->pos_key);
+ GNUNET_free (pc->check_contract.pos_key);
GNUNET_free (pc);
}
@@ -3763,7 +3811,7 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
(int) pc->phase);
switch (pc->phase)
{
- case PP_INIT:
+ case PP_PARSE_PAY:
phase_parse_pay (pc);
break;
case PP_PARSE_WALLET_DATA:
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -818,6 +818,9 @@ clean_order (void *cls)
json_decref (mctf->description_i18n);
switch (mctf->kind)
{
+ case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
+ GNUNET_break (0);
+ break;
case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
for (size_t j = 0; j<mctf->details.subscription.trusted_domains_len; j++)
GNUNET_free (mctf->details.subscription.trusted_domains[j]);
@@ -1696,12 +1699,14 @@ add_output_token_family (struct OrderContext *oc,
"lookup_token_family_key");
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* Single-statement transaction shouldn't possibly cause serialization errors.
+ Thus treating like a hard error. */
GNUNET_break (0);
reply_with_error (oc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_SOFT_FAILURE,
"lookup_token_family_key");
- return GNUNET_SYSERR; /* FIXME: retry instead? */
+ return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Output token family slug %s unknown\n",
@@ -1861,12 +1866,14 @@ add_output_token_family (struct OrderContext *oc,
NULL);
return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* Single-statement transaction shouldn't possibly cause serialization errors.
+ Thus treating like a hard error. */
GNUNET_break (0);
reply_with_error (oc,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_SOFT_FAILURE,
NULL);
- return GNUNET_SYSERR; // FIXME: or retry?
+ return GNUNET_SYSERR;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_break (0);
reply_with_error (oc,
@@ -3003,11 +3010,10 @@ parse_order (struct OrderContext *oc)
{
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");
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY,
+ oc->parse_order.details.v0.brutto.currency);
return;
}
if (NULL != choices)
@@ -3337,224 +3343,13 @@ parse_order (struct OrderContext *oc)
static void
parse_donau_instances (struct OrderContext *oc)
{
+ /* FIXME-#9059: not yet implemented! */
return;
}
#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_MERCHANT_ContractChoice *choice,
- const json_t *jinputs)
-{
- const json_t *jinput;
- size_t idx;
-
- json_array_foreach ((json_t *) jinputs, idx, jinput)
- {
- struct TALER_MERCHANT_ContractInput 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_MERCHANT_CONTRACT_INPUT_TYPE_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_MERCHANT_ContractChoice *choice,
- const json_t *joutputs)
-{
- const json_t *joutput;
- size_t idx;
-
- json_array_foreach ((json_t *) joutputs, idx, joutput)
- {
- struct TALER_MERCHANT_ContractOutput 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",
- // FIXME...
- (const char **) &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),
- ¬s),
- 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_MERCHANT_CONTRACT_OUTPUT_TYPE_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
@@ -3652,11 +3447,10 @@ parse_choices (struct OrderContext *oc)
{
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");
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY,
+ choice->amount.currency);
return;
}
@@ -3744,16 +3538,21 @@ parse_choices (struct OrderContext *oc)
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
GNUNET_assert (0);
break;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
+ GNUNET_break (0); /* FIXME-#9059: not yet implemented! */
+ break;
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
/* Ignore inputs tokens with 'count' field set to 0 */
if (0 == output.details.token.count)
continue;
- // FIXME: no valid_at in choice->output, is NOW fine?
+ if (0 == output.details.token.valid_at.abs_time.abs_value_us)
+ output.details.token.valid_at
+ = GNUNET_TIME_timestamp_get ();
if (GNUNET_OK !=
add_output_token_family (oc,
output.details.token.token_family_slug,
- GNUNET_TIME_timestamp_get (),
+ output.details.token.valid_at,
&output.details.token.key_index))
{
diff --git a/src/backend/taler-merchant-httpd_private-post-token-families.c b/src/backend/taler-merchant-httpd_private-post-token-families.c
@@ -46,6 +46,10 @@ static bool
token_families_equal (const struct TALER_MERCHANTDB_TokenFamilyDetails *tf1,
const struct TALER_MERCHANTDB_TokenFamilyDetails *tf2)
{
+ /* Note: we're not comparing 'cipher', as that is selected
+ in the database to some default value and we currently
+ do not allow the SPA to change it. As a result, it should
+ always be "NULL" in tf1 and the DB-default in tf2. */
return ( (0 == strcmp (tf1->slug,
tf2->slug)) &&
(0 == strcmp (tf1->name,
diff --git a/src/backend/taler-merchant-kyccheck.c b/src/backend/taler-merchant-kyccheck.c
@@ -579,6 +579,7 @@ exchange_check_cb (
EXCHANGE_TIMEOUT);
i->last_kyc_check = GNUNET_TIME_timestamp_get ();
i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
+ i->auth_ok = false;
break;
}
diff --git a/src/backenddb/merchantdb_helper.c b/src/backenddb/merchantdb_helper.c
@@ -79,6 +79,8 @@ TALER_MERCHANTDB_token_family_details_free (
GNUNET_free (tf->name);
GNUNET_free (tf->description);
json_decref (tf->description_i18n);
+ json_decref (tf->extra_data);
+ GNUNET_free (tf->cipher_spec);
}
diff --git a/src/backenddb/pg_get_kyc_status.c b/src/backenddb/pg_get_kyc_status.c
@@ -50,11 +50,12 @@ TMH_PG_get_kyc_status (
};
uint32_t h32 = 0;
uint32_t e32 = 0;
+ bool token_is_null = true;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_auto_from_type ("access_token",
access_token),
- auth_ok),
+ &token_is_null),
GNUNET_PQ_result_spec_uint32 ("exchange_http_status",
&h32),
GNUNET_PQ_result_spec_uint32 ("exchange_ec_code",
@@ -101,5 +102,6 @@ TMH_PG_get_kyc_status (
rs);
*last_ec = (enum TALER_ErrorCode) (int) e32;
*last_http_status = (unsigned int) h32;
+ *auth_ok = ! token_is_null;
return qs;
}
diff --git a/src/backenddb/pg_lookup_token_family.c b/src/backenddb/pg_lookup_token_family.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2023, 2024 Taler Systems SA
+ Copyright (C) 2023, 2024, 2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -60,6 +60,8 @@ TMH_PG_lookup_token_family (
&details->slug),
GNUNET_PQ_result_spec_string ("name",
&details->name),
+ GNUNET_PQ_result_spec_string ("cipher_choice",
+ &details->cipher_spec),
GNUNET_PQ_result_spec_string ("description",
&details->description),
TALER_PQ_result_spec_json ("description_i18n",
@@ -94,6 +96,7 @@ TMH_PG_lookup_token_family (
"SELECT"
" slug"
",name"
+ ",cipher_choice"
",description"
",description_i18n"
",extra_data"
diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h
@@ -852,7 +852,7 @@ TALER_TESTING_cmd_merchant_get_order (
* @param wired whether the order has been wired or not.
* @param transfers NULL-terminated list of labels (const char *) of
* wire transfers (commands) we expect to be aggregated in the order
- * (assuming @a http_code is #MHD_HTTP_OK). If @e paid is fale, this
+ * (assuming @a http_code is #MHD_HTTP_OK). If @e paid is false, this
* parameter is ignored.
* @param refunded whether the order has been refunded.
* @param refunds NULL-terminated list of labels (const char *) of
diff --git a/src/include/taler_merchant_util.h b/src/include/taler_merchant_util.h
@@ -256,8 +256,12 @@ struct TALER_MERCHANT_ContractOutput
*/
unsigned int count;
- // FIXME: add support for clients picking a validity
- // period in the future for output tokens!
+ /**
+ * Determines when the output token should be valid.
+ * Optional, set to zero for not specified (then we
+ * use the current time).
+ */
+ struct GNUNET_TIME_Timestamp valid_at;
} token;
@@ -669,6 +673,19 @@ TALER_MERCHANT_contract_parse (json_t *input,
/**
+ * Provide specification to parse an JSON contract input type.
+ * The value is provided as a descriptive string.
+ *
+ * @param name name of the JSON member with the contract type
+ * @param[out] cit where to store the contract input type
+ * @return spec for parsing a contract input type
+ */
+struct GNUNET_JSON_Specification
+TALER_MERCHANT_json_spec_cit (const char *name,
+ enum TALER_MERCHANT_ContractInputType *cit);
+
+
+/**
* Parse JSON contract terms choice input.
*
* @param[in] root JSON object containing choice input
@@ -686,6 +703,19 @@ TALER_MERCHANT_parse_choice_input (
/**
+ * Provide specification to parse an JSON contract output type.
+ * The value is provided as a descriptive string.
+ *
+ * @param name name of the JSON member with the contract type
+ * @param[out] cot where to store the contract output type
+ * @return spec for parsing a contract output type
+ */
+struct GNUNET_JSON_Specification
+TALER_MERCHANT_json_spec_cot (const char *name,
+ enum TALER_MERCHANT_ContractOutputType *cot);
+
+
+/**
* Parse JSON contract terms choice output.
*
* @param[in] root JSON object containing choice output
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
@@ -229,7 +229,7 @@ run (void *cls,
TALER_TESTING_cmd_merchant_post_orders_no_claim (
"create-proposal-bad-currency",
merchant_url,
- MHD_HTTP_BAD_REQUEST,
+ MHD_HTTP_CONFLICT,
"4",
GNUNET_TIME_UNIT_ZERO_TS,
GNUNET_TIME_UNIT_FOREVER_TS,
diff --git a/src/testing/test_merchant_order_creation.sh b/src/testing/test_merchant_order_creation.sh
@@ -251,7 +251,7 @@ echo "OK"
echo -n "Creating discount token family..."
VALID_AFTER="{\"t_s\": $(date +%s)}" # now
VALID_BEFORE="{\"t_s\": $(date +%s -d "+30 days")}" # 30 days from now
-DURATION="{\"d_us\": $(echo '30 * 24 * 60 * 60 * 1000000' | bc)}" # 30 days
+DURATION="{\"d_us\": $(expr 30 \* 24 \* 60 \* 60 \* 1000000)}" # 30 days
STATUS=$(curl 'http://localhost:9966/private/tokenfamilies' \
-d "{\"kind\": \"discount\", \"slug\":\"test-discount\", \"name\": \"Test discount\", \"description\": \"Less money $$\", \"description_i18n\": {\"en\": \"Less money $$\", \"es\": \"Menos dinero $$\"}, \"valid_after\": $VALID_AFTER, \"valid_before\": $VALID_BEFORE, \"duration\": $DURATION, \"validity_granularity\": $DURATION}" \
-w "%{http_code}" -s -o /dev/null)
@@ -267,7 +267,7 @@ echo "Ok"
echo -n "Creating subscription token family..."
VALID_AFTER="{\"t_s\": $(date +%s)}" # now
VALID_BEFORE="{\"t_s\": $(date +%s -d "+30 days")}" # 30 days from now
-DURATION="{\"d_us\": $(echo '30 * 24 * 60 * 60 * 1000000' | bc)}" # 30 days
+DURATION="{\"d_us\": $(expr 30 \* 24 \* 60 \* 60 \* 1000000)}" # 30 days
STATUS=$(curl 'http://localhost:9966/private/tokenfamilies' \
-d "{\"kind\": \"subscription\", \"slug\":\"test-subscription\", \"name\": \"Test subscription\", \"description\": \"Money per month\", \"description_i18n\": {\"en\": \"Money $$$ per month\", \"es\": \"Dinero $$$ al mes\"}, \"valid_after\": $VALID_AFTER, \"valid_before\": $VALID_BEFORE, \"duration\": $DURATION, \"validity_granularity\": $DURATION}" \
-w "%{http_code}" -s -o /dev/null)
diff --git a/src/util/.gitignore b/src/util/.gitignore
@@ -1 +1,2 @@
taler-merchant-config
+test_contract
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
@@ -41,6 +41,7 @@ lib_LTLIBRARIES = \
libtalermerchantutil_la_SOURCES = \
contract_parse.c \
contract_serialize.c \
+ json.c \
os_installation.c
libtalermerchantutil_la_LIBADD = \
-lgnunetjson \
@@ -49,7 +50,7 @@ libtalermerchantutil_la_LIBADD = \
-ltalerutil \
$(XLIB)
libtalermerchantutil_la_LDFLAGS = \
- -version-info 0:0:0 \
+ -version-info 1:0:1 \
-export-dynamic -no-undefined
test_contract_SOURCES = \
diff --git a/src/util/contract_parse.c b/src/util/contract_parse.c
@@ -112,42 +112,6 @@ spec_merchant_details (const char *name,
/**
- * Get enum value from contract input type string.
- *
- * @param str contract input type string
- * @return enum value of input type
- */
-static enum TALER_MERCHANT_ContractInputType
-contract_input_type_from_string (const char *str)
-{
- /* For now, only 'token' is the only supported option. */
- if (0 == strcmp ("token",
- str))
- return TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN;
- return TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID;
-}
-
-
-/**
- * Get enum value from contract output type string.
- *
- * @param str contract output type string
- * @return enum value of output type
- */
-static enum TALER_MERCHANT_ContractOutputType
-contract_output_type_from_string (const char *str)
-{
- if (0 == strcmp ("token",
- str))
- return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN;
- if (0 == strcmp ("tax-receipt",
- str))
- return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT;
- return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID;
-}
-
-
-/**
* Get enum value from contract token type string.
*
* @param str contract token type string
@@ -173,12 +137,11 @@ TALER_MERCHANT_parse_choice_input (
size_t index,
bool order)
{
- const char *type;
const char *ename;
unsigned int eline;
struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("type",
- &type),
+ TALER_MERCHANT_json_spec_cit ("type",
+ &input->type),
GNUNET_JSON_spec_end ()
};
@@ -197,11 +160,10 @@ TALER_MERCHANT_parse_choice_input (
return GNUNET_SYSERR;
}
- input->type = contract_input_type_from_string (type);
-
switch (input->type)
{
case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
+ GNUNET_break (0);
break;
case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
{
@@ -249,12 +211,11 @@ TALER_MERCHANT_parse_choice_output (
size_t index,
bool order)
{
- const char *type;
const char *ename;
unsigned int eline;
struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("type",
- &type),
+ TALER_MERCHANT_json_spec_cot ("type",
+ &output->type),
GNUNET_JSON_spec_end ()
};
@@ -273,11 +234,10 @@ TALER_MERCHANT_parse_choice_output (
return GNUNET_SYSERR;
}
- output->type = contract_output_type_from_string (type);
-
switch (output->type)
{
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
+ GNUNET_break (0);
break;
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
{
@@ -288,6 +248,10 @@ TALER_MERCHANT_parse_choice_output (
GNUNET_JSON_spec_uint ("count",
&output->details.token.count),
NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("valid_at",
+ &output->details.token.valid_at),
+ NULL),
(! order)
? GNUNET_JSON_spec_uint ("key_index",
&output->details.token.key_index)
@@ -995,8 +959,16 @@ parse_contract_version (void *cls,
}
-struct GNUNET_JSON_Specification
-TALER_MERCHANT_JSON_spec_contract_version (
+/**
+ * Create JSON specification to parse a merchant contract
+ * version.
+ *
+ * @param name name of the field
+ * @param[out] version where to write the contract version
+ * @return JSON specification object
+ */
+static struct GNUNET_JSON_Specification
+spec_contract_version (
const char *name,
enum TALER_MERCHANT_ContractVersion *version)
{
@@ -1018,7 +990,7 @@ TALER_MERCHANT_JSON_spec_contract_version (
* @param[out] contract where to write the data
* @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
*/
-enum GNUNET_GenericReturnValue
+static enum GNUNET_GenericReturnValue
parse_contract_v0 (
json_t *input,
struct TALER_MERCHANT_Contract *contract)
@@ -1068,7 +1040,7 @@ parse_contract_v0 (
* @param[out] contract where to write the data
* @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
*/
-enum GNUNET_GenericReturnValue
+static enum GNUNET_GenericReturnValue
parse_contract_v1 (
json_t *input,
struct TALER_MERCHANT_Contract *contract)
@@ -1110,12 +1082,11 @@ struct TALER_MERCHANT_Contract *
TALER_MERCHANT_contract_parse (json_t *input,
bool nonce_optional)
{
- struct TALER_MERCHANT_Contract *contract;
- contract = GNUNET_new (struct TALER_MERCHANT_Contract);
-
+ struct TALER_MERCHANT_Contract *contract
+ = GNUNET_new (struct TALER_MERCHANT_Contract);
struct GNUNET_JSON_Specification espec[] = {
- TALER_MERCHANT_JSON_spec_contract_version ("version",
- &contract->version),
+ spec_contract_version ("version",
+ &contract->version),
GNUNET_JSON_spec_string_copy ("summary",
&contract->summary),
/* FIXME: do i18n_str validation in the future */
diff --git a/src/util/json.c b/src/util/json.c
@@ -0,0 +1,171 @@
+/*
+ This file is part of TALER
+ (C) 2025 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/json.c
+ * @brief helper functions to parse JSON
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_util.h>
+#include "taler_merchant_util.h"
+
+
+/**
+ * Parse given JSON object to `enum TALER_MERCHANT_ContractInputType`
+ *
+ * @param cls closure, NULL
+ * @param root the json object representing data
+ * @param[out] spec where to write the data
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+static enum GNUNET_GenericReturnValue
+parse_contract_input_type (void *cls,
+ json_t *root,
+ struct GNUNET_JSON_Specification *spec)
+{
+ static const struct Entry
+ {
+ const char *name;
+ enum TALER_MERCHANT_ContractInputType val;
+ } lt [] = {
+#if FUTURE
+ { .name = "coin",
+ .val = TALER_MERCHANT_CONTRACT_INPUT_TYPE_COIN },
+#endif
+ { .name = "token",
+ .val = TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN },
+ { .name = NULL,
+ .val = TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID }
+ };
+ enum TALER_MERCHANT_ContractInputType *res
+ = (enum TALER_MERCHANT_ContractInputType *) spec->ptr;
+
+ (void) cls;
+ if (json_is_string (root))
+ {
+ const char *str;
+
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ for (unsigned int i = 0; NULL != lt[i].name; i++)
+ {
+ if (0 == strcasecmp (str,
+ lt[i].name))
+ {
+ *res = lt[i].val;
+ return GNUNET_OK;
+ }
+ }
+ }
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+}
+
+
+struct GNUNET_JSON_Specification
+TALER_MERCHANT_json_spec_cit (const char *name,
+ enum TALER_MERCHANT_ContractInputType *cit)
+{
+ struct GNUNET_JSON_Specification ret = {
+ .parser = &parse_contract_input_type,
+ .field = name,
+ .ptr = cit
+ };
+
+ *cit = TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID;
+ return ret;
+}
+
+
+/**
+ * Parse given JSON object to `enum TALER_MERCHANT_ContractOutputType`
+ *
+ * @param cls closure, NULL
+ * @param root the json object representing data
+ * @param[out] spec where to write the data
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+static enum GNUNET_GenericReturnValue
+parse_contract_output_type (void *cls,
+ json_t *root,
+ struct GNUNET_JSON_Specification *spec)
+{
+ static const struct Entry
+ {
+ const char *name;
+ enum TALER_MERCHANT_ContractOutputType val;
+ } lt [] = {
+ { .name = "token",
+ .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN },
+ { .name = "tax-receipt",
+ .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT },
+#if FUTURE
+ { .name = "coin",
+ .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN },
+#endif
+ { .name = NULL,
+ .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID }
+ };
+ enum TALER_MERCHANT_ContractOutputType *res
+ = (enum TALER_MERCHANT_ContractOutputType *) spec->ptr;
+
+ (void) cls;
+ if (json_is_string (root))
+ {
+ const char *str;
+
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ for (unsigned int i = 0; NULL != lt[i].name; i++)
+ {
+ if (0 == strcasecmp (str,
+ lt[i].name))
+ {
+ *res = lt[i].val;
+ return GNUNET_OK;
+ }
+ }
+ }
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+}
+
+
+struct GNUNET_JSON_Specification
+TALER_MERCHANT_json_spec_cot (const char *name,
+ enum TALER_MERCHANT_ContractOutputType *cot)
+{
+ struct GNUNET_JSON_Specification ret = {
+ .parser = &parse_contract_output_type,
+ .field = name,
+ .ptr = cot
+ };
+
+ *cot = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID;
+ return ret;
+}