exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 049951b644dab6f6ac8286d27a95f69892e5f2ba
parent da8389e8f2950cbb3727332844ade33c5e51d329
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 30 Aug 2025 21:57:13 +0200

fix skr_size counting logic, keep helper connections open for the entire process in a global and do not re-connect merely because we couldn't yet bootstrap /keys (#10313)

Diffstat:
Msrc/exchange/taler-exchange-httpd.c | 9---------
Msrc/exchange/taler-exchange-httpd_keys.c | 253++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/exchange/taler-exchange-httpd_keys.h | 15---------------
3 files changed, 102 insertions(+), 175 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -2712,7 +2712,6 @@ do_shutdown (void *cls) TEH_aml_decision_cleanup (); TALER_KYCLOGIC_kyc_done (); TALER_MHD_daemons_destroy (); - TEH_wire_done (); TEH_extensions_done (); TEH_keys_finished (); if (NULL != TEH_plugin) @@ -2862,14 +2861,6 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - if (GNUNET_OK != - TEH_wire_init ()) - { - GNUNET_break (0); - global_ret = EXIT_NO_RESTART; - GNUNET_SCHEDULER_shutdown (); - return; - } TEH_load_terms (TEH_cfg); TEH_curl_ctx diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020-2024 Taler Systems SA + Copyright (C) 2020-2025 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -212,6 +212,13 @@ struct HelperState /** + * Information we track for the crypto helpers. Preserved + * when the @e key_generation changes, thus kept separate. + */ +static struct HelperState helpers; + + +/** * Entry in (sorted) array with possible pre-build responses for /keys. * We keep pre-build responses for the various (valid) cherry-picking * values around. @@ -328,12 +335,6 @@ struct TEH_KeyStateHandle unsigned int krd_array_length; /** - * Information we track for thecrypto helpers. Preserved - * when the @e key_generation changes, thus kept separate. - */ - struct HelperState *helpers; - - /** * Cached reply for a GET /management/keys request. Used so we do not * re-create the reply every time. */ @@ -539,13 +540,6 @@ static struct SuspendedKeysRequests *skr_tail; static unsigned int skr_size; /** - * Handle to a connection that should be force-resumed - * with a hard error due to @a skr_size hitting - * #SKR_LIMIT. - */ -static struct MHD_Connection *skr_connection; - -/** * Task to force timeouts on /keys requests. */ static struct GNUNET_SCHEDULER_Task *keys_tt; @@ -631,45 +625,6 @@ wire_update_event_cb (void *cls, } -enum GNUNET_GenericReturnValue -TEH_wire_init () -{ - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), - }; - - wire_eh = TEH_plugin->event_listen (TEH_plugin->cls, - GNUNET_TIME_UNIT_FOREVER_REL, - &es, - &wire_update_event_cb, - NULL); - if (NULL == wire_eh) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -void -TEH_wire_done () -{ - if (NULL != wire_state) - { - destroy_wire_state (wire_state); - wire_state = NULL; - } - if (NULL != wire_eh) - { - TEH_plugin->event_listen_cancel (TEH_plugin->cls, - wire_eh); - wire_eh = NULL; - } -} - - /** * Add information about a wire account to @a cls. * @@ -1046,6 +1001,7 @@ keys_timeout_cb (void *cls) GNUNET_CONTAINER_DLL_remove (skr_head, skr_tail, skr); + skr_size--; MHD_resume_connection (skr->connection); TALER_MHD_daemon_trigger (); GNUNET_free (skr); @@ -1084,6 +1040,7 @@ suspend_request (struct MHD_Connection *connection) GNUNET_CONTAINER_DLL_insert (skr_head, skr_tail, skr); + skr_size++; skr->timeout = GNUNET_TIME_relative_to_absolute (KEYS_TIMEOUT); if (NULL == keys_tt) { @@ -1091,15 +1048,13 @@ suspend_request (struct MHD_Connection *connection) &keys_timeout_cb, NULL); } - skr_size++; - if (skr_size > SKR_LIMIT) + while (skr_size > SKR_LIMIT) { - skr = skr_head; + skr = skr_tail; GNUNET_CONTAINER_DLL_remove (skr_head, skr_tail, skr); skr_size--; - skr_connection = skr->connection; MHD_resume_connection (skr->connection); TALER_MHD_daemon_trigger (); GNUNET_free (skr); @@ -1175,6 +1130,7 @@ TEH_resume_keys_requests (bool do_shutdown) TALER_MHD_daemon_trigger (); GNUNET_free (skr); } + GNUNET_assert (0 == skr_size); } @@ -1650,28 +1606,19 @@ setup_key_helpers (struct HelperState *hs) &helper_rsa_cb, hs); if (NULL == hs->rsadh) - { - destroy_key_helpers (hs); return GNUNET_SYSERR; - } hs->csdh = TALER_CRYPTO_helper_cs_connect (TEH_cfg, "taler-exchange", &helper_cs_cb, hs); if (NULL == hs->csdh) - { - destroy_key_helpers (hs); return GNUNET_SYSERR; - } hs->esh = TALER_CRYPTO_helper_esign_connect (TEH_cfg, "taler-exchange", &helper_esign_cb, hs); if (NULL == hs->esh) - { - destroy_key_helpers (hs); return GNUNET_SYSERR; - } return GNUNET_OK; } @@ -1748,11 +1695,9 @@ clear_signkey_cb (void *cls, * the helper data. * * @param[in] ksh key state to release - * @param free_helper true to also release the helper state */ static void -destroy_key_state (struct TEH_KeyStateHandle *ksh, - bool free_helper) +destroy_key_state (struct TEH_KeyStateHandle *ksh) { struct TEH_GlobalFee *gf; @@ -1777,11 +1722,6 @@ destroy_key_state (struct TEH_KeyStateHandle *ksh, ksh->auditors = NULL; json_decref (ksh->global_fees); ksh->global_fees = NULL; - if (free_helper) - { - destroy_key_helpers (ksh->helpers); - GNUNET_free (ksh->helpers); - } if (NULL != ksh->management_keys_reply) { json_decref (ksh->management_keys_reply); @@ -1819,11 +1759,12 @@ keys_update_event_cb (void *cls, enum GNUNET_GenericReturnValue TEH_keys_init () { - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), - }; - + if (GNUNET_OK != + setup_key_helpers (&helpers)) + { + destroy_key_helpers (&helpers); + return GNUNET_SYSERR; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (TEH_cfg, "exchange", @@ -1846,15 +1787,39 @@ TEH_keys_init () "ASSET_TYPE"); asset_type = GNUNET_strdup ("fiat"); } - keys_eh = TEH_plugin->event_listen (TEH_plugin->cls, - GNUNET_TIME_UNIT_FOREVER_REL, - &es, - &keys_update_event_cb, - NULL); - if (NULL == keys_eh) { - GNUNET_break (0); - return GNUNET_SYSERR; + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), + }; + + keys_eh = TEH_plugin->event_listen (TEH_plugin->cls, + GNUNET_TIME_UNIT_FOREVER_REL, + &es, + &keys_update_event_cb, + NULL); + if (NULL == keys_eh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), + }; + + wire_eh = TEH_plugin->event_listen (TEH_plugin->cls, + GNUNET_TIME_UNIT_FOREVER_REL, + &es, + &wire_update_event_cb, + NULL); + if (NULL == wire_eh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } } return GNUNET_OK; } @@ -1866,20 +1831,31 @@ TEH_keys_init () void TEH_keys_finished () { + if (NULL != wire_state) + { + destroy_wire_state (wire_state); + wire_state = NULL; + } + if (NULL != wire_eh) + { + TEH_plugin->event_listen_cancel (TEH_plugin->cls, + wire_eh); + wire_eh = NULL; + } if (NULL != keys_tt) { GNUNET_SCHEDULER_cancel (keys_tt); keys_tt = NULL; } if (NULL != key_state) - destroy_key_state (key_state, - true); + destroy_key_state (key_state); if (NULL != keys_eh) { TEH_plugin->event_listen_cancel (TEH_plugin->cls, keys_eh); keys_eh = NULL; } + destroy_key_helpers (&helpers); } @@ -3036,7 +3012,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) struct GNUNET_JSON_PackSpec key_spec; bool private_key_lost; - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &dk->h_denom_pub.hash); private_key_lost = (NULL == hd) || @@ -3242,14 +3218,12 @@ global_fee_info_cb ( /** * Create a key state. * - * @param[in] hs helper state to (re)use, NULL if not available * @param management_only if we should NOT run 'finish_keys_response()' * because we only need the state for the /management/keys API * @return NULL on error (i.e. failed to access database) */ static struct TEH_KeyStateHandle * -build_key_state (struct HelperState *hs, - bool management_only) +build_key_state (bool management_only) { struct TEH_KeyStateHandle *ksh; enum GNUNET_DB_QueryStatus qs; @@ -3259,22 +3233,6 @@ build_key_state (struct HelperState *hs, ksh->reload_time = GNUNET_TIME_timestamp_get (); /* We must use the key_generation from when we STARTED the process! */ ksh->key_generation = key_generation; - if (NULL == hs) - { - ksh->helpers = GNUNET_new (struct HelperState); - if (GNUNET_OK != - setup_key_helpers (ksh->helpers)) - { - GNUNET_free (ksh->helpers); - GNUNET_assert (NULL == ksh->management_keys_reply); - GNUNET_free (ksh); - return NULL; - } - } - else - { - ksh->helpers = hs; - } ksh->denomserial_map = GNUNET_CONTAINER_multihashmap32_create (1024); ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, true); @@ -3299,8 +3257,7 @@ build_key_state (struct HelperState *hs, { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - destroy_key_state (ksh, - true); + destroy_key_state (ksh); return NULL; } qs = TEH_plugin->iterate_denominations (TEH_plugin->cls, @@ -3310,8 +3267,7 @@ build_key_state (struct HelperState *hs, { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - destroy_key_state (ksh, - true); + destroy_key_state (ksh); return NULL; } /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ @@ -3321,8 +3277,7 @@ build_key_state (struct HelperState *hs, if (qs < 0) { GNUNET_break (0); - destroy_key_state (ksh, - true); + destroy_key_state (ksh); return NULL; } qs = TEH_plugin->iterate_auditor_denominations (TEH_plugin->cls, @@ -3331,8 +3286,7 @@ build_key_state (struct HelperState *hs, if (qs < 0) { GNUNET_break (0); - destroy_key_state (ksh, - true); + destroy_key_state (ksh); return NULL; } qs = TEH_plugin->iterate_active_auditors (TEH_plugin->cls, @@ -3341,8 +3295,7 @@ build_key_state (struct HelperState *hs, if (qs < 0) { GNUNET_break (0); - destroy_key_state (ksh, - true); + destroy_key_state (ksh); return NULL; } @@ -3357,8 +3310,7 @@ build_key_state (struct HelperState *hs, { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not finish /keys response (required data not configured yet)\n"); - destroy_key_state (ksh, - true); + destroy_key_state (ksh); return NULL; } @@ -3392,8 +3344,7 @@ keys_get_state (bool management_only) old_ksh = key_state; if (NULL == old_ksh) { - ksh = build_key_state (NULL, - management_only); + ksh = build_key_state (management_only); if (NULL == ksh) return NULL; key_state = ksh; @@ -3406,15 +3357,12 @@ keys_get_state (bool management_only) "Rebuilding /keys, generation upgrade from %llu to %llu\n", (unsigned long long) old_ksh->key_generation, (unsigned long long) key_generation); - ksh = build_key_state (old_ksh->helpers, - management_only); + ksh = build_key_state (management_only); key_state = ksh; - old_ksh->helpers = NULL; - destroy_key_state (old_ksh, - false); + destroy_key_state (old_ksh); return ksh; } - sync_key_helpers (old_ksh->helpers); + sync_key_helpers (&helpers); return old_ksh; } @@ -3555,7 +3503,7 @@ TEH_keys_denomination_batch_sign ( const struct TALER_DenominationHashP *h_denom_pub = csds[i].h_denom_pub; const struct TALER_BlindedPlanchet *bp = csds[i].bp; - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &h_denom_pub->hash); if (NULL == hd) return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; @@ -3599,7 +3547,7 @@ TEH_keys_denomination_batch_sign ( if (0 != csrs_pos) { ec = TALER_CRYPTO_helper_cs_batch_sign ( - ksh->helpers->csdh, + helpers.csdh, csrs_pos, csrs, for_melt, @@ -3615,7 +3563,7 @@ TEH_keys_denomination_batch_sign ( if (0 != rsrs_pos) { ec = TALER_CRYPTO_helper_rsa_batch_sign ( - ksh->helpers->rsadh, + helpers.rsadh, rsrs_pos, rsrs, (0 == csrs_pos) ? bss : rs); @@ -3672,7 +3620,7 @@ TEH_keys_denomination_cs_r_pub ( { return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; } - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &h_denom_pub->hash); if (NULL == hd) { @@ -3689,7 +3637,7 @@ TEH_keys_denomination_cs_r_pub ( .h_cs = &hd->h_details.h_cs, .nonce = nonce }; - return TALER_CRYPTO_helper_cs_r_derive (ksh->helpers->csdh, + return TALER_CRYPTO_helper_cs_r_derive (helpers.csdh, &cdr, for_melt, r_pub); @@ -3718,7 +3666,7 @@ TEH_keys_denomination_cs_batch_r_pub_simple ( const struct TALER_DenominationHashP *h_denom_pub = cdds[i].h_denom_pub; const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdds[i].nonce; - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &h_denom_pub->hash); if (NULL == hd) { @@ -3733,7 +3681,7 @@ TEH_keys_denomination_cs_batch_r_pub_simple ( cdrs[i].nonce = nonce; } - return TALER_CRYPTO_helper_cs_r_batch_derive (ksh->helpers->csdh, + return TALER_CRYPTO_helper_cs_r_batch_derive (helpers.csdh, cdds_length, cdrs, for_melt, @@ -3764,7 +3712,7 @@ TEH_keys_denomination_cs_batch_r_pub ( * TEH_DenominationKey and HelperDenomination, * because only TEH_DenominationKey has .recoup_possible */ - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &h_denom_pubs[i].hash); dk = TEH_keys_denomination_by_hash_from_state (ksh, &h_denom_pubs[i], @@ -3793,7 +3741,7 @@ TEH_keys_denomination_cs_batch_r_pub ( cdrs[i].nonce = &nonces[i]; } - return TALER_CRYPTO_helper_cs_r_batch_derive (ksh->helpers->csdh, + return TALER_CRYPTO_helper_cs_r_batch_derive (helpers.csdh, num, cdrs, for_melt, @@ -3813,7 +3761,7 @@ TEH_keys_denomination_revoke (const struct TALER_DenominationHashP *h_denom_pub) GNUNET_break (0); return; } - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &h_denom_pub->hash); if (NULL == hd) { @@ -3825,12 +3773,12 @@ TEH_keys_denomination_revoke (const struct TALER_DenominationHashP *h_denom_pub) case GNUNET_CRYPTO_BSA_INVALID: break; case GNUNET_CRYPTO_BSA_RSA: - TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->rsadh, + TALER_CRYPTO_helper_rsa_revoke (helpers.rsadh, &hd->h_details.h_rsa); TEH_keys_update_states (); return; case GNUNET_CRYPTO_BSA_CS: - TALER_CRYPTO_helper_cs_revoke (ksh->helpers->csdh, + TALER_CRYPTO_helper_cs_revoke (helpers.csdh, &hd->h_details.h_cs); TEH_keys_update_states (); return; @@ -3875,7 +3823,7 @@ TEH_keys_exchange_sign2_ ( enum TALER_ErrorCode ec; TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_EDDSA]++; - ec = TALER_CRYPTO_helper_esign_sign_ (ksh->helpers->esh, + ec = TALER_CRYPTO_helper_esign_sign_ (helpers.esh, purpose, pub, sig); @@ -3901,7 +3849,7 @@ TEH_keys_exchange_sign2_ ( memset (sig, 0, sizeof (*sig)); - return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + return TALER_EC_EXCHANGE_SIGNKEY_HELPER_OFFLINE_MISSING; } } return ec; @@ -3919,7 +3867,7 @@ TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub) GNUNET_break (0); return; } - TALER_CRYPTO_helper_esign_revoke (ksh->helpers->esh, + TALER_CRYPTO_helper_esign_revoke (helpers.esh, exchange_pub); TEH_keys_update_states (); } @@ -4004,8 +3952,11 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc, if ( (NULL == ksh) || (0 == ksh->krd_array_length) ) { - if ( ( (SKR_LIMIT == skr_size) && - (rc->connection == skr_connection) ) || + if ( ( (SKR_LIMIT <= skr_size) && + (GNUNET_TIME_relative_cmp ( + GNUNET_TIME_absolute_get_duration (rc->start_time), + >, + GNUNET_TIME_UNIT_SECONDS)) ) || TEH_suicide) { return TALER_MHD_reply_with_error ( @@ -4151,7 +4102,7 @@ TEH_keys_load_fees (struct TEH_KeyStateHandle *ksh, struct HelperDenomination *hd; enum GNUNET_GenericReturnValue ok; - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, + hd = GNUNET_CONTAINER_multihashmap_get (helpers.denom_keys, &h_denom_pub->hash); if (NULL == hd) { @@ -4202,7 +4153,7 @@ TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub, } pid.public_key = exchange_pub->eddsa_pub; - hsk = GNUNET_CONTAINER_multipeermap_get (ksh->helpers->esign_keys, + hsk = GNUNET_CONTAINER_multipeermap_get (helpers.esign_keys, &pid); if (NULL == hsk) { @@ -4377,7 +4328,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, "no key state"); } - sync_key_helpers (ksh->helpers); + sync_key_helpers (&helpers); if (NULL == ksh->management_keys_reply) { struct FutureBuilderContext fbc = { @@ -4404,10 +4355,10 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, } GNUNET_assert (NULL != fbc.denoms); GNUNET_assert (NULL != fbc.signkeys); - GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys, + GNUNET_CONTAINER_multihashmap_iterate (helpers.denom_keys, &add_future_denomkey_cb, &fbc); - GNUNET_CONTAINER_multipeermap_iterate (ksh->helpers->esign_keys, + GNUNET_CONTAINER_multipeermap_iterate (helpers.esign_keys, &add_future_signkey_cb, &fbc); reply = GNUNET_JSON_PACK ( diff --git a/src/exchange/taler-exchange-httpd_keys.h b/src/exchange/taler-exchange-httpd_keys.h @@ -155,12 +155,6 @@ struct TEH_KeyStateHandle; void TEH_check_invariants (void); -/** - * Clean up wire subsystem. - */ -void -TEH_wire_done (void); - /** * Look up wire fee structure by @a ts. @@ -177,15 +171,6 @@ TEH_wire_fees_by_time ( /** - * Initialize wire subsystem. - * - * @return #GNUNET_OK on success - */ -enum GNUNET_GenericReturnValue -TEH_wire_init (void); - - -/** * Something changed in the database. Rebuild the wire replies. This function * should be called if the exchange learns about a new signature from our * master key.