commit ce56e46e30e9f65ce60042d4b8df1e95ff0cfd24
parent 1a5f7b296e1c7440a1bba164de503196e479c320
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Tue, 11 Mar 2025 11:53:31 +0100
moving the processing of the tokens to the new function
Diffstat:
1 file changed, 227 insertions(+), 80 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -2726,6 +2726,217 @@ find_family (const struct PayContext *pc,
/**
+ * Handle contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN.
+ * Looks up the token family, loads the matching private key,
+ * and signs the corresponding token envelopes from the wallet.
+ *
+ * @param pc context for the pay request
+ * @param output contract output we need to process
+ * @param output_index index of this output in the contract's outputs array
+ * @return GNUNET_OK on success, GNUNET_NO if an error was encountered
+ */
+static enum GNUNET_GenericReturnValue
+handle_output_token(struct PayContext *pc,
+ const struct TALER_MERCHANT_ContractOutput *output,
+ unsigned int output_index)
+{
+ const struct TALER_MERCHANT_ContractTokenFamily *family;
+ struct TALER_MERCHANT_ContractTokenFamilyKey *key;
+ struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
+ enum GNUNET_DB_QueryStatus qs;
+ bool mandatory;
+
+ /* Locate token family in the contract. This should never fail if
+ * the contract passed validation before insertion. */
+ family = find_family(pc,
+ output->details.token.token_family_slug);
+ if (NULL == family)
+ {
+ /* This "should never happen", so treat it as an internal error */
+ 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 GNUNET_NO; /* signals error */
+ }
+
+ /* Check the key_index field from the output. */
+ if (output->details.token.key_index >= family->keys_len)
+ {
+ /* Also "should never happen", contract was presumably 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 GNUNET_NO;
+ }
+
+ /* Pick the correct key inside that family. */
+ key = &family->keys[output->details.token.key_index];
+
+ /* Fetch the private key from the DB for the merchant instance and
+ * this particular family/time interval. */
+ qs = TMH_db->lookup_token_family_key(
+ TMH_db->cls,
+ pc->hc->instance->settings.id,
+ family->slug,
+ pc->check_contract.contract_terms->timestamp,
+ pc->check_contract.contract_terms->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->check_contract.contract_terms->timestamp.abs_time.abs_value_us,
+ (unsigned long long) pc->check_contract.contract_terms->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 GNUNET_NO;
+ }
+ GNUNET_assert(NULL != details.priv.private_key);
+
+ /* Depending on the token family, decide if the token envelope
+ * is mandatory or optional. (Simplified logic here: adapt as needed.) */
+ mandatory = test_tfk_mandatory(details.token_family.kind);
+
+ /* Actually sign the number of token envelopes specified in 'count'.
+ * 'output_index' is the offset into the parse_wallet_data arrays. */
+ if (GNUNET_OK !=
+ sign_token_envelopes(pc,
+ key,
+ &details.priv,
+ mandatory,
+ output_index,
+ output->details.token.count))
+ {
+ /* sign_token_envelopes() already queued up an error via pay_end() */
+ return GNUNET_NO;
+ }
+
+ /* Everything OK. */
+ return GNUNET_OK;
+}
+
+/**
+ * Handle contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN.
+ * Looks up the token family, loads the matching private key,
+ * and signs the corresponding token envelopes from the wallet.
+ *
+ * @param pc context for the pay request
+ * @param output contract output we need to process
+ * @param output_index index of this output in the contract's outputs array
+ * @return GNUNET_OK on success, GNUNET_NO if an error was encountered
+ */
+static enum GNUNET_GenericReturnValue
+handle_output_validate_token(struct PayContext *pc,
+ const struct TALER_MERCHANT_ContractOutput *output,
+ unsigned int output_index)
+{
+ const struct TALER_MERCHANT_ContractTokenFamily *family;
+ struct TALER_MERCHANT_ContractTokenFamilyKey *key;
+ struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
+ enum GNUNET_DB_QueryStatus qs;
+ bool mandatory;
+
+ /* Locate token family in the contract. This should never fail if
+ * the contract passed validation before insertion. */
+ family = find_family(pc,
+ output->details.token.token_family_slug);
+ if (NULL == family)
+ {
+ /* This "should never happen", so treat it as an internal error */
+ 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 GNUNET_NO; /* signals error */
+ }
+
+ /* Check the key_index field from the output. */
+ if (output->details.token.key_index >= family->keys_len)
+ {
+ /* Also "should never happen", contract was presumably 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 GNUNET_NO;
+ }
+
+ /* Pick the correct key inside that family. */
+ key = &family->keys[output->details.token.key_index];
+
+ /* Fetch the private key from the DB for the merchant instance and
+ * this particular family/time interval. */
+ qs = TMH_db->lookup_token_family_key(
+ TMH_db->cls,
+ pc->hc->instance->settings.id,
+ family->slug,
+ pc->check_contract.contract_terms->timestamp,
+ pc->check_contract.contract_terms->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->check_contract.contract_terms->timestamp.abs_time.abs_value_us,
+ (unsigned long long) pc->check_contract.contract_terms->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 GNUNET_NO;
+ }
+ GNUNET_assert(NULL != details.priv.private_key);
+
+ /* Depending on the token family, decide if the token envelope
+ * is mandatory or optional. (Simplified logic here: adapt as needed.) */
+ mandatory = test_tfk_mandatory(details.token_family.kind);
+
+ /* Actually sign the number of token envelopes specified in 'count'.
+ * 'output_index' is the offset into the parse_wallet_data arrays. */
+ if (GNUNET_OK !=
+ sign_token_envelopes(pc,
+ key,
+ &details.priv,
+ mandatory,
+ output_index,
+ output->details.token.count))
+ {
+ /* sign_token_envelopes() already queued up an error via pay_end() */
+ return GNUNET_NO;
+ }
+
+ /* Everything OK. */
+ return GNUNET_OK;
+}
+
+
+/**
* Validate tokens and token envelopes. First, we check if all tokens listed
* in the 'inputs' array of the selected choice are present in the 'tokens'
* array of the request. Then, we validate the signatures of each provided
@@ -2799,92 +3010,28 @@ phase_validate_tokens (struct PayContext *pc)
for (unsigned int i = 0; i<selected->outputs_len; i++)
{
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
const struct TALER_MERCHANT_ContractOutput *output
= &selected->outputs[i];
- const struct TALER_MERCHANT_ContractTokenFamily *family;
- struct TALER_MERCHANT_ContractTokenFamilyKey *key;
// FIXME: check donau outputs are good choices
// (allowed donau, total amount below max, correct year, ...)
// change 'if' to switch...
- if (output->type != TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN)
- {
- /* only validate outputs of type tokens (for now) */
- continue;
- }
- // FIXME: move this into a function for the switch case on token...
- 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->check_contract.contract_terms->timestamp,
- pc->check_contract.contract_terms->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->check_contract.contract_terms->timestamp.
- abs_time.
- abs_value_us,
- (unsigned long long) pc->check_contract.contract_terms->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;
- }
-
- GNUNET_assert (NULL != details.priv.private_key);
- if (GNUNET_OK !=
- 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;
+ switch(output->type){
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
+ if (GNUNET_NO ==
+ handle_output_validate_token (pc,
+ output,
+ i))
+ {
+ /* Error is already scheduled from handle_output_token. */
+ return;
+ }
+ break;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
+ //FIXME: Add function to process donations
+ continue;
+ default:
+ continue;
}
}
}