commit c3eca1b551fe824e02c26bdd2e8f98cf75b605aa parent 9f951fd902629ee35cb2f9436b854dd86a9bf498 Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com> Date: Thu, 31 Jul 2025 00:31:13 +0200 small fix Diffstat:
20 files changed, 833 insertions(+), 331 deletions(-)
diff --git a/src/backend/taler-merchant-donaukeyupdate.c b/src/backend/taler-merchant-donaukeyupdate.c @@ -128,6 +128,52 @@ static struct Donau *d_head; static struct Donau *d_tail; /** + * Context for the charity force download. + */ +struct ForceCharityCtx +{ + /** + * Pointer to the next ForceCharityCtx in the doubly linked list. + */ + struct ForceCharityCtx *next; + + /** + * Pointer to the previous ForceCharityCtx in the doubly linked list. + */ + struct ForceCharityCtx *prev; + + /** + * Serial of the Donau instance in our DB for which we running the force update. + */ + uint64_t di_serial; + + /** + * Base URL of the Donau instance for which we are running the force update. + */ + char *donau_url; + + /** + * ID of the charity for which we are running the force update. + */ + uint64_t charity_id; + + /** + * Handle to the charity update request. + */ + struct DONAU_CharityGetHandle *h; +}; + +/** + * Head of the list of charity force updates. + */ +static struct ForceCharityCtx *fcc_head; + +/** + * Tail of the list of charity force updates. + */ +static struct ForceCharityCtx *fcc_tail; + +/** * The merchant's configuration. */ static const struct GNUNET_CONFIGURATION_Handle *cfg; @@ -143,6 +189,11 @@ static struct TALER_MERCHANTDB_Plugin *db_plugin; static struct GNUNET_DB_EventHandler *eh; /** + * Our event handler listening for /charity_id forced downloads. + */ +static struct GNUNET_DB_EventHandler *eh_charity; + +/** * Handle to the context for interacting with the Donau services. */ static struct GNUNET_CURL_Context *ctx; @@ -255,7 +306,6 @@ store_donau_keys (struct DONAU_Keys *keys, struct GNUNET_TIME_Absolute first_retry) { enum GNUNET_DB_QueryStatus qs; - db_plugin->preflight (db_plugin->cls); for (unsigned int r = 0; r < MAX_RETRIES; r++) { @@ -287,7 +337,8 @@ store_donau_keys (struct DONAU_Keys *keys, { db_plugin->rollback (db_plugin->cls); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to commit Donau keys to the database: status %d", qs); + "Failed to commit Donau keys to the database: status %d", + qs); if (GNUNET_DB_STATUS_SOFT_ERROR == qs) continue; GNUNET_break (0); @@ -305,6 +356,92 @@ store_donau_keys (struct DONAU_Keys *keys, /** + * Store Donau charity in the database and handle retries. + * + * @param charity_id the charity ID to store + * @param donau_url the base URL of the Donau instance + * @param charity the charity structure to store + */ +static bool +store_donau_charity (uint64_t charity_id, + const char *donau_url, + const struct DONAU_Charity *charity) +{ + enum GNUNET_DB_QueryStatus qs; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Inserting/updating charity %llu for Donau `%s'\n", + (unsigned long long) charity_id, + donau_url); + + db_plugin->preflight (db_plugin->cls); + + for (unsigned int r = 0; r < MAX_RETRIES; r++) + { + if (GNUNET_OK != + db_plugin->start (db_plugin->cls, + "update donau charity data")) + { + db_plugin->rollback (db_plugin->cls); + GNUNET_break (0); + return false; + } + + qs = db_plugin->update_donau_instance (db_plugin->cls, + donau_url, + charity, + charity_id); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + db_plugin->rollback (db_plugin->cls); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error while updating charity into the database: status %d", + qs); + continue; + } + if (0 >= qs) + { + db_plugin->rollback (db_plugin->cls); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error while updating charity into the database: status %d", + qs); + return false; + } + + qs = db_plugin->commit (db_plugin->cls); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + db_plugin->rollback (db_plugin->cls); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to commit charity data to the database: status %d", + qs); + continue; + } + if (0 > qs) + { + db_plugin->rollback (db_plugin->cls); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to commit charity data to the database: status %d", + qs); + return false; + } + break; + } + if (0 >= qs) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Retries exhausted while inserting charity %llu for Donau `%s': last status %d", + (unsigned long long) charity_id, + donau_url, + qs); + return false; + } + return true; +} + + +/** * Callback after Donau keys are fetched. * * @param cls closure with a `struct Donau *` @@ -349,13 +486,11 @@ donau_cert_cb ( /* FIXME: Might be good to reference some key_data_expiration and not first sign_key*/ n = GNUNET_TIME_absolute_max (d->first_retry, keys->sign_keys[0].expire_sign.abs_time); - if (NULL != d->retry_task) GNUNET_SCHEDULER_cancel (d->retry_task); d->retry_task = GNUNET_SCHEDULER_add_at (n, &download_keys, d); - end_inquiry (); return; default: @@ -375,7 +510,6 @@ donau_cert_cb ( = GNUNET_SCHEDULER_add_at (n, &download_keys, d); - end_inquiry (); } @@ -431,6 +565,99 @@ download_keys (void *cls) /** + * Callback for DONAU_charity_get() that stores the charity + * information in the DB and finishes the inquiry. + * + * @param cls closure with `struct ForceCharityCtx *` + * @param gcr response from DONAU + */ +static void +donau_charity_cb (void *cls, + const struct DONAU_GetCharityResponse *gcr) +{ + struct ForceCharityCtx *fcc = cls; + fcc->h = NULL; + + switch (gcr->hr.http_status) + { + case MHD_HTTP_OK: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got charity_id `%llu' details for donau `%s', updating DB\n", + (unsigned long long) fcc->charity_id, + fcc->donau_url); + + if (! store_donau_charity (fcc->charity_id, + fcc->donau_url, + &gcr->details.ok.charity)) + { + GNUNET_break (0); + } + break; + + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "DONAU charity_get for `%s' failed with HTTP %u / ec %u\n", + fcc->donau_url, + gcr->hr.http_status, + gcr->hr.ec); + break; + } + + end_inquiry (); +} + + +/** + * Download the charity_id for a Donau instance. + * + * @param cls closure with a `struct Donau *` + */ +static void +download_charity_id (void *cls) +{ + struct ForceCharityCtx *fcc = cls; + + /* nothing to do if a request is already outstanding */ + if (NULL != fcc->h) + return; + + /* respect global inquiry limit */ + GNUNET_break (OPEN_INQUIRY_LIMIT >= active_inquiries); + if (OPEN_INQUIRY_LIMIT <= active_inquiries) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Cannot start more charity inquiries, already at limit\n"); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Downloading charity `%llu' from `%s'\n", + (unsigned long long) fcc->charity_id, + fcc->donau_url); + + fcc->h = DONAU_charity_get (ctx, + fcc->donau_url, + fcc->charity_id, + NULL, /* bearer token -- not needed */ + &donau_charity_cb, + fcc); + + if (NULL != fcc->h) + { + active_inquiries++; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to initiate DONAU_charity_get() for `%s'\n", + fcc->donau_url); + /* we do NOT retry here – simply finish the (failed) inquiry */ + end_inquiry (); + } +} + + +/** * Lookup donau by @a donau_url. Create one * if it does not exist. * @@ -449,13 +676,107 @@ lookup_donau (const char *donau_url) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Got notification about unknown Donau `%s'\n", donau_url); + return NULL; +} +/** + * Lookup a ForceCharityCtx by donau-instance serial. + * + * @param di_serial serial to search for + * @return matching context or NULL + */ +static struct ForceCharityCtx * +lookup_donau_charity (uint64_t di_serial) +{ + for (struct ForceCharityCtx *fcc = fcc_head; + NULL != fcc; + fcc = fcc->next) + if (fcc->di_serial == di_serial) + return fcc; return NULL; } /** + * Force immediate (re)loading of /charity_id for an donau. + * + * @param cls NULL + * @param extra base URL of the donau that changed + * @param extra_len number of bytes in @a extra + */ +static void +force_donau_charity_id (void *cls, + const void *extra, + size_t extra_len) +{ + if ( (sizeof(uint64_t) != extra_len) + || (NULL == extra) ) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect extra for the force_donau_charity_id"); + return; + } + uint64_t di_serial; + char *donau_url = NULL; + uint64_t charity_id = -1; + + GNUNET_memcpy (&di_serial, + extra, + sizeof(uint64_t)); + di_serial = GNUNET_ntohll (di_serial); + + enum GNUNET_DB_QueryStatus qs = + db_plugin->select_donau_instance_by_serial (db_plugin->cls, + di_serial, + &donau_url, + &charity_id); + + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "force_donau_charity_id: instance serial %llu not found (status %d)\n", + (unsigned long long) di_serial, + qs); + return; + } + + struct ForceCharityCtx *fcc = + lookup_donau_charity (di_serial); + if (NULL == fcc) + { + fcc = GNUNET_new (struct ForceCharityCtx); + fcc->di_serial = di_serial; + fcc->donau_url = donau_url; /* take ownership */ + fcc->charity_id = charity_id; + GNUNET_CONTAINER_DLL_insert (fcc_head, fcc_tail, fcc); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Created new ForceCharityCtx for donau `%s' " + "(serial %llu, charity %llu)\n", + donau_url, + (unsigned long long) di_serial, + (unsigned long long) charity_id); + } + else + { + GNUNET_free (donau_url); + if (NULL != fcc->h) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Already downloading charity_id for donau `%s'\n", + fcc->donau_url); + return; + } + } + + download_charity_id (fcc); +} + + +/** * Force immediate (re)loading of /keys for an donau. * * @param cls NULL @@ -496,7 +817,9 @@ force_donau_keys (void *cls, d->retry_delay = DONAU_MAXFREQ; d->first_retry = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_ZERO); - GNUNET_CONTAINER_DLL_insert (d_head, d_tail, d); + GNUNET_CONTAINER_DLL_insert (d_head, + d_tail, + d); download_keys (d); } @@ -525,131 +848,6 @@ force_donau_keys (void *cls, /** - * Function called on each configuration section. Finds sections - * about donau, parses the entries. - * - * @param cls NULL - * @param section name of the section - */ -static void -accept_donau (void *cls, - const char *section) -{ - char *url; - char *currency; - if (0 != - strncasecmp (section, - "merchant-donau-", - strlen ("merchant-donau-"))) - return; - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - section, - "DISABLE")) - return; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "DONAU_URL", - &url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "DONAU_BASE_URL"); - global_ret = EXIT_NOTCONFIGURED; - GNUNET_SCHEDULER_shutdown (); - return; - } - - for (struct Donau *d = d_head; - NULL != d; - d = d->next) - { - if (0 == strcmp (url, - d->donau_url)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Duplicate donau URL `%s', maybe set DISABLED in section `%s'\n", - url, - section); - GNUNET_free (url); - global_ret = EXIT_NOTCONFIGURED; - GNUNET_SCHEDULER_shutdown (); - return; - } - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "CURRENCY", - ¤cy)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "CURRENCY"); - GNUNET_free (url); - global_ret = EXIT_NOTCONFIGURED; - GNUNET_SCHEDULER_shutdown (); - return; - } - { - struct Donau *d; - d = GNUNET_new (struct Donau); - d->donau_url = url; - GNUNET_CONTAINER_DLL_insert (d_head, - d_tail, - d); - { - enum GNUNET_DB_QueryStatus qs; - struct DONAU_Keys *keys; - qs = db_plugin->lookup_donau_keys (db_plugin->cls, - url, - &d->first_retry, - &keys); - if (0 > qs) - { - GNUNET_break (0); - global_ret = EXIT_FAILURE; - GNUNET_SCHEDULER_shutdown (); - return; - } - - if ( (NULL != keys) && - (0 != strcmp (keys->currency, - d->currency)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "/keys cached in our database were for currency `%s', but we expected `%s'. Fetching /keys again.\n", - keys->currency, - d->currency); - DONAU_keys_decref (keys); - keys = NULL; - } - - d->keys = keys; - if (NULL == keys) - { - /* done synchronously so that the active_inquiries - is updated immediately */ - download_keys (d); - } - else - { - d->retry_task - = GNUNET_SCHEDULER_add_at (keys->sign_keys[0].expire_sign.abs_time, - &download_keys, - d); - } - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "DONAU `%s' setup\n", - d->donau_url); - } -} - - -/** * We're being aborted with CTRL-C (or SIGTERM). Shut down. * * @param cls closure (NULL) @@ -691,6 +889,11 @@ shutdown_task (void *cls) db_plugin->event_listen_cancel (eh); eh = NULL; } + if (NULL != eh_charity) + { + db_plugin->event_listen_cancel (eh_charity); + eh_charity = NULL; + } TALER_MERCHANTDB_plugin_unload (db_plugin); db_plugin = NULL; cfg = NULL; @@ -774,9 +977,19 @@ run (void *cls, &force_donau_keys, NULL); } - GNUNET_CONFIGURATION_iterate_sections (cfg, - &accept_donau, - NULL); + { + struct GNUNET_DB_EventHeaderP es = { + .size = ntohs (sizeof(es)), + .type = ntohs (TALER_DBEVENT_MERCHANT_DONAU_CHARITY_ID) + }; + + eh_charity = db_plugin->event_listen ( + db_plugin->cls, + &es, + GNUNET_TIME_UNIT_FOREVER_REL, + &force_donau_charity_id, + NULL); + } if ( (0 == active_inquiries) && (test_mode) ) @@ -828,4 +1041,4 @@ main (int argc, } -/* end of taler-merchant-donaukeyupdate.c */ +/* end of taler-merchant-donaukeyupdate.c */ +\ No newline at end of file diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -544,6 +544,11 @@ struct PayContext */ struct DonauData donau; + /** + * Serial from the DB of the donau instance that we are using + */ + uint64_t donau_instance_serial; + #ifdef HAVE_DONAU_DONAU_SERVICE_H /** * Number of the blinded key pairs @e bkps @@ -1878,10 +1883,72 @@ phase_payment_notification (struct PayContext *pc) NULL, 0); } + if (NULL != pc->donau_receipt.donau_sigs_json) + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_DONAU_CHARITY_ID) + }; + uint64_t serial_net = GNUNET_htonll ( + pc->parse_wallet_data.donau_instance_serial); + + TMH_db->event_notify ( + TMH_db->cls, + &es, + &serial_net, + sizeof(serial_net) + ); + } pc->phase = PP_SUCCESS_RESPONSE; } +#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. * @@ -1891,12 +1958,28 @@ static void phase_process_donation_receipt (struct PayContext *pc) { #ifndef HAVE_DONAU_DONAU_SERVICE_H - /* No Donau support compiled in – just continue the FSM. */ + /* Theoretically impossible, yet just to be safe */ pc->phase = PP_PAYMENT_NOTIFICATION; return; #else + 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)) ) + { + 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")); + return; + } + /* Attaching first sig from the donau as output_token signature, - * to reference the output_token of donau */ + * to reference the output_token related to donau_sigs */ const struct TALER_MERCHANT_ContractChoice *choice = &pc->check_contract.contract_terms->details.v1 .choices[pc->parse_wallet_data.choice_index]; @@ -1918,9 +2001,11 @@ phase_process_donation_receipt (struct PayContext *pc) if (GNUNET_OK != TMH_db->start (TMH_db->cls, "insert_order_blinded_sigs")) { - resume_pay_with_error (pc, - TALER_EC_GENERIC_DB_START_FAILED, - "start insert blinded donation receipts failed"); + 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; } @@ -1986,34 +2071,6 @@ phase_process_donation_receipt (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 #GNUNET_OK if all is fine, #GNUNET_SYSERR if we could not parse - * is malformed. - */ -static void -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]; - - GNUNET_assert ( - 0 == json_array_append ( - j_signatures, - GNUNET_JSON_PACK ( - DONAU_JSON_pack_blinded_donation_unit_sig ("blinded_signature", - signature)))); - } -} - - -/** * Callback to handle the result of a batch issue request. * * @param cls our `struct PayContext` @@ -2029,14 +2086,14 @@ merchant_donau_issue_receipt_cb (void *cls, GNUNET_assert (GNUNET_YES == pc->suspended); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Donau responded with status=%u, ec=%u\n", + "Donau responded with status=%u, ec=%u", resp->hr.http_status, resp->hr.ec); switch (resp->hr.http_status) { case 0: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Donau batch issue request from merchant-httpd failed (http_status==0)\n"); + "Donau batch issue request from merchant-httpd failed (http_status==0)"); resume_pay_with_error (pc, resp->hr.ec, "Donau batch issue request failed"); @@ -2044,19 +2101,27 @@ merchant_donau_issue_receipt_cb (void *cls, case MHD_HTTP_OK: case MHD_HTTP_CREATED: - /* Most probably, it is just some small flaw from - * donau so no point in failing, yet we have to display it*/ 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\n", + "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\n", + "Donau accepted donation receipts with total_issued=%s", TALER_amount2s (&resp->details.ok.issued_amount)); - break; /* continue normal handling after switch*/ + 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 (); + pc->phase = PP_PROCESS_DONATION_RECEIPT; + pay_resume (pc); + return; case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_FORBIDDEN: @@ -2064,7 +2129,7 @@ merchant_donau_issue_receipt_cb (void *cls, case MHD_HTTP_INTERNAL_SERVER_ERROR: default: /* make sure that everything except 200/201 will end up here*/ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Donau replied with HTTP %u (ec=%u)\n", + "Donau replied with HTTP %u (ec=%u)", resp->hr.http_status, resp->hr.ec); resume_pay_with_error (pc, @@ -2072,18 +2137,6 @@ merchant_donau_issue_receipt_cb (void *cls, "Donau HTTP error"); return; } - - 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 (); - signatures_to_json (pc->donau_receipt.num_sigs, - pc->donau_receipt.sigs, - pc->donau_receipt.donau_sigs_json); - pc->phase = PP_PROCESS_DONATION_RECEIPT; - pay_resume (pc); } @@ -2148,7 +2201,7 @@ phase_request_donation_receipt (struct PayContext *pc) if (NULL == pc->donau_receipt.birh) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create Donau receipt request\n"); + "Failed to create Donau receipt request"); pay_end (pc, TALER_MHD_reply_with_error (pc->connection, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -3233,15 +3286,15 @@ handle_output_token (struct PayContext *pc, NULL)); return GNUNET_NO; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Token-family key for %s not found at [%llu,%llu]\n", - family->slug, - (unsigned long - long) pc->check_contract.contract_terms->timestamp.abs_time. - abs_value_us, - (unsigned long - long) pc->check_contract.contract_terms->pay_deadline.abs_time. - abs_value_us); + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + "Token-family key for %s not found at [%llu,%llu]\n", + family->slug, + (unsigned long long) + pc->check_contract.contract_terms->timestamp.abs_time.abs_value_us, + (unsigned long long) + pc->check_contract.contract_terms->pay_deadline.abs_time.abs_value_us + ); GNUNET_break (0); pay_end (pc, TALER_MHD_reply_with_error ( @@ -3537,14 +3590,15 @@ deposit_paid_check ( /** * Function called with information about a token that was spent. + * FIXME: Replace this with a more specific function for this cb * - * @param cls FIXME - * @param spent_token_serial - * @param h_contract_terms - * @param h_issue_pub - * @param use_pub - * @param use_sig - * @param issue_sig + * @param cls closure with `struct PayContext *` + * @param spent_token_serial "serial" of the spent token unused + * @param h_contract_terms hash of the contract terms unused + * @param h_issue_pub hash of the token issue public key unused + * @param use_pub public key of the token + * @param use_sig signature of the token + * @param issue_sig signature of the token issue */ static void input_tokens_paid_check ( @@ -3648,8 +3702,9 @@ phase_contract_paid (struct PayContext *pc) /* In this part we are fetching donau related outputs */ { - /* Can be replaced with reference to the output_token, - * if output_tokens also are returned in contract_paid case*/ + /* FIXME: stored_sig can be replaced with reference to + * the output_token, if output_tokens also processed + * in contract_paid case */ struct GNUNET_CRYPTO_BlindedSignature *stored_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); enum GNUNET_DB_QueryStatus qs; @@ -4123,8 +4178,6 @@ phase_parse_wallet_data (struct PayContext *pc) /* Check if the needed data is present for the given donau URL */ { enum GNUNET_DB_QueryStatus qs; - - /* Part where we fetch info about the charity */ qs = TMH_db->lookup_order_charity ( TMH_db->cls, pc->hc->instance->settings.id, @@ -4133,36 +4186,34 @@ phase_parse_wallet_data (struct PayContext *pc) &pc->parse_wallet_data.charity_priv, &pc->parse_wallet_data.charity_max_per_year, &pc->parse_wallet_data.charity_receipts_to_date, - &donau_keys_json); + &donau_keys_json, + &pc->parse_wallet_data.donau_instance_serial); - /*IDEA: C wanted to have errors firstly, I don't completely agree - * Especially for productivity reasons... This O(1) is completely crazy :) */ switch (qs) { - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* We found the charity and donau keys, continue */ - pc->parse_wallet_data.donau.donau_url = GNUNET_strdup (donau_url_tmp); - break; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: TMH_db->rollback (TMH_db->cls); pay_end (pc, TALER_MHD_reply_with_error ( pc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "No matching Donau charity found for the given URL")); + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_order_charity")); return; - case GNUNET_DB_STATUS_SOFT_ERROR: - case GNUNET_DB_STATUS_HARD_ERROR: - /* SOFT or HARD errors, MULTIPLE is also here(but select single)? ask C about it */ + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 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_FETCH_FAILED, - "lookup_order_charity")); + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "No matching Donau charity found for the given URL")); return; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + pc->parse_wallet_data.donau.donau_url = + GNUNET_strdup (donau_url_tmp); + break; } } diff --git a/src/backend/taler-merchant-httpd_private-delete-donau-instance-ID.c b/src/backend/taler-merchant-httpd_private-delete-donau-instance-ID.c @@ -47,7 +47,10 @@ TMH_private_delete_donau_instance_ID (const struct TMH_RequestHandler *rh, (void) rh; GNUNET_assert (NULL != mi); - if (1 != sscanf (hc->infix, "%lu%c", &charity_id, &dummy)) + if (1 != sscanf (hc->infix, + "%lu%c", + &charity_id, + &dummy)) { return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, @@ -55,7 +58,9 @@ TMH_private_delete_donau_instance_ID (const struct TMH_RequestHandler *rh, hc->infix); } - qs = TMH_db->delete_donau_instance (TMH_db->cls, charity_id); + qs = TMH_db->delete_donau_instance (TMH_db->cls, + &hc->instance->merchant_pub, + charity_id); switch (qs) { @@ -79,7 +84,10 @@ TMH_private_delete_donau_instance_ID (const struct TMH_RequestHandler *rh, hc->infix); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - return TALER_MHD_reply_static (connection, MHD_HTTP_NO_CONTENT, NULL, NULL, + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, 0); } diff --git a/src/backend/taler-merchant-httpd_private-get-donau-instances.c b/src/backend/taler-merchant-httpd_private-get-donau-instances.c @@ -97,6 +97,7 @@ TMH_private_get_donau_instances (const struct TMH_RequestHandler *rh, GNUNET_assert (NULL != json_donau_instances); TMH_db->preflight (TMH_db->cls); qs = TMH_db->select_donau_instances (TMH_db->cls, + &hc->instance->merchant_pub, &add_donau_instance, json_donau_instances); diff --git a/src/backend/taler-merchant-httpd_private-post-donau-instance.c b/src/backend/taler-merchant-httpd_private-post-donau-instance.c @@ -29,6 +29,171 @@ #include "taler-merchant-httpd_private-post-donau-instance.h" /** + * Context for the POST /donau request handler. + */ +struct PostDonauCtx +{ + /** + * Stored in a DLL. + */ + struct PostDonauCtx *next; + + /** + * Stored in a DLL. + */ + struct PostDonauCtx *prev; + + /** + * Connection to the MHD server + */ + struct MHD_Connection *connection; + + /** + * Context of the request handler. + */ + struct TMH_HandlerContext *hc; + + /** + * URL of the DONAU service + * to which the charity belongs. + */ + const char *donau_url; + + /** + * ID of the charity in the DONAU service. + */ + uint64_t charity_id; + + /** + * Data that comes back from Donau + */ + struct DONAU_Charity charity; + + /** + * Handle returned by DONAU_charities_get(); needed to cancel on + * connection abort, etc. */ + struct DONAU_CharityGetHandle *get_handle; +}; + +/** + * Head of active pay context DLL. + */ +static struct PostDonauCtx *pdc_head; + +/** + * Tail of active pay context DLL. + */ +static struct PostDonauCtx *pdc_tail; + +/** + * Callback for DONAU_charities_get() to handle the response. + * + * @param cls closure with PostDonauCtx + * @param gcr response from Donau + */ +static void +donau_charity_get_cb (void *cls, + const struct DONAU_GetCharityResponse *gcr) +{ + struct PostDonauCtx *pdc = cls; + pdc->get_handle = NULL; + if (NULL == pdc->connection) + return; + + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Processing DONAU charity get response"); + + /* Anything but 200 => propagate Donau’s response. */ + if (MHD_HTTP_OK != gcr->hr.http_status) + { + TALER_MHD_reply_with_error (pdc->connection, + gcr->hr.http_status, + gcr->hr.ec, + gcr->hr.hint); + MHD_resume_connection (cls); + TALER_MHD_daemon_trigger (); + return; + } + + pdc->charity = gcr->details.ok.charity; + + if (GNUNET_memcmp (&pdc->charity.charity_pub.eddsa_pub, + &pdc->hc->instance->merchant_pub.eddsa_pub)) + { + TALER_MHD_reply_with_error (pdc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "Merchant public key of this instance and charity public key received " + "from DONAU for this charity_id do not match"); + MHD_resume_connection (pdc->connection); + TALER_MHD_daemon_trigger (); + return; + } + + enum GNUNET_DB_QueryStatus qs = + TMH_db->insert_donau_instance (TMH_db->cls, + pdc->donau_url, + &pdc->charity, + pdc->charity_id); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + TALER_MHD_reply_with_error (pdc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_donau_instance: Failed to insert Donau instance"); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + TALER_MHD_reply_with_error (pdc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_SOFT_FAILURE, + "insert_donau_instance: Database operation failed"); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_DONAU_KEYS) + }; + TMH_db->event_notify (TMH_db->cls, + &es, + pdc->donau_url, + strlen (pdc->donau_url) + 1); + TALER_MHD_reply_static (pdc->connection, + MHD_HTTP_NO_CONTENT, + NULL, NULL, + 0); + break; + } + } + MHD_resume_connection (pdc->connection); + TALER_MHD_daemon_trigger (); +} + + +/** + * Cleanup function for the PostDonauCtx. + * + * @param cls closure with PostDonauCtx + */ +static void +post_donau_cleanup (void *cls) +{ + struct PostDonauCtx *pdc = cls; + + if (pdc->get_handle) + DONAU_charity_get_cancel (pdc->get_handle); + + GNUNET_CONTAINER_DLL_remove (pdc_head, + pdc_tail, + pdc); + GNUNET_free (pdc); +} + + +/** * Handle a POST "/donau" request. * * @param rh context of the handler @@ -41,28 +206,15 @@ TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, struct TMH_HandlerContext *hc) { - struct DONAU_Charity charity; const char *donau_url = NULL; - uint64_t charity_id = 0; + uint64_t charity_id = -1; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("donau_url", &donau_url), - GNUNET_JSON_spec_string ("charity_name", - (const char **) &charity.name), - GNUNET_JSON_spec_fixed_auto ("charity_pub_key", - &charity.charity_pub), GNUNET_JSON_spec_uint64 ("charity_id", &charity_id), - TALER_JSON_spec_amount_any ("max_per_year", - &charity.max_per_year), - TALER_JSON_spec_amount_any ("receipts_to_date", - &charity.receipts_to_date), - GNUNET_JSON_spec_uint64 ("current_year", - &charity.current_year), GNUNET_JSON_spec_end () }; - - // Parse JSON body of the request if (GNUNET_OK != TALER_MHD_parse_json_data (connection, hc->request_body, spec) @@ -70,59 +222,49 @@ TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh, { return MHD_NO; } - - // Validate the required parameters - if (NULL == donau_url || NULL == charity.name) + if (NULL == donau_url + || -1 == charity_id) { + GNUNET_break (0); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "Missing donau_url or charity_name"); + "Missing donau_url or charity_id"); } - // Try to insert the new Donau instance into the database + struct PostDonauCtx *pdc = hc->ctx; + if (NULL == pdc) { - enum GNUNET_DB_QueryStatus qs = - TMH_db->insert_donau_instance (TMH_db->cls, - donau_url, - &charity, - charity_id); - switch (qs) + pdc = GNUNET_new (struct PostDonauCtx); + pdc->connection = connection; + pdc->hc = hc; + pdc->donau_url = donau_url; + pdc->charity_id = charity_id; + hc->ctx = pdc; + hc->cc = &post_donau_cleanup; + GNUNET_CONTAINER_DLL_insert (pdc_head, + pdc_tail, + pdc); + + pdc->get_handle = + DONAU_charity_get (TMH_curl_ctx, + donau_url, + charity_id, + NULL, /* bearer */ + &donau_charity_get_cb, + pdc); + + if (NULL == pdc->get_handle) { - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* Everything fine we can finish with throwing - * the event_notify in the next part */ - break; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - case GNUNET_DB_STATUS_SOFT_ERROR: - default: - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - "Database operation failed"); - case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_free (pdc); return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "Failed to insert Donau instance"); + MHD_HTTP_SERVICE_UNAVAILABLE, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + "Failed to initiate Donau lookup"); } - /* Notify the event system about the new Donau instance */ - { - struct GNUNET_DB_EventHeaderP es = { - .size = ntohs (sizeof (es)), - .type = ntohs (TALER_DBEVENT_MERCHANT_DONAU_KEYS) - }; - - TMH_db->event_notify (TMH_db->cls, - &es, - donau_url, - strlen (donau_url) + 1); - - return TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, NULL, - 0); - } + MHD_suspend_connection (connection); + return MHD_YES; } + return MHD_YES; } diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -3254,8 +3254,6 @@ add_donau_output (struct OrderContext *oc, struct TALER_MERCHANT_ContractOutput *output) { enum GNUNET_DB_QueryStatus qs; - - /* Invoke the database call, accumulating URLs in a JSON array */ qs = TMH_db->select_donau_instances_filtered ( TMH_db->cls, output->details.donation_receipt.amount.currency, @@ -3522,6 +3520,7 @@ phase_parse_choices (struct OrderContext *oc) /* ***************** ORDER_PHASE_PARSE_ORDER **************** */ + /** * Parse the order field of the request. Upon success, continue * processing with parse_choices(). diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am @@ -223,10 +223,12 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES += \ pg_upsert_donau_keys.h pg_upsert_donau_keys.c \ pg_insert_donau_instance.h pg_insert_donau_instance.c \ pg_insert_order_blinded_sigs.h pg_insert_order_blinded_sigs.c \ + pg_select_donau_instance_by_serial.h pg_select_donau_instance_by_serial.c \ pg_select_donau_instances.h pg_select_donau_instances.c \ pg_select_donau_instances_filtered.h pg_select_donau_instances_filtered.c \ pg_select_order_blinded_sigs.h pg_select_order_blinded_sigs.c \ - pg_delete_donau_instance.h pg_delete_donau_instance.c + pg_delete_donau_instance.h pg_delete_donau_instance.c \ + pg_update_donau_instance.h pg_update_donau_instance.c endif libtaler_plugin_merchantdb_postgres_la_LIBADD = \ diff --git a/src/backenddb/merchant-0016.sql b/src/backenddb/merchant-0016.sql @@ -18,6 +18,7 @@ -- @brief Rename default instance to admin instance -- @author Florian Dold + BEGIN; -- Check patch versioning is in place. diff --git a/src/backenddb/merchant-0018.sql b/src/backenddb/merchant-0018.sql @@ -18,6 +18,7 @@ -- @brief Tables for statistics -- @author Christian Grothoff + BEGIN; -- Check patch versioning is in place. diff --git a/src/backenddb/merchant-0022.sql b/src/backenddb/merchant-0022.sql @@ -48,7 +48,8 @@ CREATE TABLE IF NOT EXISTS merchant_donau_instances (donau_instances_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,donau_url TEXT NOT NULL ,charity_name TEXT NOT NULL - ,charity_pub_key BYTEA CHECK (LENGTH(charity_pub_key)=32) + ,merchant_instance_serial INT8 NOT NULL + REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE ,charity_id BIGINT NOT NULL ,charity_max_per_year taler_amount_currency NOT NULL ,charity_receipts_to_date taler_amount_currency NOT NULL @@ -61,8 +62,8 @@ COMMENT ON COLUMN merchant_donau_instances.donau_instances_serial IS 'Unique serial identifier for each Donau instance'; COMMENT ON COLUMN merchant_donau_instances.donau_url IS 'The URL associated with the Donau system for this instance'; -COMMENT ON COLUMN merchant_donau_instances.charity_pub_key - IS 'The public key of the charity organization linked to this instance, with a 32-byte length constraint'; +COMMENT ON COLUMN merchant_donau_instances.merchant_instance_serial + IS 'The serial from merchant_instances whose public key is public key of the charity organization'; COMMENT ON COLUMN merchant_donau_instances.charity_id IS 'The unique identifier for the charity organization linked to this Donau instance'; COMMENT ON COLUMN merchant_donau_instances.charity_max_per_year diff --git a/src/backenddb/pg_delete_donau_instance.c b/src/backenddb/pg_delete_donau_instance.c @@ -29,18 +29,24 @@ enum GNUNET_DB_QueryStatus TMH_PG_delete_donau_instance ( void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, const uint64_t charity_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&charity_id), + GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; check_connection (pg); PREPARE (pg, "delete_donau_instance", - "DELETE FROM merchant_donau_instances WHERE charity_id = $1"); + "DELETE FROM merchant_donau_instances di" + " USING merchant_instances mi" + " WHERE di.merchant_instance_serial = mi.merchant_serial" + " AND di.charity_id = $1" + " AND mi.merchant_pub = $2;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "delete_donau_instance", params); diff --git a/src/backenddb/pg_delete_donau_instance.h b/src/backenddb/pg_delete_donau_instance.h @@ -36,6 +36,7 @@ enum GNUNET_DB_QueryStatus TMH_PG_delete_donau_instance ( void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, const uint64_t charity_id); #endif diff --git a/src/backenddb/pg_insert_donau_instance.c b/src/backenddb/pg_insert_donau_instance.c @@ -27,6 +27,7 @@ #include "pg_insert_donau_instance.h" #include "pg_helper.h" + enum GNUNET_DB_QueryStatus TMH_PG_insert_donau_instance ( void *cls, @@ -35,6 +36,7 @@ TMH_PG_insert_donau_instance ( const uint64_t charity_id) { struct PostgresClosure *pg = cls; + enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (donau_url), GNUNET_PQ_query_param_string (charity->name), @@ -49,18 +51,47 @@ TMH_PG_insert_donau_instance ( }; check_connection (pg); + PREPARE (pg, "insert_donau_instance", "INSERT INTO merchant_donau_instances" - "(donau_url" - ",charity_name" - ",charity_pub_key" - ",charity_id" - ",charity_max_per_year" - ",charity_receipts_to_date" - ",current_year)" - "VALUES ($1, $2, $3, $4, $5, $6, $7);"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, + " (donau_url" + " ,charity_name" + " ,merchant_instance_serial" + " ,charity_id" + " ,charity_max_per_year" + " ,charity_receipts_to_date" + " ,current_year)" + "VALUES" + " ($1, $2," + " (SELECT merchant_serial" + " FROM merchant_instances mi" + " WHERE mi.merchant_pub = $3)," + " $4, $5, $6, $7);"); + + PREPARE (pg, + "update_donau_instance", + "UPDATE merchant_donau_instances SET" + " charity_name = $2," + " merchant_instance_serial = (SELECT merchant_serial" + " FROM merchant_instances mi" + " WHERE mi.merchant_pub = $3)," + " charity_max_per_year = $5," + " charity_receipts_to_date = $6," + " current_year = $7" + " WHERE" + " charity_id = $4" + " AND donau_url = $1;"); + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "update_donau_instance", + params); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_donau_instance", params); -} -\ No newline at end of file + } + + return qs; +} diff --git a/src/backenddb/pg_lookup_order_charity.c b/src/backenddb/pg_lookup_order_charity.c @@ -42,7 +42,8 @@ TMH_PG_lookup_order_charity ( struct DONAU_CharityPrivateKeyP *charity_priv, struct TALER_Amount *charity_max_per_year, struct TALER_Amount *charity_receipts_to_date, - json_t **donau_keys_json) + json_t **donau_keys_json, + uint64_t *donau_instance_serial) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -62,6 +63,8 @@ TMH_PG_lookup_order_charity ( charity_max_per_year), TALER_PQ_result_spec_amount_with_currency ("charity_receipts_to_date", charity_receipts_to_date), + GNUNET_PQ_result_spec_uint64 ("donau_instances_serial", + donau_instance_serial), GNUNET_PQ_result_spec_end }; @@ -69,20 +72,21 @@ TMH_PG_lookup_order_charity ( PREPARE (pg, "lookup_donau_charity", "SELECT" - " di.charity_id" + " di.donau_instances_serial" + " ,di.charity_id" " ,k.merchant_priv" " ,dk.keys_json" " ,di.charity_max_per_year" " ,di.charity_receipts_to_date" " FROM merchant_donau_instances di" - " JOIN merchant_donau_keys dk" - " ON dk.donau_url = di.donau_url" - " JOIN merchant_instances mi" - " ON mi.merchant_pub = di.charity_pub_key" - " JOIN merchant_keys k" + " JOIN merchant_donau_keys dk" + " ON dk.donau_url = di.donau_url" + " JOIN merchant_instances mi" + " ON mi.merchant_serial = di.merchant_instance_serial" + " JOIN merchant_keys k" " ON k.merchant_serial = mi.merchant_serial" - " WHERE mi.merchant_id = $1" - " AND di.donau_url = $2;"); + " WHERE mi.merchant_id = $1" + " AND di.donau_url = $2;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_donau_charity", diff --git a/src/backenddb/pg_lookup_order_charity.h b/src/backenddb/pg_lookup_order_charity.h @@ -58,6 +58,7 @@ TMH_PG_lookup_order_charity ( struct DONAU_CharityPrivateKeyP *charity_priv, struct TALER_Amount *charity_max_per_year, struct TALER_Amount *charity_receipts_to_date, - json_t **donau_keys_json); + json_t **donau_keys_json, + uint64_t *donau_instance_serial); #endif /* PG_LOOKUP_ORDER_CHARITY_H */ \ No newline at end of file diff --git a/src/backenddb/pg_select_donau_instances.c b/src/backenddb/pg_select_donau_instances.c @@ -95,18 +95,15 @@ select_donau_instance_cb (void *cls, GNUNET_PQ_result_spec_end }; - // Extract result for each row if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { - GNUNET_break (0); // Break on failure + GNUNET_break (0); sdc->extract_failed = true; return; } - - // Call the callback function with the individual values sdc->cb (sdc->cb_cls, donau_url, charity_name, @@ -116,15 +113,16 @@ select_donau_instance_cb (void *cls, &charity_receipts_to_date, current_year, donau_keys_json); - - // Clean up any dynamically allocated results in the result spec GNUNET_PQ_cleanup_result (rs); } } +/*FIXME: We might want to limit the select by merchant_pub for charity_pub_key*/ enum GNUNET_DB_QueryStatus TMH_PG_select_donau_instances (void *cls, + const struct + TALER_MerchantPublicKeyP *merchant_pub, TALER_MERCHANTDB_DonauInstanceCallback cb, void *cb_cls) { @@ -137,6 +135,7 @@ TMH_PG_select_donau_instances (void *cls, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; @@ -147,15 +146,17 @@ TMH_PG_select_donau_instances (void *cls, "SELECT" " di.donau_url" ",di.charity_name" - ",di.charity_pub_key" + ",mi.merchant_pub AS charity_pub_key" ",di.charity_id" ",di.charity_max_per_year" ",di.charity_receipts_to_date" ",di.current_year" ",dk.keys_json" " FROM merchant_donau_instances di" - " JOIN merchant_donau_keys dk" - " ON di.donau_url = dk.donau_url"); + " JOIN merchant_donau_keys dk ON di.donau_url = dk.donau_url" + " JOIN merchant_instances mi ON di.merchant_instance_serial = mi.merchant_serial" + " WHERE mi.merchant_pub = $1"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "select_donau_instances", diff --git a/src/backenddb/pg_select_donau_instances.h b/src/backenddb/pg_select_donau_instances.h @@ -38,6 +38,8 @@ */ enum GNUNET_DB_QueryStatus TMH_PG_select_donau_instances (void *cls, + const struct + TALER_MerchantPublicKeyP *merchant_pub, TALER_MERCHANTDB_DonauInstanceCallback cb, void *cb_cls); diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c @@ -165,12 +165,14 @@ #ifdef HAVE_DONAU_DONAU_SERVICE_H #include "donau/donau_service.h" #include "pg_insert_donau_instance.h" +#include "pg_select_donau_instance_by_serial.h" #include "pg_select_donau_instances.h" #include "pg_select_donau_instances_filtered.h" #include "pg_delete_donau_instance.h" #include "pg_lookup_donau_keys.h" #include "pg_lookup_order_charity.h" #include "pg_upsert_donau_keys.h" +#include "pg_update_donau_instance.h" #include "pg_insert_order_blinded_sigs.h" #include "pg_select_order_blinded_sigs.h" #endif @@ -665,9 +667,11 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_lookup_statistics_amount_by_interval; plugin->gc = &TMH_PG_gc; - #ifdef HAVE_DONAU_DONAU_SERVICE_H +#ifdef HAVE_DONAU_DONAU_SERVICE_H plugin->insert_donau_instance = &TMH_PG_insert_donau_instance; + plugin->select_donau_instance_by_serial + = &TMH_PG_select_donau_instance_by_serial; plugin->select_donau_instances = &TMH_PG_select_donau_instances; plugin->select_donau_instances_filtered @@ -680,11 +684,13 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_lookup_order_charity; plugin->upsert_donau_keys = &TMH_PG_upsert_donau_keys; + plugin->update_donau_instance + = &TMH_PG_update_donau_instance; plugin->insert_order_blinded_sigs = &TMH_PG_insert_order_blinded_sigs; plugin->select_order_blinded_sigs = &TMH_PG_select_order_blinded_sigs; - #endif +#endif return plugin; } diff --git a/src/include/taler_merchant_donau.h b/src/include/taler_merchant_donau.h @@ -344,7 +344,6 @@ void TALER_MERCHANT_donau_instances_post_cancel ( struct TALER_MERCHANT_DonauInstancePostHandle *dph); - /** * Handle for a DELETE /donau/$charity_id operation. */ diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -4147,7 +4147,8 @@ struct TALER_MERCHANTDB_Plugin struct DONAU_CharityPrivateKeyP *charity_priv, struct TALER_Amount *charity_max_per_year, struct TALER_Amount *charity_receipts_to_date, - json_t **donau_keys_json + json_t **donau_keys_json, + uint64_t *donau_instance_serial ); /** @@ -4181,6 +4182,21 @@ struct TALER_MERCHANTDB_Plugin const struct GNUNET_CRYPTO_BlindedSignature *blind_sig); /** + * Select donau instance by serial number. + * + * @param cls closure + * @param serial serial number of the Donau instance in DB + * @param[out] donau_url set to the URL of the Donau instance + * @param[out] charity_id set to the charity ID of the Donau instance + */ + enum GNUNET_DB_QueryStatus + (*select_donau_instance_by_serial)( + void *cls, + uint64_t serial, + char **donau_url, + uint64_t *charity_id); + + /** * Select all Donau instances. * * @param cls closure @@ -4190,9 +4206,9 @@ struct TALER_MERCHANTDB_Plugin enum GNUNET_DB_QueryStatus (*select_donau_instances)( void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, TALER_MERCHANTDB_DonauInstanceCallback cb, - void *cb_cls - ); + void *cb_cls); /** * Select all Donau instances, but only the donau_url @@ -4207,8 +4223,7 @@ struct TALER_MERCHANTDB_Plugin void *cls, const char *currency, TALER_MERCHANTDB_DonauInstanceFilteredCallback cb, - void *cb_cls - ); + void *cb_cls); /** * Select blinded signatures for an order. @@ -4233,6 +4248,23 @@ struct TALER_MERCHANTDB_Plugin enum GNUNET_DB_QueryStatus (*delete_donau_instance)( void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const uint64_t charity_id + ); + + /** + * Update information about a Donau instance. + * + * @param cls closure + * @param donau_url URL of the Donau instance + * @param charity details of the charity + * @param charity_id charity ID of the Donau instance + */ + enum GNUNET_DB_QueryStatus + (*update_donau_instance)( + void *cls, + const char *donau_url, + const struct DONAU_Charity *charity, const uint64_t charity_id ); #endif