[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant] branch master updated: fix pay handling for v1 contract
From: |
gnunet |
Subject: |
[taler-merchant] branch master updated: fix pay handling for v1 contracts, including in test |
Date: |
Tue, 24 Dec 2024 17:51:46 +0100 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository merchant.
The following commit(s) were added to refs/heads/master by this push:
new 1d7f15a5 fix pay handling for v1 contracts, including in test
1d7f15a5 is described below
commit 1d7f15a524fdeca2304d1fa5bf13dcf0decd8ecc
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Dec 24 17:51:41 2024 +0100
fix pay handling for v1 contracts, including in test
---
src/backend/taler-merchant-httpd_contract.c | 8 +-
.../taler-merchant-httpd_post-orders-ID-pay.c | 666 ++++++++++++---------
.../taler-merchant-httpd_private-post-orders.c | 5 +-
src/testing/testing_api_cmd_pay_order.c | 371 +++++++-----
4 files changed, 630 insertions(+), 420 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_contract.c
b/src/backend/taler-merchant-httpd_contract.c
index d311990a..a3547a56 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -111,9 +111,16 @@ parse_choices (void *cls,
for (unsigned int i = 0; i<*choices_len; i++)
{
+ struct TALER_MerchantContractChoice *choice = &(*choices)[i];
const json_t *jinputs;
const json_t *joutputs;
struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &choice->amount),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("max_fee",
+ &choice->max_fee),
+ NULL),
GNUNET_JSON_spec_array_const ("inputs",
&jinputs),
GNUNET_JSON_spec_array_const ("outputs",
@@ -122,7 +129,6 @@ parse_choices (void *cls,
};
const char *error_name;
unsigned int error_line;
- struct TALER_MerchantContractChoice *choice = &(*choices)[i];
if (GNUNET_OK !=
GNUNET_JSON_parse (json_array_get (root, i),
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index 4a5c7d3f..ba47d7a6 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -107,7 +107,7 @@ enum PayPhase
PP_VALIDATE_TOKENS,
/**
- * Contract has been paid.
+ * Check if contract has been paid.
*/
PP_CONTRACT_PAID,
@@ -365,6 +365,8 @@ struct ExchangeGroup
*/
struct PayContext
{
+ // FIXME: group more entries by phase that initializes them,
+ // like we do for 'validate_tokens'.
/**
* Stored in a DLL.
@@ -403,16 +405,6 @@ struct PayContext
*/
struct TokenEnvelope *token_envelopes;
- /**
- * Array with @e choices_len choices from the contract terms.
- */
- struct TALER_MerchantContractChoice *choices;
-
- /**
- * Array with @e token_families_len token families from the contract terms.
- */
- struct TALER_MerchantContractTokenFamily *token_families;
-
/**
* MHD connection to return to
*/
@@ -498,6 +490,7 @@ struct PayContext
*/
struct TALER_PrivateContractHashP h_contract_terms;
+
/**
* "h_wire" from @e contract_terms. Used to identify
* the instance's wire transfer method.
@@ -505,19 +498,83 @@ struct PayContext
struct TALER_MerchantWireHashP h_wire;
/**
- * Maximum fee the merchant is willing to pay, from @e root.
- * Note that IF the total fee of the exchange is higher, that is
- * acceptable to the merchant if the customer is willing to
- * pay the difference
- * (i.e. amount - max_fee <= actual_amount - actual_fee).
+ * Version of the contract terms.
+ */
+ enum TALER_MerchantContractVersion version;
+
+ /**
+ * @e version dependent details from our contract.
*/
- struct TALER_Amount max_fee;
+ union
+ {
+
+ struct
+ {
+ /**
+ * Maximum fee the merchant is willing to pay, from @e root.
+ * Note that IF the total fee of the exchange is higher, that is
+ * acceptable to the merchant if the customer is willing to
+ * pay the difference
+ * (i.e. amount - max_fee <= actual_amount - actual_fee).
+ */
+ struct TALER_Amount max_fee;
+
+ /**
+ * Amount from @e root. This is the amount the merchant expects
+ * to make, minus @e max_fee.
+ */
+ struct TALER_Amount amount;
+ } v0;
+
+ struct
+ {
+
+ /**
+ * Array with @e choices_len choices from the contract terms.
+ */
+ struct TALER_MerchantContractChoice *choices;
+
+ /**
+ * Array with @e token_families_len token families from the contract
terms.
+ */
+ struct TALER_MerchantContractTokenFamily *token_families;
+
+ /**
+ * Length of the @e choices array.
+ */
+ unsigned int choices_len;
+
+ /**
+ * Length of the @e token_families array.
+ */
+ unsigned int token_families_len;
+
+ } v1;
+
+ } details;
/**
- * Amount from @e root. This is the amount the merchant expects
- * to make, minus @e max_fee.
+ * Results from the phase_validate_tokens()
*/
- struct TALER_Amount amount;
+ struct
+ {
+
+ /**
+ * Maximum fee the merchant is willing to pay, from @e root.
+ * Note that IF the total fee of the exchange is higher, that is
+ * acceptable to the merchant if the customer is willing to
+ * pay the difference
+ * (i.e. amount - max_fee <= actual_amount - actual_fee).
+ */
+ struct TALER_Amount max_fee;
+
+ /**
+ * Amount from @e root. This is the amount the merchant expects
+ * to make, minus @e max_fee.
+ */
+ struct TALER_Amount brutto;
+
+ } validate_tokens;
/**
* Considering all the coins with the "found_in_db" flag
@@ -599,16 +656,6 @@ struct PayContext
*/
unsigned int output_tokens_len;
- /**
- * Length of the @e choices array.
- */
- unsigned int choices_len;
-
- /**
- * Length of the @e token_families array.
- */
- unsigned int token_families_len;
-
/**
* Number of exchanges involved in the payment. Length
* of the @e eg array.
@@ -879,7 +926,7 @@ batch_deposit_transaction (const struct ExchangeGroup *eg,
uint32_t off = 0;
GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&total_without_fees));
for (size_t i = 0; i<pc->coins_cnt; i++)
{
@@ -1691,7 +1738,7 @@ phase_success_response (struct PayContext *pc)
? NULL
: TALER_build_pos_confirmation (pc->pos_key,
pc->pos_alg,
- &pc->amount,
+ &pc->validate_tokens.brutto,
pc->timestamp);
token_sigs = (0 >= pc->output_tokens_len)
? NULL
@@ -1903,9 +1950,9 @@ check_payment_sufficient (struct PayContext *pc)
struct TALER_Amount total_needed;
if (0 == pc->coins_cnt)
- return TALER_amount_is_zero (&pc->amount);
+ return TALER_amount_is_zero (&pc->validate_tokens.brutto);
GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&total_wire_fee));
for (unsigned int i = 0; i < pc->num_exchanges; i++)
{
@@ -1942,10 +1989,10 @@ check_payment_sufficient (struct PayContext *pc)
* amount with fee / and wire fee, for all the coins.
*/
GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&acc_fee));
GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&acc_amount));
for (size_t i = 0; i<pc->coins_cnt; i++)
{
@@ -2013,7 +2060,7 @@ check_payment_sufficient (struct PayContext *pc)
TALER_amount2s (&total_wire_fee));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deposit fee limit for merchant: %s\n",
- TALER_amount2s (&pc->max_fee));
+ TALER_amount2s (&pc->validate_tokens.max_fee));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Total refunded amount: %s\n",
TALER_amount2s (&pc->total_refunded));
@@ -2049,7 +2096,7 @@ check_payment_sufficient (struct PayContext *pc)
"Overflow adding up amounts"));
return false;
}
- if (-1 == TALER_amount_cmp (&pc->max_fee,
+ if (-1 == TALER_amount_cmp (&pc->validate_tokens.max_fee,
&acc_fee))
{
/**
@@ -2063,12 +2110,12 @@ check_payment_sufficient (struct PayContext *pc)
GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
TALER_amount_subtract (&excess_fee,
&acc_fee,
- &pc->max_fee));
+ &pc->validate_tokens.max_fee));
/* add that to the total */
if (0 >
TALER_amount_add (&total_needed,
&excess_fee,
- &pc->amount))
+ &pc->validate_tokens.brutto))
{
GNUNET_break (0);
pay_end (pc,
@@ -2084,7 +2131,7 @@ check_payment_sufficient (struct PayContext *pc)
{
/* Fees are fully covered by the merchant, all we require
is that the total payment is not below the contract's amount */
- total_needed = pc->amount;
+ total_needed = pc->validate_tokens.brutto;
}
/* Do not count refunds towards the payment */
@@ -2123,7 +2170,7 @@ check_payment_sufficient (struct PayContext *pc)
return false;
}
if (-1 < TALER_amount_cmp (&acc_amount,
- &pc->amount))
+ &pc->validate_tokens.brutto))
{
GNUNET_break_op (0);
pay_end (pc,
@@ -2181,13 +2228,13 @@ phase_execute_pay_transaction (struct PayContext *pc)
(used in check_coin_paid(), check_coin_refunded()
and check_payment_sufficient()). */
GNUNET_break (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&pc->total_paid));
GNUNET_break (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&pc->total_fees_paid));
GNUNET_break (GNUNET_OK ==
- TALER_amount_set_zero (pc->amount.currency,
+ TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
&pc->total_refunded));
for (size_t i = 0; i<pc->coins_cnt; i++)
pc->dc[i].found_in_db = false;
@@ -2240,7 +2287,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
else if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
/* UNIQUE constraint violation, meaning this token was already used. */
- TMH_db->rollback(TMH_db->cls);
+ TMH_db->rollback (TMH_db->cls);
pay_end (pc,
TALER_MHD_reply_with_error (pc->connection,
MHD_HTTP_CONFLICT,
@@ -2275,13 +2322,14 @@ phase_execute_pay_transaction (struct PayContext *pc)
}
if (pc->deposit_currency_mismatch)
{
- TMH_db->rollback(TMH_db->cls);
+ TMH_db->rollback (TMH_db->cls);
GNUNET_break_op (0);
pay_end (pc,
TALER_MHD_reply_with_error (pc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- pc->amount.currency));
+
pc->validate_tokens.brutto.currency))
+ ;
return;
}
}
@@ -2647,20 +2695,23 @@ sign_token_envelopes (struct PayContext *pc,
* @param slug slug to search for
* @return NULL if @a slug was not found
*/
-static struct TALER_MerchantContractTokenFamily *
+static const struct TALER_MerchantContractTokenFamily *
find_family (const struct PayContext *pc,
const char *slug)
{
- for (unsigned int i = 0; i<pc->token_families_len; i++)
+ for (unsigned int i = 0; i<pc->details.v1.token_families_len; i++)
{
- if (0 == strcmp (pc->token_families[i].slug,
+ const struct TALER_MerchantContractTokenFamily *tfi
+ = &pc->details.v1.token_families[i];
+
+ if (0 == strcmp (tfi->slug,
slug))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Token family %s found with %u keys\n",
slug,
- pc->token_families[i].keys_len);
- return &pc->token_families[i];
+ tfi->keys_len);
+ return tfi;
}
}
return NULL;
@@ -2678,178 +2729,173 @@ find_family (const struct PayContext *pc,
static void
phase_validate_tokens (struct PayContext *pc)
{
- if (NULL == pc->choices || 0 >= pc->choices_len)
+ switch (pc->version)
{
+ case TALER_MCV_V0:
/* No tokens to validate */
pc->phase = PP_PAY_TRANSACTION;
- return;
- }
-
- if (pc->choice_index < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order `%s' has non-empty choices array but"
- "request is missing 'choice_index' field\n",
- pc->order_id);
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING,
- NULL));
- return;
- }
-
- if (pc->choice_index >= pc->choices_len)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order `%s' has choices array with %u elements but "
- "request has 'choice_index' field with value %ld\n",
- pc->order_id,
- pc->choices_len,
- pc->choice_index);
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
- NULL));
- return;
- }
-
- {
- const struct TALER_MerchantContractChoice *selected
- = &pc->choices[pc->choice_index];
-
- for (unsigned int i = 0; i<selected->inputs_len; i++)
+ pc->validate_tokens.max_fee = pc->details.v0.max_fee;
+ pc->validate_tokens.brutto = pc->details.v0.amount;
+ break;
+ case TALER_MCV_V1:
{
- const struct TALER_MerchantContractInput *input
- = &selected->inputs[i];
- const struct TALER_MerchantContractTokenFamily *family;
+ const struct TALER_MerchantContractChoice *selected
+ = &pc->details.v1.choices[pc->choice_index];
- if (input->type != TALER_MCIT_TOKEN)
- {
- /* only validate inputs of type token (for now) */
- continue;
- }
+ pc->validate_tokens.max_fee = selected->max_fee;
+ pc->validate_tokens.brutto = selected->amount;
- family = find_family (pc,
- input->details.token.token_family_slug);
- if (NULL == family)
+ for (unsigned int i = 0; i<selected->inputs_len; i++)
{
- /* this should never happen, since the choices and
- token families are validated on insert. */
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "token family not found in order"));
- return;
- }
- if (GNUNET_NO ==
- find_valid_input_tokens (pc,
- family,
- i,
- input->details.token.count))
- {
- /* Error is already scheduled from find_valid_input_token. */
- return;
- }
- }
-
- GNUNET_array_grow (pc->output_tokens,
- pc->output_tokens_len,
- selected->outputs_len);
+ const struct TALER_MerchantContractInput *input
+ = &selected->inputs[i];
+ const struct TALER_MerchantContractTokenFamily *family;
- for (unsigned int i = 0; i<selected->outputs_len; i++)
- {
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
- const struct TALER_MerchantContractOutput *output
- = &selected->outputs[i];
- struct TALER_MerchantContractTokenFamily *family;
- struct TALER_MerchantContractTokenFamilyKey *key;
+ if (input->type != TALER_MCIT_TOKEN)
+ {
+ /* only validate inputs of type token (for now) */
+ continue;
+ }
- if (output->type != TALER_MCOT_TOKEN)
- {
- /* only validate outputs of type tokens (for now) */
- continue;
+ family = find_family (pc,
+ input->details.token.token_family_slug);
+ if (NULL == family)
+ {
+ /* this should never happen, since the choices and
+ token families are validated on insert. */
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "token family not found in order"));
+ return;
+ }
+ if (GNUNET_NO ==
+ find_valid_input_tokens (pc,
+ family,
+ i,
+ input->details.token.count))
+ {
+ /* Error is already scheduled from find_valid_input_token. */
+ return;
+ }
}
- family = find_family (pc,
- output->details.token.token_family_slug);
- if (NULL == family)
- {
- /* this should never happen, since the choices and
- token families are validated on insert. */
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "token family not found in order"));
- return;
- }
- if (output->details.token.key_index >= family->keys_len)
- {
- /* this should never happen, since the choices and
- token families are validated on insert. */
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "key index invalid for token family"));
- return;
- }
- key = &family->keys[output->details.token.key_index];
- qs = TMH_db->lookup_token_family_key (
- TMH_db->cls,
- pc->hc->instance->settings.id,
- family->slug,
- pc->timestamp,
- pc->pay_deadline,
- &details);
- if (qs <= 0)
+ GNUNET_array_grow (pc->output_tokens,
+ pc->output_tokens_len,
+ selected->outputs_len);
+
+ for (unsigned int i = 0; i<selected->outputs_len; i++)
{
- GNUNET_log (
- GNUNET_ERROR_TYPE_ERROR,
- "Did not find key for %s at [%llu,%llu]\n",
- family->slug,
- (unsigned long long) pc->timestamp.abs_time.abs_value_us,
- (unsigned long long) pc->pay_deadline.abs_time.abs_value_us);
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL));
- return;
- }
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
+ const struct TALER_MerchantContractOutput *output
+ = &selected->outputs[i];
+ const struct TALER_MerchantContractTokenFamily *family;
+ struct TALER_MerchantContractTokenFamilyKey *key;
+
+ if (output->type != TALER_MCOT_TOKEN)
+ {
+ /* only validate outputs of type tokens (for now) */
+ continue;
+ }
- GNUNET_assert (NULL != details.priv.private_key);
+ family = find_family (pc,
+ output->details.token.token_family_slug);
+ if (NULL == family)
+ {
+ /* this should never happen, since the choices and
+ token families are validated on insert. */
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "token family not found in order"));
+ return;
+ }
+ if (output->details.token.key_index >= family->keys_len)
+ {
+ /* this should never happen, since the choices and
+ token families are validated on insert. */
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "key index invalid for token family"));
+ return;
+ }
+ key = &family->keys[output->details.token.key_index];
+ qs = TMH_db->lookup_token_family_key (
+ TMH_db->cls,
+ pc->hc->instance->settings.id,
+ family->slug,
+ pc->timestamp,
+ pc->pay_deadline,
+ &details);
+ if (qs <= 0)
+ {
+ GNUNET_log (
+ GNUNET_ERROR_TYPE_ERROR,
+ "Did not find key for %s at [%llu,%llu]\n",
+ family->slug,
+ (unsigned long long) pc->timestamp.abs_time.abs_value_us,
+ (unsigned long long) pc->pay_deadline.abs_time.abs_value_us);
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL));
+ return;
+ }
- if (GNUNET_OK !=
- sign_token_envelopes (pc,
- key,
- &details.priv,
- /* TODO: Use critical field stored in database
here instead. */
- details.token_family.kind ==
- TALER_MERCHANTDB_TFK_Subscription,
- i,
- output->details.token.count))
- {
- /* Error is already scheduled from sign_token_envelopes. */
- return;
+ GNUNET_assert (NULL != details.priv.private_key);
+
+ if (GNUNET_OK !=
+ sign_token_envelopes (pc,
+ key,
+ &details.priv,
+ /* TODO: Use critical field stored in
database here instead. */
+ details.token_family.kind ==
+ TALER_MERCHANTDB_TFK_Subscription,
+ i,
+ output->details.token.count))
+ {
+ /* Error is already scheduled from sign_token_envelopes. */
+ return;
+ }
}
+ }
+ }
+
+ for (size_t i = 0; i<pc->coins_cnt; i++)
+ {
+ const struct DepositConfirmation *dc = &pc->dc[i];
+ if (GNUNET_OK !=
+ TALER_amount_cmp_currency (&dc->cdd.amount,
+ &pc->validate_tokens.brutto))
+ {
+ GNUNET_break_op (0);
+ fprintf (stderr,
+ "HERE (%u): %s != %s\n",
+ (unsigned int) pc->version,
+ dc->cdd.amount.currency,
+ TALER_amount2s (&pc->validate_tokens.brutto));
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+ pc->validate_tokens.brutto.currency));
+ return;
}
}
@@ -3174,16 +3220,17 @@ phase_check_contract (struct PayContext *pc)
/* Get details from contract and check fundamentals */
{
const char *fulfillment_url = NULL;
+ uint64_t version = 0;
struct GNUNET_JSON_Specification espec[] = {
- TALER_JSON_spec_amount_any ("amount",
- &pc->amount),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint64 ("version",
+ &version),
+ NULL),
GNUNET_JSON_spec_mark_optional (
/* This one does not have to be a Web URL */
GNUNET_JSON_spec_string ("fulfillment_url",
&fulfillment_url),
NULL),
- TALER_JSON_spec_amount_any ("max_fee",
- &pc->max_fee),
GNUNET_JSON_spec_timestamp ("timestamp",
&pc->timestamp),
GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -3192,16 +3239,6 @@ phase_check_contract (struct PayContext *pc)
&pc->pay_deadline),
GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
&pc->wire_transfer_deadline),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_choices ("choices",
- &pc->choices,
- &pc->choices_len),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_token_families ("token_families",
- &pc->token_families,
- &pc->token_families_len),
- NULL),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&pc->h_wire),
GNUNET_JSON_spec_mark_optional (
@@ -3216,8 +3253,6 @@ phase_check_contract (struct PayContext *pc)
res = TALER_MHD_parse_internal_json_data (pc->connection,
pc->contract_terms,
espec);
- if (NULL != fulfillment_url)
- pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
if (GNUNET_YES != res)
{
GNUNET_break (0);
@@ -3227,41 +3262,131 @@ phase_check_contract (struct PayContext *pc)
: MHD_NO);
return;
}
- }
-
- if (GNUNET_OK !=
- TALER_amount_cmp_currency (&pc->max_fee,
- &pc->amount))
- {
- GNUNET_break (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "'max_fee' in database does not match currency of contract
price"));
- return;
- }
-
- for (size_t i = 0; i<pc->coins_cnt; i++)
- {
- struct DepositConfirmation *dc = &pc->dc[i];
-
- if (GNUNET_OK !=
- TALER_amount_cmp_currency (&dc->cdd.amount,
- &pc->amount))
+ if (NULL != fulfillment_url)
+ pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
+ switch (version)
{
- GNUNET_break_op (0);
+ case 0:
+ {
+ struct GNUNET_JSON_Specification v0spec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &pc->details.v0.amount),
+ TALER_JSON_spec_amount_any ("max_fee",
+ &pc->details.v0.max_fee),
+ GNUNET_JSON_spec_end ()
+ };
+ res = TALER_MHD_parse_internal_json_data (pc->connection,
+ pc->contract_terms,
+ v0spec);
+ if (GNUNET_YES != res)
+ {
+ GNUNET_break (0);
+ pay_end (pc,
+ (GNUNET_NO == res)
+ ? MHD_YES
+ : MHD_NO);
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_cmp_currency (&pc->details.v0.max_fee,
+ &pc->details.v0.amount))
+ {
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "'max_fee' in database does not match currency of
contract price"));
+ return;
+ }
+
+ if (pc->choice_index > 0)
+ {
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_BAD_REQUEST,
+
TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
+ "contract terms v0 has no choices"));
+ return;
+ }
+ }
+ pc->version = TALER_MCV_V0;
+ break;
+ case 1:
+ {
+ struct GNUNET_JSON_Specification v1spec[] = {
+ TALER_JSON_spec_choices ("choices",
+ &pc->details.v1.choices,
+ &pc->details.v1.choices_len),
+ TALER_JSON_spec_token_families ("token_families",
+ &pc->details.v1.token_families,
+ &pc->details.v1.token_families_len),
+ GNUNET_JSON_spec_end ()
+ };
+ res = TALER_MHD_parse_internal_json_data (pc->connection,
+ pc->contract_terms,
+ v1spec);
+ if (GNUNET_YES != res)
+ {
+ GNUNET_break (0);
+ pay_end (pc,
+ (GNUNET_NO == res)
+ ? MHD_YES
+ : MHD_NO);
+ return;
+ }
+ if (pc->choice_index < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Order `%s' has non-empty choices array but"
+ "request is missing 'choice_index' field\n",
+ pc->order_id);
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING,
+ NULL));
+ return;
+ }
+ if (pc->choice_index >= pc->details.v1.choices_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Order `%s' has choices array with %u elements but "
+ "request has 'choice_index' field with value %ld\n",
+ pc->order_id,
+ pc->details.v1.choices_len,
+ pc->choice_index);
+ GNUNET_break (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_BAD_REQUEST,
+
TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
+ NULL));
+ return;
+ }
+ }
+ pc->version = TALER_MCV_V1;
+ break;
+ default:
+ GNUNET_break (0);
pay_end (pc,
TALER_MHD_reply_with_error (
pc->connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- pc->amount.currency));
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "contract 'version' in database not supported by this
backend")
+ );
return;
}
}
+
if (GNUNET_TIME_timestamp_cmp (pc->wire_transfer_deadline,
<,
pc->refund_deadline))
@@ -3288,7 +3413,7 @@ phase_check_contract (struct PayContext *pc)
return;
}
- /* Make sure wire method (still) exists for this instance */
+/* Make sure wire method (still) exists for this instance */
{
struct TMH_WireMethod *wm;
@@ -3753,25 +3878,32 @@ pay_context_cleanup (void *cls)
GNUNET_free (eg);
}
GNUNET_free (pc->egs);
- for (unsigned int i = 0; i< pc->token_families_len; i++)
+ switch (pc->version)
{
- struct TALER_MerchantContractTokenFamily *tf
- = &pc->token_families[i];
-
- GNUNET_free (tf->slug);
- GNUNET_free (tf->name);
- GNUNET_free (tf->description);
- json_decref (tf->description_i18n);
- for (unsigned int j = 0; j<tf->keys_len; j++)
+ case TALER_MCV_V0:
+ break;
+ case TALER_MCV_V1:
+ for (unsigned int i = 0; i< pc->details.v1.token_families_len; i++)
{
- struct TALER_MerchantContractTokenFamilyKey *key
- = &tf->keys[j];
+ struct TALER_MerchantContractTokenFamily *tf
+ = &pc->details.v1.token_families[i];
- TALER_token_issue_pub_free (&key->pub);
+ GNUNET_free (tf->slug);
+ GNUNET_free (tf->name);
+ GNUNET_free (tf->description);
+ json_decref (tf->description_i18n);
+ for (unsigned int j = 0; j<tf->keys_len; j++)
+ {
+ struct TALER_MerchantContractTokenFamilyKey *key
+ = &tf->keys[j];
+
+ TALER_token_issue_pub_free (&key->pub);
+ }
+ GNUNET_free (tf->keys);
}
- GNUNET_free (tf->keys);
+ GNUNET_free (pc->details.v1.token_families);
+ break;
}
- GNUNET_free (pc->token_families);
if (NULL != pc->response)
{
MHD_destroy_response (pc->response);
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c
b/src/backend/taler-merchant-httpd_private-post-orders.c
index 1cb11a15..98831226 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -2270,6 +2270,7 @@ compute_fee (struct OrderContext *oc,
return;
}
if ( (settings->use_stefan) &&
+ (NULL != max_stefan_fee) &&
(GNUNET_OK ==
TALER_amount_is_valid (max_stefan_fee)) )
{
@@ -2309,7 +2310,9 @@ set_max_fee (struct OrderContext *oc)
compute_fee (oc,
&oc->parse_choices.choices[i].amount,
&oc->parse_choices.choices[i].max_fee,
- &oc->set_exchanges.details.v1.max_stefan_fees[i],
+ NULL != oc->set_exchanges.details.v1.max_stefan_fees
+ ? &oc->set_exchanges.details.v1.max_stefan_fees[i]
+ : NULL,
&oc->set_max_fee.details.v1.max_fees[i]);
break;
default:
diff --git a/src/testing/testing_api_cmd_pay_order.c
b/src/testing/testing_api_cmd_pay_order.c
index 49dd7e81..67ea3fdd 100644
--- a/src/testing/testing_api_cmd_pay_order.c
+++ b/src/testing/testing_api_cmd_pay_order.c
@@ -559,8 +559,6 @@ pay_run (void *cls,
struct TALER_MerchantWireHashP h_wire;
const struct TALER_PrivateContractHashP *h_proposal;
struct TALER_Amount max_fee;
- const json_t *choices = NULL;
- const json_t *token_families = NULL;
const char *error_name = NULL;
unsigned int error_line = 0;
struct TALER_MERCHANT_PayCoin *pay_coins;
@@ -598,7 +596,12 @@ pay_run (void *cls,
{
/* Get information that needs to be put verbatim in the
* deposit permission */
+ uint64_t version = 0;
struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint64 ("version",
+ &version),
+ NULL),
GNUNET_JSON_spec_string ("order_id",
&order_id),
GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -611,18 +614,6 @@ pay_run (void *cls,
&merchant_pub),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&h_wire),
- TALER_JSON_spec_amount_any ("amount",
- &ps->total_amount),
- TALER_JSON_spec_amount_any ("max_fee",
- &max_fee),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_object_const ("token_families",
- &token_families),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("choices",
- &choices),
- NULL),
/* FIXME oec: parse minimum age, use data later? */
GNUNET_JSON_spec_end ()
};
@@ -645,6 +636,221 @@ pay_run (void *cls,
free (js);
TALER_TESTING_FAIL (is);
}
+ switch (version)
+ {
+ case 0:
+ {
+ struct GNUNET_JSON_Specification v0spec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &ps->total_amount),
+ TALER_JSON_spec_amount_any ("max_fee",
+ &max_fee),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (contract_terms,
+ v0spec,
+ &error_name,
+ &error_line))
+ {
+ char *js;
+
+ js = json_dumps (contract_terms,
+ JSON_INDENT (1));
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Parser failed on %s:%u for input `%s'\n",
+ error_name,
+ error_line,
+ js);
+ free (js);
+ TALER_TESTING_FAIL (is);
+ }
+ }
+ if (0 < ps->choice_index)
+ TALER_TESTING_FAIL (is);
+ break;
+ case 1:
+ {
+ const json_t *choices;
+ const json_t *token_families;
+ struct GNUNET_JSON_Specification v1spec[] = {
+ GNUNET_JSON_spec_object_const ("token_families",
+ &token_families),
+ GNUNET_JSON_spec_array_const ("choices",
+ &choices),
+ GNUNET_JSON_spec_end ()
+ };
+ const json_t *outputs;
+ json_t *output;
+ unsigned int output_index;
+ const json_t *choice;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (contract_terms,
+ v1spec,
+ &error_name,
+ &error_line))
+ {
+ char *js;
+
+ js = json_dumps (contract_terms,
+ JSON_INDENT (1));
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Parser failed on %s:%u for input `%s'\n",
+ error_name,
+ error_line,
+ js);
+ free (js);
+ TALER_TESTING_FAIL (is);
+ }
+
+ choice = json_array_get (choices,
+ ps->choice_index);
+ if (NULL == choice)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No choice found at index %d\n",
+ ps->choice_index);
+ TALER_TESTING_FAIL (is);
+ }
+
+ {
+ const char *ierror_name = NULL;
+ unsigned int ierror_line = 0;
+
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &ps->total_amount),
+ TALER_JSON_spec_amount_any ("max_fee",
+ &max_fee),
+ GNUNET_JSON_spec_array_const ("outputs",
+ &outputs),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (choice,
+ ispec,
+ &ierror_name,
+ &ierror_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Parser failed on %s:%u for input `%s'\n",
+ ierror_name,
+ ierror_line,
+ json_dumps (choice,
+ JSON_INDENT (2)));
+ TALER_TESTING_FAIL (is);
+ }
+ }
+
+ json_array_foreach (outputs, output_index, output)
+ {
+ const char *slug;
+ const char *kind;
+ uint32_t key_index;
+ uint32_t count = 1;
+ const char *ierror_name = NULL;
+ unsigned int ierror_line = 0;
+
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_string ("kind",
+ &kind),
+ GNUNET_JSON_spec_string ("token_family_slug",
+ &slug),
+ GNUNET_JSON_spec_uint32 ("key_index",
+ &key_index),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("count",
+ &count),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (output,
+ ispec,
+ &ierror_name,
+ &ierror_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Parser failed on %s:%u for input `%s'\n",
+ ierror_name,
+ ierror_line,
+ json_dumps (output,
+ JSON_INDENT (2)));
+ TALER_TESTING_FAIL (is);
+ }
+
+ if (0 != strcmp ("token", kind))
+ {
+ continue;
+ }
+
+ GNUNET_array_grow (ps->issued_tokens,
+ ps->num_issued_tokens,
+ ps->num_issued_tokens + count);
+
+ for (unsigned int k = 0; k < count; k++)
+ {
+ struct TALER_MERCHANT_PrivateTokenDetails *details =
+ &ps->issued_tokens[ps->num_issued_tokens - count + k];
+
+ if (GNUNET_OK !=
+ find_token_public_key (token_families,
+ slug,
+ key_index,
+ &details->issue_pub))
+ {
+ TALER_TESTING_FAIL (is);
+ }
+
+ /* Only RSA is supported for now. */
+ GNUNET_assert (GNUNET_CRYPTO_BSA_RSA ==
+ details->issue_pub.public_key->cipher);
+
+ TALER_token_blind_input_copy (&details->blinding_inputs,
+
TALER_token_blind_input_rsa_singleton ()
+ );
+ /* TODO: Where to get details->blinding_inputs from? */
+ TALER_token_use_setup_random (&details->master);
+ TALER_token_use_setup_priv (&details->master,
+ &details->blinding_inputs,
+ &details->token_priv);
+ TALER_token_use_blinding_secret_create (&details->master,
+ &details->blinding_inputs,
+ &details->blinding_secret);
+ GNUNET_CRYPTO_eddsa_key_get_public
(&details->token_priv.private_key
+ ,
+
&details->token_pub.public_key);
+ GNUNET_CRYPTO_hash (&details->token_pub.public_key,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+ &details->h_token_pub.hash);
+ details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign
+ (
+ details->issue_pub.public_key,
+ &details->blinding_secret,
+ NULL, /* TODO: Add session nonce to support CS tokens */
+ &details->h_token_pub.hash,
+ sizeof (details->h_token_pub.hash),
+ details->blinding_inputs.blinding_inputs);
+
+ if (NULL == details->envelope.blinded_pub)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_FAIL (is);
+ }
+ }
+ }
+ }
+
+ break;
+ default:
+ TALER_TESTING_FAIL (is);
+ }
+
+
}
{
@@ -688,143 +894,6 @@ pay_run (void *cls,
}
GNUNET_free (tr);
}
- if (0 <= ps->choice_index)
- {
- const json_t *outputs;
- json_t *output;
- unsigned int output_index;
- const json_t *choice = json_array_get (choices, ps->choice_index);
-
- if (NULL == choice)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "No choice found at index %d\n",
- ps->choice_index);
- TALER_TESTING_FAIL (is);
- }
-
- {
- const char *ierror_name = NULL;
- unsigned int ierror_line = 0;
-
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_array_const ("outputs",
- &outputs),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (choice,
- ispec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Parser failed on %s:%u for input `%s'\n",
- ierror_name,
- ierror_line,
- json_dumps (choice,
- JSON_INDENT (2)));
- TALER_TESTING_FAIL (is);
- }
- }
-
- json_array_foreach (outputs, output_index, output)
- {
- const char *slug;
- const char *kind;
- uint32_t key_index;
- uint32_t count = 1;
- const char *ierror_name = NULL;
- unsigned int ierror_line = 0;
-
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("kind",
- &kind),
- GNUNET_JSON_spec_string ("token_family_slug",
- &slug),
- GNUNET_JSON_spec_uint32 ("key_index",
- &key_index),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint32 ("count",
- &count),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (output,
- ispec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Parser failed on %s:%u for input `%s'\n",
- ierror_name,
- ierror_line,
- json_dumps (output,
- JSON_INDENT (2)));
- TALER_TESTING_FAIL (is);
- }
-
- if (0 != strcmp ("token", kind))
- {
- continue;
- }
-
- GNUNET_array_grow (ps->issued_tokens,
- ps->num_issued_tokens,
- ps->num_issued_tokens + count);
-
- for (unsigned int k = 0; k < count; k++)
- {
- struct TALER_MERCHANT_PrivateTokenDetails *details =
- &ps->issued_tokens[ps->num_issued_tokens - count + k];
-
- if (GNUNET_OK !=
- find_token_public_key (token_families,
- slug,
- key_index,
- &details->issue_pub))
- {
- TALER_TESTING_FAIL (is);
- }
-
- /* Only RSA is supported for now. */
- GNUNET_assert (GNUNET_CRYPTO_BSA_RSA ==
- details->issue_pub.public_key->cipher);
-
- TALER_token_blind_input_copy (&details->blinding_inputs,
- TALER_token_blind_input_rsa_singleton
());
- /* TODO: Where to get details->blinding_inputs from? */
- TALER_token_use_setup_random (&details->master);
- TALER_token_use_setup_priv (&details->master,
- &details->blinding_inputs,
- &details->token_priv);
- TALER_token_use_blinding_secret_create (&details->master,
- &details->blinding_inputs,
- &details->blinding_secret);
- GNUNET_CRYPTO_eddsa_key_get_public (&details->token_priv.private_key,
- &details->token_pub.public_key);
- GNUNET_CRYPTO_hash (&details->token_pub.public_key,
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
- &details->h_token_pub.hash);
- details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign (
- details->issue_pub.public_key,
- &details->blinding_secret,
- NULL, /* TODO: Add session nonce to support CS tokens */
- &details->h_token_pub.hash,
- sizeof (details->h_token_pub.hash),
- details->blinding_inputs.blinding_inputs);
-
- if (NULL == details->envelope.blinded_pub)
- {
- GNUNET_break (0);
- TALER_TESTING_FAIL (is);
- }
- }
- }
- }
GNUNET_array_grow (output_tokens,
len_output_tokens,
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-merchant] branch master updated: fix pay handling for v1 contracts, including in test,
gnunet <=