exchange

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

commit f05990eb80339bb5f5137af0c4b84eeeb942044a
parent 03cb0a556a7856247689bb698ca9fb0a7f2f021d
Author: Florian Dold <dold@taler.net>
Date:   Thu, 27 Aug 2026 23:29:47 +0200

exchange /keys: document withdrawal denomination filter

Diffstat:
Msrc/exchange/taler-exchange-httpd_get-keys.c | 30+++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_get-keys.c b/src/exchange/taler-exchange-httpd_get-keys.c @@ -218,7 +218,8 @@ struct TEH_KeyStateHandle struct KeysResponseData *withdraw_krd_array; /** - * Length of @e withdraw_krd_array. Either zero or one. + * Length of @e withdraw_krd_array. The filtered response is not + * cherry-picked, so this is either zero or one. */ unsigned int withdraw_krd_array_length; @@ -246,13 +247,14 @@ struct TEH_KeyStateHandle struct GNUNET_TIME_Relative rekey_frequency; /** - * When does our online signing key expire and we - * thus need to re-generate this response? + * When the key state must next be rebuilt due to signing key expiration or + * a change in response contents. */ struct GNUNET_TIME_Timestamp signature_expires; /** * When the membership of the withdrawal-focused response next changes. + * Used as its HTTP expiration time. */ struct GNUNET_TIME_Timestamp withdraw_expiration; @@ -1785,7 +1787,8 @@ get_withdraw_family_key (const struct TEH_DenominationKey *dk, /** - * Find the earliest future key start for every denomination family. + * Find the next withdrawable denomination of every family. This requires a + * separate pass because denomination map iteration is unordered. * * @param cls a `struct FindWithdrawFamiliesContext *` * @param h_denom_pub denomination hash, unused @@ -1833,7 +1836,7 @@ find_withdraw_family_cb (void *cls, /** - * Mark current denominations and each family's earliest future denomination. + * Mark the current and next withdrawable denomination of every family. * * @param cls a `struct SelectWithdrawDenominationsContext *` * @param h_denom_pub denomination hash, unused @@ -1887,6 +1890,7 @@ select_withdraw_denomination_cb (void *cls, return GNUNET_OK; dk->withdraw_relevant = true; ctx->selected++; + /* Rebuild the response and expire client caches at the first rotation. */ ctx->ksh->withdraw_expiration = GNUNET_TIME_timestamp_min ( ctx->ksh->withdraw_expiration, transition); @@ -1918,7 +1922,9 @@ free_withdraw_family (void *cls, /** - * Select denominations for `denom_filter=withdraw`. + * Select denominations for `denom_filter=withdraw`. The filtered query + * returns each family's current and next withdrawable denomination, covering + * the next key rotation while omitting expired and later keys. * * @param[in,out] ksh key state containing the denominations * @param now exchange time to use for selection @@ -1958,7 +1964,8 @@ select_withdraw_denominations (struct TEH_KeyStateHandle *ksh, /** * Build auditor information containing signatures only for denominations in * the withdrawal-focused response. Auditor identity metadata is retained - * even when none of its denomination signatures is selected. + * even without selected signatures, so filtering only changes denomination + * coverage. * * @param ksh key state with full auditor information * @return newly allocated JSON array @@ -2010,6 +2017,7 @@ build_withdraw_auditors (const struct TEH_KeyStateHandle *ksh) json_array_append (filtered_keys, denom_sig)); } + /* Keep the auditor data for the unfiltered response unchanged. */ filtered_auditor = json_deep_copy (auditor); GNUNET_assert (NULL != filtered_auditor); GNUNET_assert (0 == @@ -2043,6 +2051,7 @@ setup_response_headers (struct TEH_KeyStateHandle *ksh, MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "application/json")); + /* A fixed max-age would override the filtered response's exact expiration. */ GNUNET_break (MHD_YES == MHD_add_response_header (response, MHD_HTTP_HEADER_CACHE_CONTROL, @@ -2567,6 +2576,8 @@ compute_msig_hash (struct SignatureContext *sig_ctx, /** * Add one denomination to a grouped denomination response. + * Denominations with equal cipher, value, fees and age mask share group + * metadata. The signature context records their resulting array positions. * * @param[in,out] denominations_by_group map used to find existing groups * @param[in,out] grouped_denominations JSON array of denomination groups @@ -2799,6 +2810,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO /* NO, because keys are only on the stack */ ); + /* Filtering changes group and denomination offsets. */ withdraw_denominations_by_group = GNUNET_CONTAINER_multihashmap_create (128, false); @@ -2850,6 +2862,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) &has_age_restricted_denomination); if (dk->withdraw_relevant) { + /* Newest-first iteration leaves the oldest selected issue date. */ withdraw_list_issue_date = dk->meta.start; append_denomination (withdraw_denominations_by_group, withdraw_denominations, @@ -2895,12 +2908,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh) GNUNET_TIME_timestamp2s (last_cherry_pick_date)); goto CLEANUP; } + /* An empty selection is reported as missing keys, not a valid response. */ if (0 != withdraw_denomination_count) { struct GNUNET_HashCode withdraw_hc; GNUNET_assert (! GNUNET_TIME_absolute_is_zero ( withdraw_list_issue_date.abs_time)); + /* Filtering changes group offsets and therefore the signature hash. */ compute_msig_hash (&withdraw_sig_ctx, &withdraw_hc); if (GNUNET_OK != @@ -3429,6 +3444,7 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc, have_cherrypick = MHD_lookup_connection_value (rc->connection, MHD_GET_ARGUMENT_KIND, "last_issue_date"); + /* The withdrawal filter has one response for the current key state. */ if (withdraw_filter && (NULL != have_cherrypick)) return TALER_MHD_reply_with_error (