From 91566ddee24dc0685cf33519583d55c813b26974 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 13 Jan 2019 16:22:16 +0100 Subject: [PATCH] more code towards fixing #5447 --- .gitignore | 2 + src/include/taler_exchange_service.h | 12 +- src/lib/exchange_api_deposit.c | 103 +++++++------ src/lib/exchange_api_handle.c | 216 ++++++++++++++++++++++----- src/lib/exchange_api_handle.h | 55 ++++++- 5 files changed, 301 insertions(+), 87 deletions(-) diff --git a/.gitignore b/.gitignore index 8e3596100..8a99e4be8 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ contrib/auditor-report.pdf src/bank-lib/taler-bank-transfer src/bank-lib/test_bank_api_twisted src/lib/test_exchange_api_new +src/lib/test_auditor_api +src/lib/test_exchange_api_overlapping_keys_bug diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 0118d72d7..fa93dca80 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -48,7 +48,7 @@ enum TALER_EXCHANGE_Option * /keys data (or at least only download the deltas). */ TALER_EXCHANGE_OPTION_DATA - + }; @@ -66,7 +66,7 @@ struct TALER_EXCHANGE_SigningPublicKey * Signature over this signing key by the exchange's master signature. */ struct TALER_MasterSignatureP master_sig; - + /** * Validity start time */ @@ -103,7 +103,7 @@ struct TALER_EXCHANGE_DenomPublicKey * Exchange's master signature over this denomination record. */ struct TALER_MasterSignatureP master_sig; - + /** * Timestamp indicating when the denomination key becomes valid */ @@ -167,7 +167,7 @@ struct TALER_EXCHANGE_AuditorDenominationInfo * denomination. */ struct TALER_AuditorSignatureP auditor_sig; - + /** * Offsets into the key's main `denom_keys` array identifying the * denomination being audited by this auditor. @@ -201,7 +201,7 @@ struct TALER_EXCHANGE_AuditorInformation /** * Array of length @a num_denom_keys with the denomination - * keys audited by this auditor. + * keys audited by this auditor. */ struct TALER_EXCHANGE_AuditorDenominationInfo *denom_keys; @@ -393,7 +393,7 @@ TALER_EXCHANGE_connect (struct GNUNET_CURL_Context *ctx, /** * Serialize the latest key data from @a exchange to be persisted - * on disk (to be used with #TALER_EXCHANGE_OPTION_DATA to more + * on disk (to be used with #TALER_EXCHANGE_OPTION_DATA to more * efficiently recover the state). * * @param exchange which exchange's key and wire data should be serialized diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c index 55b3ca6b3..9ff9d97d3 100644 --- a/src/lib/exchange_api_deposit.c +++ b/src/lib/exchange_api_deposit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2018 GNUnet e.V. + Copyright (C) 2014, 2015, 2018, 2019 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -75,6 +75,16 @@ struct TALER_EXCHANGE_DepositHandle */ struct TALER_DepositConfirmationPS depconf; + /** + * Exchange signature, set for #auditor_cb. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Exchange signing public key, set for #auditor_cb. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + /** * Value of the /deposit transaction, including fee. */ @@ -89,21 +99,52 @@ struct TALER_EXCHANGE_DepositHandle /** - * Signature of functions called with the result from our call to the - * auditor's /deposit-confirmation handler. + * Function called for each auditor to give us a chance to possibly + * launch a deposit confirmation interaction. * * @param cls closure - * @param http_status HTTP status code, 200 on success - * @param ec taler protocol error status code, 0 on success - * @param json raw json response + * @param ah handle to the auditor + * @param auditor_pub public key of the auditor + * @return NULL if no deposit confirmation interaction was launched */ -static void -acc_confirmation_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *json) +static struct TEAH_AuditorInteractionEntry * +auditor_cb (void *cls, + struct TALER_AUDITOR_Handle *ah, + const struct TALER_AuditorPublicKeyP *auditor_pub) { - /* FIXME: clean up state, some logging on errors! */ + struct TALER_EXCHANGE_DepositHandle *dh = cls; + const struct TALER_EXCHANGE_Keys *key_state; + const struct TALER_EXCHANGE_SigningPublicKey *spk; + struct TALER_Amount amount_without_fee; + struct TEAH_AuditorInteractionEntry *aie; + + if (1 /* #5447: replace with "for all auditors, if auditor selected for DC notification... */) + return NULL; + key_state = TALER_EXCHANGE_get_keys (dh->exchange); + spk = TALER_EXCHANGE_get_signing_key_details (key_state, + &dh->exchange_pub); + GNUNET_assert (NULL != spk); + TALER_amount_ntoh (&amount_without_fee, + &dh->depconf.amount_without_fee); + aie = GNUNET_new (struct TEAH_AuditorInteractionEntry); + aie->dch = TALER_AUDITOR_deposit_confirmation (ah, + &dh->depconf.h_wire, + &dh->depconf.h_contract_terms, + GNUNET_TIME_absolute_ntoh (dh->depconf.timestamp), + GNUNET_TIME_absolute_ntoh (dh->depconf.refund_deadline), + &amount_without_fee, + &dh->depconf.coin_pub, + &dh->depconf.merchant, + &dh->exchange_pub, + &dh->exchange_sig, + &key_state->master_pub, + spk->valid_from, + spk->valid_until, + spk->valid_legal, + &spk->master_sig, + &TEAH_acc_confirmation_cb, + aie); + return aie; } @@ -118,7 +159,7 @@ acc_confirmation_cb (void *cls, * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not */ static int -verify_deposit_signature_ok (const struct TALER_EXCHANGE_DepositHandle *dh, +verify_deposit_signature_ok (struct TALER_EXCHANGE_DepositHandle *dh, const json_t *json, struct TALER_ExchangeSignatureP *exchange_sig, struct TALER_ExchangePublicKeyP *exchange_pub) @@ -155,37 +196,11 @@ verify_deposit_signature_ok (const struct TALER_EXCHANGE_DepositHandle *dh, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (0 /* #5447: replace with "for all auditors, if auditor selected for DC notification... */) - { - struct TALER_AUDITOR_DepositConfirmationHandle *dch; - const struct TALER_EXCHANGE_SigningPublicKey *spk; - struct TALER_Amount amount_without_fee; - - spk = TALER_EXCHANGE_get_signing_key_details (key_state, - exchange_pub); - GNUNET_assert (NULL != spk); - TALER_amount_ntoh (&amount_without_fee, - &dh->depconf.amount_without_fee); - dch = TALER_AUDITOR_deposit_confirmation (NULL /* FIXME: auditor */, - &dh->depconf.h_wire, - &dh->depconf.h_contract_terms, - GNUNET_TIME_absolute_ntoh (dh->depconf.timestamp), - GNUNET_TIME_absolute_ntoh (dh->depconf.refund_deadline), - &amount_without_fee, - &dh->depconf.coin_pub, - &dh->depconf.merchant, - exchange_pub, - exchange_sig, - &key_state->master_pub, - spk->valid_from, - spk->valid_until, - spk->valid_legal, - &spk->master_sig, - &acc_confirmation_cb, - NULL /* FIXME: context! */); - } - - + dh->exchange_sig = *exchange_sig; + dh->exchange_pub = *exchange_pub; + TEAH_get_auditors_for_dc (dh->exchange, + &auditor_cb, + dh); return GNUNET_OK; } diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index 9743b1f09..8eedfd60a 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -91,47 +91,25 @@ enum ExchangeHandleState struct KeysRequest; -/** - * Entry in list of ongoing interactions with an auditor. - */ -struct AuditorInteractionEntry -{ - /** - * DLL entry. - */ - struct AuditorInteractionEntry *next; - - /** - * DLL entry. - */ - struct AuditorInteractionEntry *prev; - - /** - * Interaction state. - */ - struct TALER_AUDITOR_DepositConfirmationHandle *dch; -}; - - /** * Entry in DLL of auditors used by an exchange. */ -struct AuditorListEntry +struct TEAH_AuditorListEntry { /** * Next pointer of DLL. */ - struct AuditorListEntry *next; + struct TEAH_AuditorListEntry *next; /** * Prev pointer of DLL. */ - struct AuditorListEntry *prev; + struct TEAH_AuditorListEntry *prev; /** * Base URL of the auditor. */ - const char *auditor_url; + char *auditor_url; /** * Handle to the auditor. @@ -141,12 +119,12 @@ struct AuditorListEntry /** * Head of DLL of interactions with this auditor. */ - struct AuditorInteractionEntry *ai_head; + struct TEAH_AuditorInteractionEntry *ai_head; /** * Tail of DLL of interactions with this auditor. */ - struct AuditorInteractionEntry *ai_tail; + struct TEAH_AuditorInteractionEntry *ai_tail; /** * Public key of the auditor. @@ -208,12 +186,12 @@ struct TALER_EXCHANGE_Handle /** * Head of DLL of auditors of this exchange. */ - struct AuditorListEntry *auditors_head; + struct TEAH_AuditorListEntry *auditors_head; /** * Tail of DLL of auditors of this exchange. */ - struct AuditorListEntry *auditors_tail; + struct TEAH_AuditorListEntry *auditors_tail; /** * Key data of the exchange, only valid if @@ -270,6 +248,39 @@ struct KeysRequest }; +/** + * Signature of functions called with the result from our call to the + * auditor's /deposit-confirmation handler. + * + * @param cls closure of type `struct TEAH_AuditorInteractionEntry *` + * @param http_status HTTP status code, 200 on success + * @param ec taler protocol error status code, 0 on success + * @param json raw json response + */ +void +TEAH_acc_confirmation_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *json) +{ + struct TEAH_AuditorInteractionEntry *aie = cls; + struct TEAH_AuditorListEntry *ale = aie->ale; + + if (MHD_HTTP_OK != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n"), + ale->auditor_url, + http_status, + (int) ec); + } + GNUNET_CONTAINER_DLL_remove (ale->ai_head, + ale->ai_tail, + aie); + GNUNET_free (aie); +} + + /** * Iterate over all available auditors for @a h, calling * @param ah and giving it a chance to start a deposit @@ -284,14 +295,38 @@ TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Handle *h, TEAH_AuditorCallback ac, void *ac_cls) { - // FIXME! + if (NULL == h->auditors_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("No auditor available for exchange `%s'. Not submitting deposit confirmations.\n"), + h->url); + return; + } + for (struct TEAH_AuditorListEntry *ale = h->auditors_head; + NULL != ale; + ale = ale->next) + { + struct TEAH_AuditorInteractionEntry *aie; + + if (GNUNET_NO == ale->is_up) + continue; + aie = ac (ac_cls, + ale->ah, + &ale->auditor_pub); + if (NULL != aie) + { + aie->ale = ale; + GNUNET_CONTAINER_DLL_insert (ale->ai_head, + ale->ai_tail, + aie); + } + } } /** - * Release memory occupied by a keys request. - * Note that this does not cancel the request - * itself. + * Release memory occupied by a keys request. Note that this does not + * cancel the request itself. * * @param kr request to free */ @@ -601,6 +636,90 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor, } +/** + * Function called with information about the auditor. Marks an + * auditor as 'up'. + * + * @param cls closure, a `struct TEAH_AuditorListEntry *` + * @param vi basic information about the auditor + * @param compat protocol compatibility information + */ +static void +auditor_version_cb (void *cls, + const struct TALER_AUDITOR_VersionInformation *vi, + enum TALER_AUDITOR_VersionCompatibility compat) +{ + struct TEAH_AuditorListEntry *ale = cls; + + if (0 != (TALER_AUDITOR_VC_INCOMPATIBLE & compat)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Auditor `%s' runs incompatible protocol version!\n"), + ale->auditor_url); + if (0 != (TALER_AUDITOR_VC_OLDER & compat)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Auditor `%s' runs outdated protocol version!\n"), + ale->auditor_url); + } + if (0 != (TALER_AUDITOR_VC_NEWER & compat)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Auditor `%s' runs more recent incompatible version. We should upgrade!\n"), + ale->auditor_url); + } + return; + } + ale->is_up = GNUNET_YES; +} + + +/** + * Recalculate our auditor list, we got /keys and it may have + * changed. + * + * @param exchange exchange for which to update the list. + */ +static void +update_auditors (struct TALER_EXCHANGE_Handle *exchange) +{ + struct TALER_EXCHANGE_Keys *kd = &exchange->key_data; + + for (unsigned int i=0;inum_auditors;i++) + { + struct TALER_EXCHANGE_AuditorInformation *auditor = &kd->auditors[i]; + struct TEAH_AuditorListEntry *ale = NULL; + + for (struct TEAH_AuditorListEntry *a = exchange->auditors_head; + NULL != a; + a = a->next) + { + if (0 == memcmp (&auditor->auditor_pub, + &a->auditor_pub, + sizeof (struct TALER_AuditorPublicKeyP))) + { + ale = a; + break; + } + } + if (NULL != ale) + continue; /* found, no need to add */ + /* new auditor, add */ + ale = GNUNET_new (struct TEAH_AuditorListEntry); + ale->auditor_pub = auditor->auditor_pub; + ale->auditor_url = GNUNET_strdup (auditor->auditor_url); + GNUNET_CONTAINER_DLL_insert (exchange->auditors_head, + exchange->auditors_tail, + ale); + ale->ah = TALER_AUDITOR_connect (exchange->ctx, + ale->auditor_url, + &auditor_version_cb, + ale); + } +} + + + /** * Decode the JSON in @a resp_obj from the /keys response * and store the data in the @a key_data. @@ -827,6 +946,7 @@ decode_keys_json (const json_t *resp_obj, GNUNET_array_grow (key_data->auditors, key_data->auditors_size, key_data->auditors_size * 2 + 2); + GNUNET_assert (NULL != ai.auditor_url); key_data->auditors[key_data->num_auditors++] = ai; }; } @@ -997,6 +1117,7 @@ keys_completed_cb (void *cls, struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i]; anew->auditor_pub = aold->auditor_pub; + GNUNET_assert (NULL != aold->auditor_url); anew->auditor_url = GNUNET_strdup (aold->auditor_url); GNUNET_array_grow (anew->denom_keys, anew->num_denom_keys, @@ -1072,6 +1193,7 @@ keys_completed_cb (void *cls, exchange->key_data_expiration = kr->expire; free_keys_request (kr); exchange->state = MHS_CERT; + update_auditors (exchange); /* notify application about the key information */ exchange->cert_cb (exchange->cert_cb_cls, &exchange->key_data, @@ -1305,6 +1427,7 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange, exchange->key_data = key_data; exchange->key_data_expiration = expire; exchange->state = MHS_CERT; + update_auditors (exchange); /* notify application about the key information */ exchange->cert_cb (exchange->cert_cb_cls, &exchange->key_data, @@ -1619,6 +1742,31 @@ request_keys (void *cls) void TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange) { + struct TEAH_AuditorListEntry *ale; + + while (NULL != (ale = exchange->auditors_head)) + { + struct TEAH_AuditorInteractionEntry *aie; + + while (NULL != (aie = ale->ai_head)) + { + GNUNET_assert (aie->ale == ale); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Not sending deposit confirmation to auditor `%s' due to exchange disconnect\n"), + ale->auditor_url); + TALER_AUDITOR_deposit_confirmation_cancel (aie->dch); + GNUNET_CONTAINER_DLL_remove (ale->ai_head, + ale->ai_tail, + aie); + GNUNET_free (aie); + } + GNUNET_CONTAINER_DLL_remove (exchange->auditors_head, + exchange->auditors_tail, + ale); + TALER_AUDITOR_disconnect (ale->ah); + GNUNET_free (ale->auditor_url); + GNUNET_free (ale); + } if (NULL != exchange->kr) { GNUNET_CURL_job_cancel (exchange->kr->job); diff --git a/src/lib/exchange_api_handle.h b/src/lib/exchange_api_handle.h index f06fa4eef..2c01e319a 100644 --- a/src/lib/exchange_api_handle.h +++ b/src/lib/exchange_api_handle.h @@ -26,21 +26,70 @@ #include "taler_crypto_lib.h" +/** + * Entry in DLL of auditors used by an exchange. + */ +struct TEAH_AuditorListEntry; + + +/** + * Entry in list of ongoing interactions with an auditor. + */ +struct TEAH_AuditorInteractionEntry +{ + /** + * DLL entry. + */ + struct TEAH_AuditorInteractionEntry *next; + + /** + * DLL entry. + */ + struct TEAH_AuditorInteractionEntry *prev; + + /** + * Which auditor is this action associated with? + */ + struct TEAH_AuditorListEntry *ale; + + /** + * Interaction state. + */ + struct TALER_AUDITOR_DepositConfirmationHandle *dch; +}; + + /** * Function called for each auditor to give us a chance to possibly - * launch a deposit confirmation interaction. - * + * launch a deposit confirmation interaction. + * * @param cls closure * @param ah handle to the auditor * @param auditor_pub public key of the auditor * @return NULL if no deposit confirmation interaction was launched */ -typedef struct TALER_AUDITOR_DepositConfirmationHandle * +typedef struct TEAH_AuditorInteractionEntry * (*TEAH_AuditorCallback)(void *cls, struct TALER_AUDITOR_Handle *ah, const struct TALER_AuditorPublicKeyP *auditor_pub); +/** + * Signature of functions called with the result from our call to the + * auditor's /deposit-confirmation handler. + * + * @param cls closure of type `struct TEAH_AuditorInteractionEntry *` + * @param http_status HTTP status code, 200 on success + * @param ec taler protocol error status code, 0 on success + * @param json raw json response + */ +void +TEAH_acc_confirmation_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *json); + + /** * Iterate over all available auditors for @a h, calling * @param ah and giving it a chance to start a deposit