commit ddbe83488339297e5c887c277ea2e28782cd5597
parent 09619ce12ab88c6e024f2d56951afb8005b76845
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Thu, 14 Aug 2025 09:14:00 +0200
first attempt to sort out output token mess in the pay logic
Diffstat:
1 file changed, 268 insertions(+), 343 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
@@ -125,10 +125,12 @@ enum PayPhase
*/
PP_PAY_TRANSACTION,
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
/**
* Communicate with DONAU to generate a donation receipt from the donor BUDIs.
*/
PP_REQUEST_DONATION_RECEIPT,
+#endif
/**
* Process the donation receipt response from DONAU (save the donau_sigs to the db).
@@ -439,6 +441,18 @@ struct PayContext
struct MHD_Response *response;
/**
+ * Array with @e output_tokens_len signed tokens returned in
+ * the response to the wallet.
+ */
+ struct SignedOutputToken *output_tokens;
+
+ /**
+ * Number of output tokens to return in the response.
+ * Length of the @e output_tokens array.
+ */
+ unsigned int output_tokens_len;
+
+ /**
* 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).
@@ -658,18 +672,6 @@ struct PayContext
*/
struct TALER_Amount brutto;
- /**
- * Array with @e output_tokens_len signed tokens returned in
- * the response to the wallet.
- */
- struct SignedOutputToken *output_tokens;
-
- /**
- * Number of output tokens to return in the response.
- * Length of the @e output_tokens array.
- */
- unsigned int output_tokens_len;
-
} validate_tokens;
/**
@@ -747,7 +749,7 @@ struct PayContext
} batch_deposits;
- #ifdef HAVE_DONAU_DONAU_SERVICE_H
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
/**
* Struct for #phase_request_donation_receipt()
*/
@@ -758,22 +760,8 @@ struct PayContext
*/
struct DONAU_BatchIssueReceiptHandle *birh;
- /**
- * Receipts for the donations from the Donau.
- */
- struct DONAU_BlindedDonationUnitSignature *sigs;
-
- /**
- * Number of receipts in @e sigs for the donations from the Donau.
- */
- unsigned int num_sigs;
-
- /**
- * The donation receipt in json format.
- */
- json_t *donau_sigs_json;
} donau_receipt;
- #endif
+#endif
};
@@ -1770,19 +1758,21 @@ phase_batch_deposits (struct PayContext *pc)
static json_t *
build_token_sigs (struct PayContext *pc)
{
- json_t *token_sigs = json_array ();
+ json_t *token_sigs;
+ if (0 == pc->output_tokens_len)
+ return NULL;
+ token_sigs = json_array ();
GNUNET_assert (NULL != token_sigs);
- for (unsigned int i = 0; i < pc->validate_tokens.output_tokens_len; i++)
+ for (unsigned int i = 0; i < pc->output_tokens_len; i++)
{
- /* FIXME: Do we want to output hash? */
GNUNET_assert (0 ==
json_array_append_new (
token_sigs,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_blinded_sig (
"blind_sig",
- pc->validate_tokens.output_tokens[i].sig.signature)
+ pc->output_tokens[i].sig.signature)
)));
}
return token_sigs;
@@ -1799,7 +1789,6 @@ phase_success_response (struct PayContext *pc)
{
struct TALER_MerchantSignatureP sig;
char *pos_confirmation;
- json_t *token_sigs;
/* Sign on our end (as the payment did go through, even if it may
have been refunded already) */
@@ -1814,10 +1803,6 @@ phase_success_response (struct PayContext *pc)
&pc->validate_tokens.brutto,
pc->check_contract.contract_terms->timestamp
);
- token_sigs = (0 >= pc->validate_tokens.output_tokens_len)
- ? NULL
- : build_token_sigs (pc);
-
pay_end (pc,
TALER_MHD_REPLY_JSON_PACK (
pc->connection,
@@ -1827,7 +1812,7 @@ phase_success_response (struct PayContext *pc)
pos_confirmation)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_array_steal ("token_sigs",
- token_sigs)),
+ build_token_sigs (pc))),
GNUNET_JSON_pack_data_auto ("sig",
&sig)));
GNUNET_free (pos_confirmation);
@@ -1890,165 +1875,35 @@ phase_payment_notification (struct PayContext *pc)
}
-#ifdef HAVE_DONAU_DONAU_SERVICE_H
-/**
- * Parse signatures to JSON.
- *
- * @param num_sig number of signatures
- * @param signatures Blinded donation unit signatures
- * @param[out] j_signatures JSON object
- * @return true if all is fine,
- * false if we could not compose the JSON
- * is malformed.
- */
-static bool
-signatures_to_json (size_t num_sig,
- const struct DONAU_BlindedDonationUnitSignature *signatures,
- json_t *j_signatures)
-{
- for (size_t i = 0; i < num_sig; i++)
- {
- const struct DONAU_BlindedDonationUnitSignature *signature = &signatures[i];
-
- json_t *sig_obj = GNUNET_JSON_PACK (
- DONAU_JSON_pack_blinded_donation_unit_sig ("blinded_signature",
- signature));
- if (NULL == sig_obj)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to pack blinded signature #%zu",
- i);
- return false;
- }
- if (0 != json_array_append_new (j_signatures,
- sig_obj))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to append blinded signature #%zu to JSON array",
- i);
- json_decref (sig_obj);
- return false;
- }
- }
- return true;
-}
-
-
-#endif /* HAVE_DONAU_DONAU_SERVICE_H */
-
-
/**
- * Phase to process received donation receipts and store them into the database.
+ * Phase to write all outputs to our database so we do
+ * not re-request them in case the client re-plays the
+ * request.
*
- * @param pc payment context
+ * @param[in,out] pc payment context
*/
static void
phase_final_output_token_processing (struct PayContext *pc)
{
-#ifdef HAVE_DONAU_DONAU_SERVICE_H
- if (pc->parse_wallet_data.num_bkps > 0)
+ if (0 == pc->output_tokens_len)
{
- if (pc->parse_wallet_data.num_bkps !=
- pc->donau_receipt.num_sigs)
- { /* Not supposed to happen, as it is already checked by donau c-api, but just in case */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Mismatch in wallet_data.num_bkps %llu and "
- "donau_receipt.num_sigs %llu",
- (unsigned long long) pc->parse_wallet_data.num_bkps,
- (unsigned long long) pc->donau_receipt.num_sigs);
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "Mismatch between blinded key pairs and donation signatures received from donau"));
- return;
- }
+ pc->phase++;
+ return;
+ }
+ for (unsigned int retry = 0; retry < MAX_RETRIES; retry++)
+ {
+ enum GNUNET_DB_QueryStatus qs;
- if ( (NULL == pc->donau_receipt.donau_sigs_json) ||
- (! signatures_to_json (pc->donau_receipt.num_sigs,
- pc->donau_receipt.sigs,
- pc->donau_receipt.donau_sigs_json)) )
+ TMH_db->preflight (TMH_db->cls);
+ if (GNUNET_OK !=
+ TMH_db->start (TMH_db->cls,
+ "insert_order_blinded_sigs"))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to convert Donau signatures to JSON (order %s)",
- pc->order_id);
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_JSON_INVALID,
- "donau signature JSON creation failure"));
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "start insert_order_blinded_sigs_failed");
+ pc->phase++;
return;
}
-
- /* Attaching sigs from the donau to output_tokens*/
- uint64_t d_out = -1;
- const struct TALER_MERCHANT_ContractChoice *choice =
- &pc->check_contract.contract_terms->details.v1
- .choices[pc->parse_wallet_data.choice_index];
-
- for (size_t i = 0; i < pc->validate_tokens.output_tokens_len; i++)
- if (i < choice->outputs_len &&
- TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT ==
- choice->outputs[i].type)
- {
- d_out = i;
- break;
- }
- GNUNET_assert (-1 != d_out);
-
- {
- uint64_t ot_len
- = pc->validate_tokens.output_tokens_len;
- uint64_t ot_len_with_donau_sig
- = pc->validate_tokens.output_tokens_len + pc->donau_receipt.num_sigs
- - 1;
-
- if (pc->donau_receipt.num_sigs > 1)
- {
- // FIXME: completely wrong use of grow ahead.
- GNUNET_array_grow (pc->validate_tokens.output_tokens,
- pc->validate_tokens.output_tokens_len,
- ot_len_with_donau_sig);
-
- memmove (
- &pc->validate_tokens.output_tokens[d_out
- + pc->donau_receipt.num_sigs],
- &pc->validate_tokens.output_tokens[d_out + 1],
- (ot_len - d_out - 1) * sizeof (struct SignedOutputToken));
- }
-
- for (unsigned int i = 0; i < pc->donau_receipt.num_sigs; i++)
- {
- struct SignedOutputToken *ot =
- &pc->validate_tokens.output_tokens[d_out + i];
-
- ot->sig.signature = pc->donau_receipt.sigs[i].blinded_sig;
- ot->h_issue.hash =
- pc->parse_wallet_data.bkps[i].h_donation_unit_pub.hash;
-
- }
- pc->validate_tokens.output_tokens_len = ot_len_with_donau_sig;
- }
- }
-#endif
- /* Part of saving the output_token sigs so that on re-pay we can fetch them from db*/
- enum GNUNET_DB_QueryStatus qs;
- unsigned int retry;
-
- TMH_db->preflight (TMH_db->cls);
- if (GNUNET_OK != TMH_db->start (TMH_db->cls,
- "insert_order_blinded_sigs"))
- {
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_START_FAILED,
- "start insert blinded donation receipts failed"));
- return;
- }
-
- for (retry = 0; retry < MAX_RETRIES; retry++)
- {
#ifdef HAVE_DONAU_DONAU_SERVICE_H
if (pc->parse_wallet_data.num_bkps > 0)
{
@@ -2056,94 +1911,157 @@ phase_final_output_token_processing (struct PayContext *pc)
TMH_db->cls,
&pc->parse_wallet_data.donau_instance_serial,
&pc->parse_wallet_data.charity_receipts_to_date);
-
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- TMH_db->rollback (TMH_db->cls);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Retrying update of Donau instance receipts amount");
- continue;
- }
- if (0 >= qs)
+ switch (qs)
{
+ case GNUNET_DB_STATUS_HARD_ERROR:
TMH_db->rollback (TMH_db->cls);
- pay_end (pc,
- TALER_MHD_reply_with_error (
- pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "update donau instance receipts amount"));
+ GNUNET_break (0);
return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TMH_db->rollback (TMH_db->cls);
+ continue;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* weird for an update */
+ GNUNET_break (0);
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
}
}
-#endif /* HAVE_DONAU_DONAU_SERVICE_H*/
-
+#endif
for (unsigned int i = 0;
- i < pc->validate_tokens.output_tokens_len;
+ i < pc->output_tokens_len;
i++)
{
qs = TMH_db->insert_order_blinded_sigs (
TMH_db->cls,
pc->order_id,
i,
- &pc->validate_tokens.output_tokens[i].h_issue.hash,
- pc->validate_tokens.output_tokens[i].sig.signature);
-
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- TMH_db->rollback (TMH_db->cls);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Retrying insert blinded token signature");
- continue;
- }
+ &pc->output_tokens[i].h_issue.hash,
+ pc->output_tokens[i].sig.signature);
- if (0 >= qs)
+ switch (qs)
{
+ case GNUNET_DB_STATUS_HARD_ERROR:
TMH_db->rollback (TMH_db->cls);
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "insert blinded donation receipts")
- );
+ pc->phase++;
return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TMH_db->rollback (TMH_db->cls);
+ goto OUTER;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* weird for an update */
+ GNUNET_break (0);
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
}
- }
-
+ } /* for i */
qs = TMH_db->commit (TMH_db->cls);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ switch (qs)
{
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ TMH_db->rollback (TMH_db->cls);
+ pc->phase++;
+ return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
TMH_db->rollback (TMH_db->cls);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Retrying commit blinded donation receipts");
continue;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ pc->phase++;
+ return; /* success */
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ pc->phase++;
+ return; /* success */
}
- if (0 > qs)
+ GNUNET_break (0);
+ pc->phase++;
+ return; /* strange */
+OUTER:
+ } /* for retry */
+ TMH_db->rollback (TMH_db->cls);
+ pc->phase++;
+ /* We continue anyway, as there is not much we can
+ do here: the Donau *did* issue us the receipts;
+ also, we'll eventually ask the Donau for the
+ balance and get the correct one. Plus, we were
+ paid by the client, so it's technically all still
+ OK. If the request fails anyway, the wallet will
+ most likely replay the request and then hopefully
+ we will succeed the next time */
+}
+
+
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
+
+/**
+ * Add donation receipt outputs to the output_tokens.
+ *
+ * Note that under the current (odd, bad) libdonau
+ * API *we* are responsible for freeing blinded_sigs,
+ * so we truly own that array!
+ *
+ * @param[in,out] pc payment context
+ * @param num_blinded_sigs number of signatures received
+ * @param blinded_sigs blinded signatures from Donau
+ */
+static void
+add_donation_receipt_outputs (
+ struct PayContext *pc,
+ size_t num_blinded_sigs,
+ struct DONAU_BlindedDonationUnitSignature *blinded_sigs)
+{
+ const struct TALER_MERCHANT_ContractChoice *choice
+ = &pc->check_contract.contract_terms->details.v1.choices[
+ pc->parse_wallet_data.choice_index];
+ unsigned int off = 0;
+
+ GNUNET_assert (pc->parse_wallet_data.num_bkps ==
+ num_blinded_sigs);
+
+ for (unsigned int i = 0; i < choice->outputs_len; i++)
+ {
+ const struct TALER_MERCHANT_ContractOutput *output
+ = &choice->outputs[i];
+
+ switch (output->type)
{
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
- "commit blinded donation receipts"));
- return;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
+ GNUNET_assert (0);
+ break;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
+ GNUNET_assert (off + output->details.token.count >= off);
+ off += output->details.token.count;
+ continue;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
+ break;
+#if FUTURE
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
+ GNUNET_assert (0); // FIXME: return not implemented
+ continue;
+#endif
}
+ /* must have been the donau case we care about */
break;
}
- if (retry == MAX_RETRIES)
+ GNUNET_assert (off + num_blinded_sigs >= off);
+ GNUNET_assert (off + num_blinded_sigs <= pc->output_tokens_len);
+ for (unsigned int i = 0; i<pc->output_tokens_len; i++)
{
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- "insert blinded donation receipts retry exhausted"));
- return;
+ struct SignedOutputToken *sot
+ = &pc->output_tokens[off + i];
+
+ // FIXME: in the future, use incref here once
+ // we change libdonau to do decref!
+ sot->sig.signature = blinded_sigs[i].blinded_sig;
+ sot->h_issue.hash = pc->parse_wallet_data.bkps[i].h_donation_unit_pub.hash;
}
- pc->phase = PP_PAYMENT_NOTIFICATION;
+ // FIXME: do this in libdonau in the future!
+ GNUNET_free (blinded_sigs);
}
-#ifdef HAVE_DONAU_DONAU_SERVICE_H
/**
* Callback to handle the result of a batch issue request.
*
@@ -2151,14 +2069,14 @@ phase_final_output_token_processing (struct PayContext *pc)
* @param resp the response from Donau
*/
static void
-merchant_donau_issue_receipt_cb (void *cls,
- const struct DONAU_BatchIssueResponse *resp)
+merchant_donau_issue_receipt_cb (
+ void *cls,
+ const struct DONAU_BatchIssueResponse *resp)
{
struct PayContext *pc = cls;
/* Donau replies asynchronously, so we expect the PayContext
* to be suspended. */
GNUNET_assert (GNUNET_YES == pc->suspended);
-
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Donau responded with status=%u, ec=%u",
resp->hr.http_status,
@@ -2176,23 +2094,20 @@ merchant_donau_issue_receipt_cb (void *cls,
case MHD_HTTP_OK:
case MHD_HTTP_CREATED:
if (TALER_EC_NONE != resp->hr.ec)
+ {
/* Most probably, it is just some small flaw from
* donau so no point in failing, yet we have to display it */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Donau signalled error %u despite HTTP %u",
resp->hr.ec,
resp->hr.http_status);
-
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Donau accepted donation receipts with total_issued=%s",
TALER_amount2s (&resp->details.ok.issued_amount));
-
- pc->donau_receipt.sigs =
- resp->details.ok.blinded_sigs;
- pc->donau_receipt.num_sigs =
- resp->details.ok.num_blinded_sigs;
- pc->donau_receipt.donau_sigs_json =
- json_array ();
+ add_donation_receipt_outputs (pc,
+ resp->details.ok.num_blinded_sigs,
+ resp->details.ok.blinded_sigs);
pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING;
pay_resume (pc);
return;
@@ -2247,32 +2162,31 @@ merchant_parse_json_bkp (struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp,
}
-#endif
-
-
/**
* Generate a donation signature for the bkp and charity.
*
- * @param pc payment context containing the charity and bkps
+ * @param[in,out] pc payment context containing the charity and bkps
*/
static void
phase_request_donation_receipt (struct PayContext *pc)
{
-#ifndef HAVE_DONAU_DONAU_SERVICE_H
- /* If Donau is disabled at compile-time, skip. */
- pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING;
- return;
-#else
+ if ( (NULL == pc->parse_wallet_data.donau.donau_url) ||
+ (0 == pc->parse_wallet_data.num_bkps) )
+ {
+ pc->phase++;
+ return;
+ }
pc->donau_receipt.birh =
- DONAU_charity_issue_receipt (TMH_curl_ctx,
- pc->parse_wallet_data.donau.donau_url,
- &pc->parse_wallet_data.charity_priv,
- pc->parse_wallet_data.charity_id,
- pc->parse_wallet_data.donau.donation_year,
- pc->parse_wallet_data.num_bkps,
- pc->parse_wallet_data.bkps,
- &merchant_donau_issue_receipt_cb,
- pc);
+ DONAU_charity_issue_receipt (
+ TMH_curl_ctx,
+ pc->parse_wallet_data.donau.donau_url,
+ &pc->parse_wallet_data.charity_priv,
+ pc->parse_wallet_data.charity_id,
+ pc->parse_wallet_data.donau.donation_year,
+ pc->parse_wallet_data.num_bkps,
+ pc->parse_wallet_data.bkps,
+ &merchant_donau_issue_receipt_cb,
+ pc);
if (NULL == pc->donau_receipt.birh)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -2286,10 +2200,12 @@ phase_request_donation_receipt (struct PayContext *pc)
}
MHD_suspend_connection (pc->connection);
pc->suspended = GNUNET_YES;
-#endif
}
+#endif
+
+
/**
* Function called with information about a coin that was deposited.
*
@@ -2897,7 +2813,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
&pc->check_contract.contract_terms->details.v1
.choices[pc->parse_wallet_data.choice_index];
- for (size_t i = 0; i<pc->validate_tokens.output_tokens_len; i++)
+ for (size_t i = 0; i<pc->output_tokens_len; i++)
{
switch (choice->outputs[i].type)
{
@@ -2916,7 +2832,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
break;
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
struct SignedOutputToken *output =
- &pc->validate_tokens.output_tokens[i];
+ &pc->output_tokens[i];
enum GNUNET_DB_QueryStatus qs;
qs = TMH_db->insert_issued_token (TMH_db->cls,
@@ -3008,19 +2924,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
return;
}
}
-
- if (NULL != pc->parse_wallet_data.donau.donau_url)
- {
- pc->phase = PP_REQUEST_DONATION_RECEIPT;
- }
- else if (pc->validate_tokens.output_tokens_len > 0)
- {
- pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING;
- }
- else
- {
- pc->phase = PP_PAYMENT_NOTIFICATION;
- }
+ pc->phase++;
}
@@ -3152,6 +3056,7 @@ find_valid_input_tokens (
* @return true if such outputs are mandatory and wallets must supply
* the corresponding blinded input
*/
+/* FIXME: this function belongs into a lower-level lib! */
static bool
test_tfk_mandatory (enum TALER_MERCHANTDB_TokenFamilyKind tfk)
{
@@ -3197,10 +3102,10 @@ sign_token_envelopes (struct PayContext *pc,
const struct TokenEnvelope *env
= &pc->parse_wallet_data.token_envelopes[pos];
struct SignedOutputToken *output
- = &pc->validate_tokens.output_tokens[pos];
+ = &pc->output_tokens[pos];
if ( (pos >= pc->parse_wallet_data.token_envelopes_cnt) ||
- (pos >= pc->validate_tokens.output_tokens_len) )
+ (pos >= pc->output_tokens_len) )
{
GNUNET_assert (0); /* this should not happen */
return GNUNET_NO;
@@ -3407,31 +3312,23 @@ handle_output_token (struct PayContext *pc,
}
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
/**
* Handle checks for contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT.
* For now, this does nothing and simply returns #GNUNET_OK.
*
* @param pc context for the pay request
* @param output the contract output describing the donation receipt requirement
- * @param output_index index of this output in the contract's outputs array
* @return #GNUNET_OK unconditionally (placeholder)
*/
static enum GNUNET_GenericReturnValue
handle_output_donation_receipt (
struct PayContext *pc,
- const struct TALER_MERCHANT_ContractOutput *output,
- unsigned int output_index)
+ const struct TALER_MERCHANT_ContractOutput *output)
{
-#ifndef HAVE_DONAU_DONAU_SERVICE_H
- /* If you ended up here, I want to know how... - Bohdan */
- GNUNET_break_op (0);
- pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_NOT_IMPLEMENTED,
- TALER_EC_MERCHANT_GENERIC_FEATURE_NOT_AVAILABLE,
- "donau support disabled"));
- return GNUNET_NO;
-#else
+ // FIXME: double-check that this logic checks
+ // correctly the total amount the BUDIs are
+ // requesting donation receipts for!
if (GNUNET_OK !=
DONAU_get_donation_amount_from_bkps (
pc->parse_wallet_data.donau_keys,
@@ -3442,10 +3339,11 @@ handle_output_donation_receipt (
{
GNUNET_break_op (0);
pay_end (pc,
- TALER_MHD_reply_with_error (pc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "inconsistent bkps / donau keys"));
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "inconsistent bkps / donau keys"));
return GNUNET_NO;
}
@@ -3463,8 +3361,9 @@ handle_output_donation_receipt (
return GNUNET_NO;
}
- if (0 != TALER_amount_cmp (&pc->parse_wallet_data.donation_amount,
- &output->details.donation_receipt.amount))
+ if (0 !=
+ TALER_amount_cmp (&pc->parse_wallet_data.donation_amount,
+ &output->details.donation_receipt.amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wallet amount: %s\n",
@@ -3483,6 +3382,7 @@ handle_output_donation_receipt (
}
{
struct TALER_Amount receipts_to_date;
+
if (0 >
TALER_amount_add (&receipts_to_date,
&pc->parse_wallet_data.charity_receipts_to_date,
@@ -3512,10 +3412,12 @@ handle_output_donation_receipt (
pc->parse_wallet_data.charity_receipts_to_date = receipts_to_date;
}
return GNUNET_OK;
-#endif /* HAVE_DONAU_DONAU_SERVICE_H */
}
+#endif /* HAVE_DONAU_DONAU_SERVICE_H */
+
+
/**
* 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'
@@ -3570,8 +3472,7 @@ phase_validate_tokens (struct PayContext *pc)
TALER_MHD_reply_with_error (
pc->connection,
MHD_HTTP_NOT_IMPLEMENTED,
- /* FIXME: fix EC here... */
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ TALER_EC_MERCHANT_GENERIC_FEATURE_NOT_AVAILABLE,
"token type not yet supported"));
return;
#endif
@@ -3603,13 +3504,37 @@ phase_validate_tokens (struct PayContext *pc)
}
}
- // FIXME: this is wrong, as it doesn't consider
- // that some outputs in the outputs_len array
- // have a count > 1!
- GNUNET_array_grow (pc->validate_tokens.output_tokens,
- pc->validate_tokens.output_tokens_len,
- selected->outputs_len);
+ /* calculate pc->output_tokens_len */
+ for (unsigned int i = 0; i<selected->outputs_len; i++)
+ {
+ const struct TALER_MERCHANT_ContractOutput *output
+ = &selected->outputs[i];
+ switch (output->type)
+ {
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
+ GNUNET_assert (0);
+ break;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
+ // FIXME: replace assert by returning 400
+ GNUNET_assert (pc->output_tokens_len + output->details.token.count
+ >= pc->output_tokens_len);
+ pc->output_tokens_len += output->details.token.count;
+ break;
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
+ // FIXME: replace assert by returning 400
+ // FIXME: also check that this output type only appears ONCE
+ GNUNET_assert (pc->output_tokens_len + pc->parse_wallet_data.num_bkps
+ >= pc->output_tokens_len);
+ pc->output_tokens_len += pc->parse_wallet_data.num_bkps;
+ break;
+ }
+ }
+
+ pc->output_tokens
+ = GNUNET_new_array (pc->output_tokens_len,
+ struct SignedOutputToken);
+ /* compute non-donau outputs */
for (unsigned int i = 0; i<selected->outputs_len; i++)
{
const struct TALER_MERCHANT_ContractOutput *output
@@ -3617,6 +3542,9 @@ phase_validate_tokens (struct PayContext *pc)
switch (output->type)
{
+ case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
+ GNUNET_assert (0);
+ break;
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
if (GNUNET_OK !=
handle_output_token (pc,
@@ -3628,21 +3556,32 @@ phase_validate_tokens (struct PayContext *pc)
}
break;
case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
+#ifndef HAVE_DONAU_DONAU_SERVICE_H
+ if (0 == pc->parse_wallet_data.num_bkps)
+ return; /* wallet didn't want donau, so OK! */
+ GNUNET_break_op (0);
+ pay_end (pc,
+ TALER_MHD_reply_with_error (
+ pc->connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_MERCHANT_GENERIC_FEATURE_NOT_AVAILABLE,
+ "donau support disabled"));
+ return;
+#else
if (GNUNET_OK !=
handle_output_donation_receipt (pc,
- output,
- i))
+ output))
{
/* Error is already scheduled from handle_output_donation_receipt. */
return;
}
continue;
- default:
- continue;
- }
- }
- }
- }
+#endif
+ } /* switch on output token */
+ } /* for all output token types */
+ } /* case contract v1 */
+ break;
+ } /* switch on contract type */
for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
{
@@ -3653,11 +3592,6 @@ phase_validate_tokens (struct PayContext *pc)
&pc->validate_tokens.brutto))
{
GNUNET_break_op (0);
- fprintf (stderr,
- "HERE (%u): %s != %s\n",
- (unsigned int) pc->check_contract.contract_terms->version,
- dc->cdd.amount.currency,
- TALER_amount2s (&pc->validate_tokens.brutto));
pay_end (pc,
TALER_MHD_reply_with_error (
pc->connection,
@@ -3781,8 +3715,8 @@ append_output_token_sig (void *cls,
out.h_issue.hash = *h_issue;
out.sig.signature = sig;
- GNUNET_array_append (pc->validate_tokens.output_tokens,
- pc->validate_tokens.output_tokens_len,
+ GNUNET_array_append (pc->output_tokens,
+ pc->output_tokens_len,
out);
}
@@ -4788,17 +4722,6 @@ pay_context_cleanup (void *cls)
pc);
GNUNET_free (pc->check_contract.pos_key);
#ifdef HAVE_DONAU_DONAU_SERVICE_H
- if (NULL != pc->donau_receipt.donau_sigs_json)
- {
- json_decref (pc->donau_receipt.donau_sigs_json);
- pc->donau_receipt.donau_sigs_json = NULL;
- }
- if (NULL != pc->donau_receipt.sigs)
- {
- GNUNET_free (pc->donau_receipt.sigs);
- pc->donau_receipt.sigs = NULL;
- pc->donau_receipt.num_sigs = 0;
- }
if (NULL != pc->parse_wallet_data.bkps)
{
GNUNET_free (pc->parse_wallet_data.bkps);
@@ -4860,9 +4783,11 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
case PP_PAY_TRANSACTION:
phase_execute_pay_transaction (pc);
break;
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
case PP_REQUEST_DONATION_RECEIPT:
phase_request_donation_receipt (pc);
break;
+#endif
case PP_FINAL_OUTPUT_TOKEN_PROCESSING:
phase_final_output_token_processing (pc);
break;