merchant

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

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:
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 611+++++++++++++++++++++++++++++++++++--------------------------------------------
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;