commit f21080a5091d94773556c3e2e7eca506dc40f69c parent abf287e47b9b9263a0a25c863aee249dcd0a01fe Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com> Date: Tue, 4 Mar 2025 17:09:19 +0100 Merge branch 'master' into dev/bohdan-potuzhnyi/donau-integration Diffstat:
20 files changed, 156 insertions(+), 66 deletions(-)
diff --git a/configure.ac b/configure.ac @@ -321,12 +321,12 @@ AS_CASE([$with_exchange], CPPFLAGS="-I$with_exchange/include $CPPFLAGS $POSTGRESQL_CPPFLAGS"]) AC_CHECK_HEADERS([taler/taler_mhd_lib.h], - [AC_CHECK_LIB([talermhd], [TALER_MHD_parse_request_arg_snumber], libtalermhd=1)]) + [AC_CHECK_LIB([talermhd], [TALER_MHD_arg_to_yna], libtalermhd=1)]) AM_CONDITIONAL(HAVE_TALERMHD, test x$libtalermhd = x1) AS_IF([test $libtalermhd != 1], [AC_MSG_ERROR([[ *** -*** You need libtalermhd >= 0.10.1 (API v2) to build this program. +*** You need libtalermhd >= 0.14.7 (API v3) to build this program. *** This library is part of the GNU Taler exchange, available at *** https://taler.net *** ]])]) diff --git a/src/backend/taler-merchant-depositcheck.c b/src/backend/taler-merchant-depositcheck.c @@ -19,6 +19,7 @@ * @author Christian Grothoff */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> @@ -423,6 +424,7 @@ deposit_get_cb ( case MHD_HTTP_OK: { enum GNUNET_DB_QueryStatus qs; + bool cleared = false; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Exchange returned wire transfer over %s for deposited coin %s\n", @@ -431,7 +433,24 @@ deposit_get_cb ( qs = db_plugin->insert_deposit_to_transfer ( db_plugin->cls, w->deposit_serial, - &dr->details.ok); + &dr->details.ok, + &cleared); + if (qs < 0) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (! cleared) + { + qs = db_plugin->update_deposit_confirmation_status ( + db_plugin->cls, + w->deposit_serial, + true, /* this failed, wire_pending remains true */ + GNUNET_TIME_absolute_to_timestamp (future_retry), + w->retry_backoff, + "wire transfer unknown"); + } if (qs < 0) { GNUNET_break (0); diff --git a/src/backend/taler-merchant-exchangekeyupdate.c b/src/backend/taler-merchant-exchangekeyupdate.c @@ -19,6 +19,7 @@ * @author Christian Grothoff */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> @@ -106,6 +107,15 @@ struct Exchange * hard limit? */ bool limited; + + /** + * Are we force-retrying a /keys download because some keys + * were missing (and we thus should not cherry-pick, as + * a major reason for a force-reload would be an + * exchange that has lost keys and backfilled them, which + * breaks keys downloads with cherry-picking). + */ + bool force_retry; }; @@ -581,9 +591,12 @@ download_keys (void *cls) = GNUNET_TIME_STD_BACKOFF (e->retry_delay); e->conn = TALER_EXCHANGE_get_keys (ctx, e->exchange_url, - e->keys, + e->force_retry + ? NULL + : e->keys, &cert_cb, e); + e->force_retry = false; if (NULL != e->conn) { active_inquiries++; @@ -676,6 +689,7 @@ force_exchange_keys (void *cls, true)); if (NULL != e->retry_task) GNUNET_SCHEDULER_cancel (e->retry_task); + e->force_retry = true; e->retry_task = GNUNET_SCHEDULER_add_at (e->first_retry, &download_keys, diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -1613,10 +1613,10 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, "token", &god->claim_token, god->claim_token_provided); - if (! (TALER_arg_to_yna (connection, - "allow_refunded_for_repurchase", - TALER_EXCHANGE_YNA_NO, - &god->allow_refunded_for_repurchase)) ) + if (! (TALER_MHD_arg_to_yna (connection, + "allow_refunded_for_repurchase", + TALER_EXCHANGE_YNA_NO, + &god->allow_refunded_for_repurchase)) ) return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -116,6 +116,8 @@ enum PayPhase */ PP_PAY_TRANSACTION, + // FIXME: new (optional) phase for DONAU interaction here. + /** * Notify other processes about successful payment. */ @@ -1323,6 +1325,9 @@ process_pay_with_keys ( TALER_EXCHANGE_keys_to_json (keys))))); return; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Have missing denomination for exchange, updating %s\n", + eg->exchange_url); force_keys (eg); return; } @@ -1691,6 +1696,7 @@ phase_success_response (struct PayContext *pc) token_sigs = (0 >= pc->validate_tokens.output_tokens_len) ? NULL : build_token_sigs (pc); + // FIXME: add signatures obtained from donau to response pay_end (pc, TALER_MHD_REPLY_JSON_PACK ( pc->connection, @@ -2398,6 +2404,9 @@ phase_execute_pay_transaction (struct PayContext *pc) } } + // FIXME: insert donau blinded inputs (into DB here!), + // idempotency: if already exists, no problem! + TMH_notify_order_change (hc->instance, TMH_OSF_CLAIMED | TMH_OSF_PAID, pc->check_contract.contract_terms->timestamp, @@ -2451,6 +2460,9 @@ phase_execute_pay_transaction (struct PayContext *pc) return; } } + // FIXME: if we have donation receipts, do NEW phase + // DONAU interaction here, otherwise skip DONAU phase + // and move to payment notification pc->phase = PP_PAYMENT_NOTIFICATION; } @@ -2794,12 +2806,15 @@ phase_validate_tokens (struct PayContext *pc) 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) @@ -3358,6 +3373,12 @@ phase_parse_wallet_data (struct PayContext *pc) GNUNET_JSON_spec_array_const ("tokens_evs", &tokens_evs), NULL), + // FIXME: extend spec for wallet to submit + // - URL of selected donau + // - year + // - BUDIs with blinded donation receipts (donau-key-hash, blinded value) + // + check in later phase (once we have the contract) + // that the selected donau was offered and the BUDIs are below the allowed amount GNUNET_JSON_spec_end () }; @@ -3831,27 +3852,28 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh, case PP_CHECK_CONTRACT: phase_check_contract (pc); break; - case PP_CONTRACT_PAID: - phase_contract_paid (pc); - break; case PP_VALIDATE_TOKENS: phase_validate_tokens (pc); break; + case PP_CONTRACT_PAID: + phase_contract_paid (pc); + break; case PP_PAY_TRANSACTION: phase_execute_pay_transaction (pc); break; - case PP_BATCH_DEPOSITS: - phase_batch_deposits (pc); - break; - case PP_GENERATE_DONATION_RECEIPT: - phase_generate_donation_receipt (pc); - break; case PP_PAYMENT_NOTIFICATION: phase_payment_notification (pc); break; +// FIXME: donau phase +// case PP_GENERATE_DONATION_RECEIPT: +// phase_generate_donation_receipt (pc); +// break; case PP_SUCCESS_RESPONSE: phase_success_response (pc); break; + case PP_BATCH_DEPOSITS: + phase_batch_deposits (pc); + break; case PP_RETURN_RESPONSE: phase_return_response (pc); break; diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c @@ -526,10 +526,6 @@ map_to_status (const struct ExchangeKycRequest *ekr) { return "no-exchange-keys"; } - if (ekr->kyc_ok) - { - return "ready"; - } if (! ekr->auth_ok) { if (ekr->kyc_auth_conflict) @@ -538,6 +534,10 @@ map_to_status (const struct ExchangeKycRequest *ekr) } if (ekr->in_aml_review) return "awaiting-aml-review"; + if (ekr->kyc_ok) + { + return "ready"; + } switch (ekr->last_http_status) { case 0: diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -1518,10 +1518,10 @@ TMH_private_get_orders_ID ( gorc->session_id = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "session_id"); - if (! (TALER_arg_to_yna (connection, - "allow_refunded_for_repurchase", - TALER_EXCHANGE_YNA_NO, - &gorc->allow_refunded_for_repurchase)) ) + if (! (TALER_MHD_arg_to_yna (connection, + "allow_refunded_for_repurchase", + TALER_EXCHANGE_YNA_NO, + &gorc->allow_refunded_for_repurchase)) ) return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, diff --git a/src/backend/taler-merchant-httpd_private-get-orders.c b/src/backend/taler-merchant-httpd_private-get-orders.c @@ -674,10 +674,10 @@ TMH_private_get_orders (const struct TMH_RequestHandler *rh, po->instance_id = hc->instance->settings.id; po->mi = hc->instance; - if (! (TALER_arg_to_yna (connection, - "paid", - TALER_EXCHANGE_YNA_ALL, - &po->of.paid)) ) + if (! (TALER_MHD_arg_to_yna (connection, + "paid", + TALER_EXCHANGE_YNA_ALL, + &po->of.paid)) ) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (connection, @@ -685,10 +685,10 @@ TMH_private_get_orders (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_PARAMETER_MALFORMED, "paid"); } - if (! (TALER_arg_to_yna (connection, - "refunded", - TALER_EXCHANGE_YNA_ALL, - &po->of.refunded)) ) + if (! (TALER_MHD_arg_to_yna (connection, + "refunded", + TALER_EXCHANGE_YNA_ALL, + &po->of.refunded)) ) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (connection, @@ -696,10 +696,10 @@ TMH_private_get_orders (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_PARAMETER_MALFORMED, "refunded"); } - if (! (TALER_arg_to_yna (connection, - "wired", - TALER_EXCHANGE_YNA_ALL, - &po->of.wired)) ) + if (! (TALER_MHD_arg_to_yna (connection, + "wired", + TALER_EXCHANGE_YNA_ALL, + &po->of.wired)) ) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (connection, diff --git a/src/backend/taler-merchant-httpd_private-get-transfers.c b/src/backend/taler-merchant-httpd_private-get-transfers.c @@ -159,10 +159,10 @@ TMH_private_get_transfers (const struct TMH_RequestHandler *rh, TALER_MHD_parse_request_number (connection, "offset", &offset); - if (! (TALER_arg_to_yna (connection, - "verified", - TALER_EXCHANGE_YNA_ALL, - &verified)) ) + if (! (TALER_MHD_arg_to_yna (connection, + "verified", + TALER_EXCHANGE_YNA_ALL, + &verified)) ) return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, diff --git a/src/backend/taler-merchant-kyccheck.c b/src/backend/taler-merchant-kyccheck.c @@ -19,6 +19,7 @@ * @author Christian Grothoff */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> diff --git a/src/backend/taler-merchant-reconciliation.c b/src/backend/taler-merchant-reconciliation.c @@ -19,6 +19,7 @@ * @author Christian Grothoff */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> diff --git a/src/backend/taler-merchant-webhook.c b/src/backend/taler-merchant-webhook.c @@ -19,6 +19,7 @@ * @author Priscilla HUANG */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> diff --git a/src/backend/taler-merchant-wirewatch.c b/src/backend/taler-merchant-wirewatch.c @@ -19,6 +19,7 @@ * @author Christian Grothoff */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> diff --git a/src/backenddb/pg_insert_deposit_to_transfer.c b/src/backenddb/pg_insert_deposit_to_transfer.c @@ -30,7 +30,8 @@ enum GNUNET_DB_QueryStatus TMH_PG_insert_deposit_to_transfer ( void *cls, uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd) + const struct TALER_EXCHANGE_DepositData *dd, + bool *wpc) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -43,12 +44,11 @@ TMH_PG_insert_deposit_to_transfer ( GNUNET_PQ_query_param_auto_from_type (&dd->wtid), GNUNET_PQ_query_param_end }; - bool wpc; bool conflict; bool no_exchange_pub; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_bool ("out_wire_pending_cleared", - &wpc), + wpc), GNUNET_PQ_result_spec_bool ("out_conflict", &conflict), GNUNET_PQ_result_spec_bool ("out_no_exchange_pub", @@ -57,6 +57,7 @@ TMH_PG_insert_deposit_to_transfer ( }; enum GNUNET_DB_QueryStatus qs; + *wpc = false; PREPARE (pg, "insert_deposit_to_transfer", "SELECT" @@ -74,7 +75,7 @@ TMH_PG_insert_deposit_to_transfer ( if (no_exchange_pub) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Exchange public key unknown (bug!?)\n"); - if (wpc) + if (*wpc) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Wire pending flag cleared (good!)\n"); if (conflict) diff --git a/src/backenddb/pg_insert_deposit_to_transfer.h b/src/backenddb/pg_insert_deposit_to_transfer.h @@ -32,13 +32,15 @@ * @param cls closure * @param deposit_serial serial number of the deposit * @param dd deposit transfer data from the exchange to store + * @param[out] wpc set to true if the wire_pending flag was cleared * @return transaction status */ enum GNUNET_DB_QueryStatus TMH_PG_insert_deposit_to_transfer ( void *cls, uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd); + const struct TALER_EXCHANGE_DepositData *dd, + bool *wpc); #endif diff --git a/src/backenddb/pg_insert_deposit_to_transfer.sql b/src/backenddb/pg_insert_deposit_to_transfer.sql @@ -80,12 +80,19 @@ INSERT INTO merchant_deposit_to_transfer IF NOT FOUND THEN - -- Same or conflicting wire transfer existed in the table already - -- Note: we don't distinguish here between - -- conflict and duplicate. Do we need to? + PERFORM FROM merchant_deposit_to_transfer + WHERE deposit_serial=in_deposit_serial + AND wtid=in_wtid + AND signkey_serial=my_signkey_serial + AND exchange_sig=in_exchange_sig; +END IF; + +IF NOT FOUND +THEN + -- Conflicting (!) wire transfer existed in the table already out_conflict=TRUE; out_wire_pending_cleared=FALSE; - return; + RETURN; END IF; out_conflict=FALSE; @@ -123,9 +130,9 @@ UPDATE merchant_deposit_confirmations (SELECT 1 FROM merchant_deposits md LEFT JOIN merchant_deposit_to_transfer mdtt - USING (wtid) + USING (deposit_serial) WHERE md.deposit_confirmation_serial=my_decose - AND mdtt.credit_serial IS NULL); + AND mdtt.signkey_serial IS NULL); -- credit_serial will be NULL due to LEFT JOIN -- if we do not have an entry in mdtt for the deposit -- and thus some entry in md was not yet wired. diff --git a/src/backenddb/pg_update_deposit_confirmation_status.c b/src/backenddb/pg_update_deposit_confirmation_status.c @@ -54,7 +54,7 @@ TMH_PG_update_deposit_confirmation_status ( " wire_transfer_deadline=$2" ",exchange_failure=$3" ",retry_backoff=$4" - ",wire_pending=$5" + ",wire_pending=$5 AND wire_pending" " WHERE deposit_confirmation_serial=" " (SELECT deposit_confirmation_serial" " FROM merchant_deposits" diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c @@ -21,6 +21,7 @@ * @author Pricilla Huang */ #include "platform.h" +#include "microhttpd.h" #include <taler/taler_util.h> #include <taler/taler_json_lib.h> #include <taler/taler_signatures.h> @@ -4208,6 +4209,7 @@ test_insert_transfer (const struct InstanceData *instance, * @param order the order the deposit and transfer were made for. * @param transfer the transfer. * @param expected_result the result the database should return. + * @param expected_cleared clearance status the database should return. * @return 0 on success, 1 otherwise. */ static int @@ -4216,7 +4218,8 @@ test_insert_deposit_to_transfer (const struct InstanceData *instance, const struct OrderData *order, const struct DepositData *deposit, const struct TransferData *transfer, - enum GNUNET_DB_QueryStatus expected_result) + enum GNUNET_DB_QueryStatus expected_result, + bool expect_cleared) { const struct TALER_EXCHANGE_DepositData deposit_data = { .exchange_pub = signkey->exchange_pub, @@ -4228,12 +4231,17 @@ test_insert_deposit_to_transfer (const struct InstanceData *instance, uint64_t deposit_serial = get_deposit_serial (instance, order, deposit); + bool cleared; TEST_COND_RET_ON_FAIL (expected_result == plugin->insert_deposit_to_transfer (plugin->cls, deposit_serial, - &deposit_data), + &deposit_data, + &cleared), "insert deposit to transfer failed\n"); + TEST_COND_RET_ON_FAIL (expect_cleared == + cleared, + "cleared status wrong"); return 0; } @@ -4255,13 +4263,13 @@ test_insert_transfer_details ( enum GNUNET_DB_QueryStatus expected_result) { TEST_COND_RET_ON_FAIL (expected_result == - plugin->insert_transfer_details (plugin->cls, - instance->instance.id, - transfer->exchange_url - , - account->payto_uri, - &transfer->wtid, - &transfer->data), + plugin->insert_transfer_details ( + plugin->cls, + instance->instance.id, + transfer->exchange_url, + account->payto_uri, + &transfer->wtid, + &transfer->data), "Insert transfer details failed\n"); return 0; } @@ -4452,13 +4460,15 @@ run_test_transfers (struct TestTransfers_Closure *cls) &cls->order, &cls->deposit, &cls->transfers[0], - GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + false)); TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, &cls->signkey, &cls->order, &cls->deposit, &cls->transfers[0], - GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + false)); TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance, &cls->account, &cls->transfers[0], @@ -4482,6 +4492,14 @@ run_test_transfers (struct TestTransfers_Closure *cls) cls->transfers[0].confirmed = true; TEST_RET_ON_FAIL (test_lookup_transfer (&cls->instance, &cls->transfers[0])); + TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, + &cls->signkey, + &cls->order, + &cls->deposit, + &cls->transfers[0], + GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, + true)); + TEST_RET_ON_FAIL (test_lookup_transfer_summary (cls->deposit.exchange_url, &cls->transfers[0].wtid, cls->order.id, diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -2632,12 +2632,14 @@ struct TALER_MERCHANTDB_Plugin * @param cls closure * @param deposit_serial serial number of the deposit * @param dd deposit transfer data from the exchange to store + * @param[out] wpc set to true if the wire_pending flag was cleared * @return transaction status */ enum GNUNET_DB_QueryStatus (*insert_deposit_to_transfer)(void *cls, uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd); + const struct TALER_EXCHANGE_DepositData *dd, + bool *wpc); /** diff --git a/src/lib/merchant_api_common.c b/src/lib/merchant_api_common.c @@ -21,6 +21,7 @@ * @author Priscilla Huang */ #include "platform.h" +#include "microhttpd.h" #include <curl/curl.h> #include "taler_merchant_service.h" #include "merchant_api_common.h"