From 31286b66f253c62ab3c33827490b55a4cc025608 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 21 Jan 2023 21:38:50 +0100 Subject: -integrate new AML APIs into build --- src/lib/Makefile.am | 3 +++ src/lib/exchange_api_add_aml_decision.c | 7 ++++--- src/lib/exchange_api_management_update_aml_officer.c | 8 ++++---- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index b775719e6..00b604acf 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -21,6 +21,7 @@ libtalerexchange_la_LDFLAGS = \ -version-info 5:0:0 \ -no-undefined libtalerexchange_la_SOURCES = \ + exchange_api_add_aml_decision.c \ exchange_api_auditor_add_denomination.c \ exchange_api_batch_deposit.c \ exchange_api_batch_withdraw.c \ @@ -37,6 +38,7 @@ libtalerexchange_la_SOURCES = \ exchange_api_kyc_proof.c \ exchange_api_kyc_wallet.c \ exchange_api_link.c \ + exchange_api_management_add_partner.c \ exchange_api_management_auditor_disable.c \ exchange_api_management_auditor_enable.c \ exchange_api_management_drain_profits.c \ @@ -47,6 +49,7 @@ libtalerexchange_la_SOURCES = \ exchange_api_management_revoke_signing_key.c \ exchange_api_management_set_global_fee.c \ exchange_api_management_set_wire_fee.c \ + exchange_api_management_update_aml_officer.c \ exchange_api_management_wire_disable.c \ exchange_api_management_wire_enable.c \ exchange_api_melt.c \ diff --git a/src/lib/exchange_api_add_aml_decision.c b/src/lib/exchange_api_add_aml_decision.c index 7230c5ed4..d4b0d1250 100644 --- a/src/lib/exchange_api_add_aml_decision.c +++ b/src/lib/exchange_api_add_aml_decision.c @@ -136,7 +136,7 @@ TALER_EXCHANGE_add_aml_decision ( TALER_EXCHANGE_AddAmlDecisionCallback cb, void *cb_cls) { - struct TALER_AmlOfficerPrivateKeyP officer_pub; + struct TALER_AmlOfficerPublicKeyP officer_pub; struct TALER_AmlOfficerSignatureP officer_sig; struct TALER_EXCHANGE_AddAmlDecision *wh; CURL *eh; @@ -146,6 +146,7 @@ TALER_EXCHANGE_add_aml_decision ( &officer_pub.eddsa_pub); TALER_officer_aml_decision_sign (justification, decision_time, + new_threshold, h_payto, new_state, officer_priv, @@ -187,8 +188,8 @@ TALER_EXCHANGE_add_aml_decision ( &officer_sig), GNUNET_JSON_pack_data_auto ("h_payto", h_payto), - GNUNET_JSON_pack_data_uint64 ("state", - (uint32_t) new_state), + GNUNET_JSON_pack_uint64 ("state", + (uint32_t) new_state), TALER_JSON_pack_amount ("new_threshold", new_threshold), GNUNET_JSON_pack_timestamp ("decision_time", diff --git a/src/lib/exchange_api_management_update_aml_officer.c b/src/lib/exchange_api_management_update_aml_officer.c index bdc0dbe4a..96ff5a4c7 100644 --- a/src/lib/exchange_api_management_update_aml_officer.c +++ b/src/lib/exchange_api_management_update_aml_officer.c @@ -175,10 +175,10 @@ TALER_EXCHANGE_management_update_aml_officer ( officer_name), GNUNET_JSON_pack_data_auto ("master_sig", master_sig), - GNUNET_JSON_pack_data_bool ("is_active", - is_active), - GNUNET_JSON_pack_data_bool ("read_only", - read_only), + GNUNET_JSON_pack_bool ("is_active", + is_active), + GNUNET_JSON_pack_bool ("read_only", + read_only), GNUNET_JSON_pack_timestamp ("change_date", change_date)); eh = TALER_EXCHANGE_curl_easy_get_ (wh->url); -- cgit v1.2.3 From d131951fbe09b3415c9976acd11f660d51493086 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 21 Jan 2023 23:08:29 +0100 Subject: add new endpoints to main dispatcher --- src/exchange/Makefile.am | 3 + src/exchange/taler-exchange-httpd.c | 226 ++++++++++++++++++++- src/exchange/taler-exchange-httpd.h | 4 +- src/exchange/taler-exchange-httpd_aml-decision.c | 57 +++--- src/exchange/taler-exchange-httpd_aml-decision.h | 45 ++++ src/exchange/taler-exchange-httpd_management.h | 26 +++ .../taler-exchange-httpd_management_aml-officers.c | 21 +- .../taler-exchange-httpd_management_partners.c | 29 ++- src/exchangedb/0002-partners.sql | 1 + src/exchangedb/pg_insert_aml_decision.c | 12 +- src/exchangedb/pg_insert_aml_decision.h | 9 +- src/exchangedb/pg_insert_aml_officer.c | 8 +- src/exchangedb/pg_insert_aml_officer.h | 9 +- src/exchangedb/pg_insert_partner.c | 19 +- src/include/taler_exchangedb_plugin.h | 13 +- src/lib/exchange_api_add_aml_decision.c | 2 +- src/lib/exchange_api_management_add_partner.c | 26 +-- .../exchange_api_management_update_aml_officer.c | 24 +-- 18 files changed, 418 insertions(+), 116 deletions(-) create mode 100644 src/exchange/taler-exchange-httpd_aml-decision.h (limited to 'src') diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 29596c381..ffcfc5e99 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -123,6 +123,7 @@ taler_exchange_wirewatch_LDADD = \ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd.c taler-exchange-httpd.h \ taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \ + taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \ taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \ taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \ taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \ @@ -139,12 +140,14 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \ taler-exchange-httpd_link.c taler-exchange-httpd_link.h \ taler-exchange-httpd_management.h \ + taler-exchange-httpd_management_aml-officers.c \ taler-exchange-httpd_management_auditors.c \ taler-exchange-httpd_management_auditors_AP_disable.c \ taler-exchange-httpd_management_denominations_HDP_revoke.c \ taler-exchange-httpd_management_drain.c \ taler-exchange-httpd_management_extensions.c \ taler-exchange-httpd_management_global_fees.c \ + taler-exchange-httpd_management_partners.c \ taler-exchange-httpd_management_post_keys.c \ taler-exchange-httpd_management_signkey_EP_revoke.c \ taler-exchange-httpd_management_wire_enable.c \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index def4fd4af..5501687fa 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-2023 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 @@ -30,6 +30,7 @@ #include "taler_kyclogic_lib.h" #include "taler_templating_lib.h" #include "taler_mhd_lib.h" +#include "taler-exchange-httpd_aml-decision.h" #include "taler-exchange-httpd_auditors.h" #include "taler-exchange-httpd_batch-deposit.h" #include "taler-exchange-httpd_batch-withdraw.h" @@ -322,6 +323,187 @@ handle_post_coins (struct TEH_RequestContext *rc, } +/** + * Signature of functions that handle operations + * authorized by AML officers. + * + * @param rc request context + * @param officer_pub the public key of the AML officer + * @param root uploaded JSON data + * @return MHD result code + */ +typedef MHD_RESULT +(*AmlOpPostHandler)(struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root); + + +/** + * Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub" + * EdDSA key of the officer and demultiplexes based on $OP. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_post_aml (struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[2]) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + AmlOpPostHandler handler; + + } h[] = { + { + .op = "decision", + .handler = &TEH_handler_post_aml_decision + }, + { + .op = NULL, + .handler = NULL + }, + }; + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + args[0]); + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &officer_pub, + root); + return r404 (rc->connection, + args[1]); +} + + +/** + * Signature of functions that handle operations + * authorized by AML officers. + * + * @param rc request context + * @param officer_pub the public key of the AML officer + * @param args remaining arguments + * @return MHD result code + */ +typedef MHD_RESULT +(*AmlOpGetHandler)(struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]); + + +/** + * Handle a "/aml/$OFFICER_PUB/$OP" GET request. Parses the "officer_pub" + * EdDSA key of the officer, checks the authentication signature, and + * demultiplexes based on $OP. + * + * @param rc request context + * @param args array of additional options + * @return MHD result code + */ +static MHD_RESULT +handle_get_aml (struct TEH_RequestContext *rc, + const char *const args[]) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + static const struct + { + /** + * Name of the operation (args[1]) + */ + const char *op; + + /** + * Function to call to perform the operation. + */ + AmlOpGetHandler handler; + + } h[] = { +#if FIXME_AML_GET_DECISIONS_NOT_IMPLEMENTED + { + .op = "decisions", + .handler = &TEH_handler_get_aml_decisions + }, + { + .op = "decision", + .handler = &TEH_handler_get_aml_decision + }, +#endif + { + .op = NULL, + .handler = NULL + }, + }; + + if (NULL == args[0]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + "argument missing"); + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, + args[0]); + } + if (NULL == args[1]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, + "AML GET operations must specify an operation identifier"); + } + if (1) // FIXME: check AML officer GET signature! + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, + NULL); + } + for (unsigned int i = 0; NULL != h[i].op; i++) + if (0 == strcmp (h[i].op, + args[1])) + return h[i].handler (rc, + &officer_pub, + &args[2]); + return r404 (rc->connection, + args[1]); +} + + /** * Signature of functions that handle operations on reserves. * @@ -890,6 +1072,8 @@ handle_post_management (struct TEH_RequestContext *rc, &exchange_pub, root); } + /* FIXME-STYLE: all of the following can likely be nicely combined + into an array-based dispatcher to deduplicate the logic... */ if (0 == strcmp (args[0], "keys")) { @@ -967,6 +1151,30 @@ handle_post_management (struct TEH_RequestContext *rc, return TEH_handler_management_post_drain (rc->connection, root); } + if (0 == strcmp (args[0], + "aml-officers")) + { + if (NULL != args[1]) + { + GNUNET_break_op (0); + return r404 (rc->connection, + "/management/aml-officers/*"); + } + return TEH_handler_management_aml_officers (rc->connection, + root); + } + if (0 == strcmp (args[0], + "partners")) + { + if (NULL != args[1]) + { + GNUNET_break_op (0); + return r404 (rc->connection, + "/management/partners/*"); + } + return TEH_handler_management_partners (rc->connection, + root); + } GNUNET_break_op (0); return r404 (rc->connection, "/management/*"); @@ -1289,6 +1497,22 @@ handle_mhd_request (void *cls, .nargs = 4, .nargs_is_upper_bound = true }, + /* AML endpoints */ + { + .url = "aml", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &handle_get_aml, + .nargs = 4, + .nargs_is_upper_bound = true + }, + { + .url = "aml", + .method = MHD_HTTP_METHOD_POST, + .handler.post = &handle_post_aml, + .nargs = 2 + }, + + /* mark end of list */ { .url = NULL diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 2be26f14d..7715444a3 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -213,7 +213,7 @@ struct TEH_RequestHandler * * @param rc context for the request * @param json uploaded JSON data - * @param args array of arguments, needs to be of length @e args_expected + * @param args array of arguments, needs to be of length @e nargs * @return MHD result code */ MHD_RESULT @@ -225,7 +225,7 @@ struct TEH_RequestHandler * Function to call to handle DELETE requests. * * @param rc context for the request - * @param args array of arguments, needs to be of length @e args_expected + * @param args array of arguments, needs to be of length @e nargs * @return MHD result code */ MHD_RESULT diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c index c93c10668..4589c8eb9 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -31,27 +31,26 @@ MHD_RESULT -TEH_handler_management_post_aml_decision ( - struct MHD_Connection *connection, +TEH_handler_post_aml_decision ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, const json_t *root) { + struct MHD_Connection *connection = rc->connection; const char *justification; struct GNUNET_TIME_Timestamp decision_time; struct TALER_Amount new_threshold; struct TALER_PaytoHashP h_payto; uint32_t new_state32; enum TALER_AmlDecisionState new_state; - struct TALER_AmlOfficerPublicKeyP officer_pub; struct TALER_AmlOfficerSignatureP officer_sig; struct GNUNET_JSON_Specification spec[] = { - // FIXME: officer_pub is in URL path, not in JSON body! - GNUNET_JSON_spec_fixed_auto ("officer_pub", - &officer_pub), GNUNET_JSON_spec_fixed_auto ("officer_sig", &officer_sig), GNUNET_JSON_spec_fixed_auto ("h_payto", &h_payto), TALER_JSON_spec_amount ("new_threshold", + TEH_currency, &new_threshold), GNUNET_JSON_spec_string ("justification", &justification), @@ -76,13 +75,13 @@ TEH_handler_management_post_aml_decision ( new_state = (enum TALER_AmlDecisionState) new_state32; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; if (GNUNET_OK != - TALER_exchange_aml_decision_verify (justification, - decision_time, - &new_threshold, - &h_payto, - new_state, - &officer_pub, - &officer_sig)) + TALER_officer_aml_decision_verify (justification, + decision_time, + &new_threshold, + &h_payto, + new_state, + &officer_pub, + &officer_sig)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error ( @@ -97,25 +96,25 @@ TEH_handler_management_post_aml_decision ( bool invalid_officer; do { - qs = TEH_plugin->add_aml_decision (TEH_plugin->cls, - justification, - decision_time, - &new_threshold, - &h_payto, - new_state, - &officer_pub, - &officer_sig, - &invalid_officer, - &last_date); + // FIXME: bound loop? + qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls, + &h_payto, + &new_threshold, + new_state, + decision_time, + justification, + &officer_pub, + &officer_sig, + &invalid_officer, + &last_date); } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "add aml_decision"); - return qs; + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "add aml_decision"); } if (invalid_officer) { @@ -127,7 +126,7 @@ TEH_handler_management_post_aml_decision ( } if (GNUNET_TIME_timestamp_cmp (last_date, >, - validity_start)) + decision_time)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error ( diff --git a/src/exchange/taler-exchange-httpd_aml-decision.h b/src/exchange/taler-exchange-httpd_aml-decision.h new file mode 100644 index 000000000..e6e355524 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_aml-decision.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + Copyright (C) 2023 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_aml_decision.h + * @brief Handle /aml/$OFFICER_PUB/decision requests + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_AML_DECISION_H +#define TALER_EXCHANGE_HTTPD_AML_DECISION_H + +#include +#include "taler-exchange-httpd.h" + + +/** + * Handle an "/aml/$OFFICER_PUB/decision" request. Parses the decision + * details, checks the signatures and if appropriately authorized excecutes + * the decision. + * + * @param rc request context + * @param officer_pub public key of the AML officer who made the request + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_post_aml_decision ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root); + + +#endif diff --git a/src/exchange/taler-exchange-httpd_management.h b/src/exchange/taler-exchange-httpd_management.h index a5a8b0e72..2fc1fe8db 100644 --- a/src/exchange/taler-exchange-httpd_management.h +++ b/src/exchange/taler-exchange-httpd_management.h @@ -174,6 +174,32 @@ TEH_handler_management_post_drain ( const json_t *root); +/** + * Handle a POST "/management/aml-officers" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_management_aml_officers ( + struct MHD_Connection *connection, + const json_t *root); + + +/** + * Handle a POST "/management/partners" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_management_partners ( + struct MHD_Connection *connection, + const json_t *root); + + /** * Initialize extension configuration handling. * diff --git a/src/exchange/taler-exchange-httpd_management_aml-officers.c b/src/exchange/taler-exchange-httpd_management_aml-officers.c index 139ccdb25..b82c18d47 100644 --- a/src/exchange/taler-exchange-httpd_management_aml-officers.c +++ b/src/exchange/taler-exchange-httpd_management_aml-officers.c @@ -92,14 +92,15 @@ TEH_handler_management_aml_officers ( struct GNUNET_TIME_Timestamp last_date; do { - qs = TEH_plugin->set_aml_officer (TEH_plugin->cls, - &officer_pub, - officer_name, - change_date, - is_active, - read_only, - &master_sig, - &last_date); + // FIXME: bound loop! + qs = TEH_plugin->insert_aml_officer (TEH_plugin->cls, + &officer_pub, + &master_sig, + officer_name, + is_active, + read_only, + change_date, + &last_date); } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { @@ -107,13 +108,13 @@ TEH_handler_management_aml_officers ( return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_STORE_FAILED, - "XXX"); + "insert_aml_officer"); } if (GNUNET_TIME_timestamp_cmp (last_date, >, change_date)) { - GNUNER_break_op (0); + GNUNET_break_op (0); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, diff --git a/src/exchange/taler-exchange-httpd_management_partners.c b/src/exchange/taler-exchange-httpd_management_partners.c index 5d860120a..1c0d4a9a1 100644 --- a/src/exchange/taler-exchange-httpd_management_partners.c +++ b/src/exchange/taler-exchange-httpd_management_partners.c @@ -51,13 +51,14 @@ TEH_handler_management_partners ( GNUNET_JSON_spec_string ("partner_base_url", &partner_base_url), TALER_JSON_spec_amount ("wad_fee", + TEH_currency, &wad_fee), GNUNET_JSON_spec_timestamp ("start_date", &start_date), GNUNET_JSON_spec_timestamp ("end_date", &start_date), - GNUNET_JSON_spec_time_rel ("wad_frequency", - &wad_frequency), + GNUNET_JSON_spec_relative_time ("wad_frequency", + &wad_frequency), GNUNET_JSON_spec_end () }; @@ -94,14 +95,14 @@ TEH_handler_management_partners ( { enum GNUNET_DB_QueryStatus qs; - qs = TEH_plugin->add_partner (TEH_plugin->cls, - &partner_pub, - start_date, - end_date, - wad_frequency, - &wad_fee, - partner_base_url, - &master_sig); + qs = TEH_plugin->insert_partner (TEH_plugin->cls, + &partner_pub, + start_date, + end_date, + wad_frequency, + &wad_fee, + partner_base_url, + &master_sig); if (qs < 0) { GNUNET_break (0); @@ -110,6 +111,14 @@ TEH_handler_management_partners ( TALER_EC_GENERIC_DB_STORE_FAILED, "add_partner"); } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* FIXME: check for idempotency! */ + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_EXCHANGE_MANAGEMENT_ADD_PARTNER_DATA_CONFLICT, + NULL); + } } return TALER_MHD_reply_static ( connection, diff --git a/src/exchangedb/0002-partners.sql b/src/exchangedb/0002-partners.sql index ff57f8fc1..c80f2d745 100644 --- a/src/exchangedb/0002-partners.sql +++ b/src/exchangedb/0002-partners.sql @@ -25,6 +25,7 @@ CREATE TABLE partners ,wad_fee_frac INT4 NOT NULL ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64) ,partner_base_url TEXT NOT NULL + ,PRIMARY KEY (partner_master_pub, start_date) ); COMMENT ON TABLE partners IS 'exchanges we do wad transfers to'; diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c index d046c87f9..421628f65 100644 --- a/src/exchangedb/pg_insert_aml_decision.c +++ b/src/exchangedb/pg_insert_aml_decision.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 Taler Systems SA 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 @@ -32,10 +32,12 @@ TEH_PG_insert_aml_decision ( const struct TALER_PaytoHashP *h_payto, const struct TALER_Amount *new_threshold, enum TALER_AmlDecisionState new_status, - struct GNUNET_TIME_Absolute decision_time, + struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_AmlOfficerSignatureP *decider_sig) + const struct TALER_AmlOfficerSignatureP *decider_sig, + bool *invalid_officer, + struct GNUNET_TIME_Timestamp *last_date) { struct PostgresClosure *pg = cls; uint32_t ns = (uint32_t) new_status; @@ -43,13 +45,15 @@ TEH_PG_insert_aml_decision ( GNUNET_PQ_query_param_auto_from_type (h_payto), TALER_PQ_query_param_amount (new_threshold), GNUNET_PQ_query_param_uint32 (&ns), - GNUNET_PQ_query_param_absolute_time (&decision_time), + GNUNET_PQ_query_param_timestamp (&decision_time), GNUNET_PQ_query_param_string (justification), GNUNET_PQ_query_param_auto_from_type (decider_pub), GNUNET_PQ_query_param_auto_from_type (decider_sig), GNUNET_PQ_query_param_end }; + // FIXME: set invalid_officer + // FIXME: set last_date! PREPARE (pg, "insert_aml_decision", "INSERT INTO aml_history " diff --git a/src/exchangedb/pg_insert_aml_decision.h b/src/exchangedb/pg_insert_aml_decision.h index 205c1c74b..94f648fb1 100644 --- a/src/exchangedb/pg_insert_aml_decision.h +++ b/src/exchangedb/pg_insert_aml_decision.h @@ -38,6 +38,9 @@ * @param justification human-readable text justifying the decision * @param decider_pub public key of the staff member * @param decider_sig signature of the staff member + * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now + * @param[out] last_date set to the previous decision time; + * the INSERT is not performed if @a last_date is not before @a decision_time * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -46,10 +49,12 @@ TEH_PG_insert_aml_decision ( const struct TALER_PaytoHashP *h_payto, const struct TALER_Amount *new_threshold, enum TALER_AmlDecisionState new_status, - struct GNUNET_TIME_Absolute decision_time, + struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_AmlOfficerSignatureP *decider_sig); + const struct TALER_AmlOfficerSignatureP *decider_sig, + bool *invalid_officer, + struct GNUNET_TIME_Timestamp *last_date); #endif diff --git a/src/exchangedb/pg_insert_aml_officer.c b/src/exchangedb/pg_insert_aml_officer.c index fc2cadef2..33e6c86f7 100644 --- a/src/exchangedb/pg_insert_aml_officer.c +++ b/src/exchangedb/pg_insert_aml_officer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 Taler Systems SA 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 @@ -34,7 +34,8 @@ TEH_PG_insert_aml_officer ( const char *decider_name, bool is_active, bool read_only, - struct GNUNET_TIME_Absolute last_change) + struct GNUNET_TIME_Timestamp last_change, + struct GNUNET_TIME_Timestamp *previous_change) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -43,10 +44,11 @@ TEH_PG_insert_aml_officer ( GNUNET_PQ_query_param_string (decider_name), GNUNET_PQ_query_param_bool (is_active), GNUNET_PQ_query_param_bool (read_only), - GNUNET_PQ_query_param_absolute_time (&last_change), + GNUNET_PQ_query_param_timestamp (&last_change), GNUNET_PQ_query_param_end }; + // FIXME: need to check for previous record! PREPARE (pg, "insert_aml_staff", "INSERT INTO aml_staff " diff --git a/src/exchangedb/pg_insert_aml_officer.h b/src/exchangedb/pg_insert_aml_officer.h index be034d9ff..3c6f5d82e 100644 --- a/src/exchangedb/pg_insert_aml_officer.h +++ b/src/exchangedb/pg_insert_aml_officer.h @@ -27,7 +27,10 @@ /** - * Insert AML staff record. + * Insert AML staff record. If the time given in + * @a last_change is before the previous change in the + * database, only @e previous_change is returned and + * no actual change is committed to the database. * * @param cls closure * @param decider_pub public key of the staff member @@ -36,6 +39,7 @@ * @param is_active true to enable, false to set as inactive * @param read_only true to set read-only access * @param last_change when was the change made effective + * @param[out] previous_change set to the time of the previous change * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -46,6 +50,7 @@ TEH_PG_insert_aml_officer ( const char *decider_name, bool is_active, bool read_only, - struct GNUNET_TIME_Absolute last_change); + struct GNUNET_TIME_Timestamp last_change, + struct GNUNET_TIME_Timestamp *previous_change); #endif diff --git a/src/exchangedb/pg_insert_partner.c b/src/exchangedb/pg_insert_partner.c index 567f37763..5abb2c910 100644 --- a/src/exchangedb/pg_insert_partner.c +++ b/src/exchangedb/pg_insert_partner.c @@ -28,13 +28,13 @@ enum GNUNET_DB_QueryStatus TEH_PG_insert_partner (void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp end_date, - struct GNUNET_TIME_Relative wad_frequency, - const struct TALER_Amount *wad_fee, - const char *partner_base_url, - const struct TALER_MasterSignatureP *master_sig) + const struct TALER_MasterPublicKeyP *master_pub, + struct GNUNET_TIME_Timestamp start_date, + struct GNUNET_TIME_Timestamp end_date, + struct GNUNET_TIME_Relative wad_frequency, + const struct TALER_Amount *wad_fee, + const char *partner_base_url, + const struct TALER_MasterSignatureP *master_sig) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -61,10 +61,9 @@ TEH_PG_insert_partner (void *cls, " ,master_sig" " ,partner_base_url" " ) VALUES " - " ($1, $2, $3, $4, $5, $6, $7, $8);"); + " ($1, $2, $3, $4, $5, $6, $7, $8)" + " ON CONFLICT DO NOTHING;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_partner", params); } - - diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 146321eab..d6bf798c5 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -6602,6 +6602,7 @@ struct TALER_EXCHANGEDB_Plugin * @param is_active true to enable, false to set as inactive * @param read_only true to set read-only access * @param last_change when was the change made effective + * @param[out] previous_change when was the previous change made * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -6612,7 +6613,8 @@ struct TALER_EXCHANGEDB_Plugin const char *decider_name, bool is_active, bool read_only, - struct GNUNET_TIME_Absolute last_change); + struct GNUNET_TIME_Timestamp last_change, + struct GNUNET_TIME_Timestamp *previous_change); /** @@ -6727,6 +6729,9 @@ struct TALER_EXCHANGEDB_Plugin * @param justification human-readable text justifying the decision * @param decider_pub public key of the staff member * @param decider_sig signature of the staff member + * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now + * @param[out] last_date set to the previous decision time; + * the INSERT is not performed if @a last_date is not before @a decision_time * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -6735,10 +6740,12 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_PaytoHashP *h_payto, const struct TALER_Amount *new_threshold, enum TALER_AmlDecisionState new_status, - struct GNUNET_TIME_Absolute decision_time, + struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_AmlOfficerSignatureP *decider_sig); + const struct TALER_AmlOfficerSignatureP *decider_sig, + bool *invalid_officer, + struct GNUNET_TIME_Timestamp *last_date); }; diff --git a/src/lib/exchange_api_add_aml_decision.c b/src/lib/exchange_api_add_aml_decision.c index d4b0d1250..c6283532b 100644 --- a/src/lib/exchange_api_add_aml_decision.c +++ b/src/lib/exchange_api_add_aml_decision.c @@ -167,7 +167,7 @@ TALER_EXCHANGE_add_aml_decision ( sizeof (opus)); *end = '\0'; GNUNET_asprintf (&path, - "aml-decision/%s", + "aml/%s/decision", opus); wh->url = TALER_url_join (url, path, diff --git a/src/lib/exchange_api_management_add_partner.c b/src/lib/exchange_api_management_add_partner.c index 264fd664e..75fb8aa6f 100644 --- a/src/lib/exchange_api_management_add_partner.c +++ b/src/lib/exchange_api_management_add_partner.c @@ -66,7 +66,7 @@ struct TALER_EXCHANGE_ManagementAddPartner /** * Function called when we're done processing the - * HTTP POST /aml-decision/$OFFICER_PUB request. + * HTTP POST /management/partners request. * * @param cls the `struct TALER_EXCHANGE_ManagementAddPartner *` * @param response_code HTTP response code, 0 on error @@ -145,25 +145,9 @@ TALER_EXCHANGE_management_add_partner ( wh->cb = cb; wh->cb_cls = cb_cls; wh->ctx = ctx; - { - char *path; - char opus[sizeof (*partner_pub) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - partner_pub, - sizeof (*partner_pub), - opus, - sizeof (opus)); - *end = '\0'; - GNUNET_asprintf (&path, - "management/partners/%s", - opus); - wh->url = TALER_url_join (url, - path, - NULL); - GNUNET_free (path); - } + wh->url = TALER_url_join (url, + "management/partners", + NULL); if (NULL == wh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -180,6 +164,8 @@ TALER_EXCHANGE_management_add_partner ( end_date), GNUNET_JSON_pack_time_rel ("wad_frequency", wad_frequency), + GNUNET_JSON_pack_data_auto ("partner_pub", + &partner_pub), GNUNET_JSON_pack_data_auto ("master_sig", &master_sig), TALER_JSON_pack_amount ("wad_fee", diff --git a/src/lib/exchange_api_management_update_aml_officer.c b/src/lib/exchange_api_management_update_aml_officer.c index 96ff5a4c7..6e1669465 100644 --- a/src/lib/exchange_api_management_update_aml_officer.c +++ b/src/lib/exchange_api_management_update_aml_officer.c @@ -144,25 +144,9 @@ TALER_EXCHANGE_management_update_aml_officer ( wh->cb = cb; wh->cb_cls = cb_cls; wh->ctx = ctx; - { - char *path; - char opus[sizeof (*officer_pub) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - officer_pub, - sizeof (*officer_pub), - opus, - sizeof (opus)); - *end = '\0'; - GNUNET_asprintf (&path, - "management/aml-officers/%s", - opus); - wh->url = TALER_url_join (url, - path, - NULL); - GNUNET_free (path); - } + wh->url = TALER_url_join (url, + "management/aml-officers", + NULL); if (NULL == wh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -173,6 +157,8 @@ TALER_EXCHANGE_management_update_aml_officer ( body = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("officer_name", officer_name), + GNUNET_JSON_pack_data_auto ("officer_pub", + officer_pub), GNUNET_JSON_pack_data_auto ("master_sig", master_sig), GNUNET_JSON_pack_bool ("is_active", -- cgit v1.2.3 From f8bfc4dc9d9dcb69ccdd95258aea53dabf997246 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 22 Jan 2023 15:13:34 +0100 Subject: address DB FIXMEs for AML --- src/exchangedb/0003-aml_history.sql | 2 +- src/exchangedb/exchange_do_insert_aml_decision.sql | 102 +++++++++++++++++++++ src/exchangedb/exchange_do_insert_aml_officer.sql | 74 +++++++++++++++ src/exchangedb/pg_insert_aml_decision.c | 32 +++---- src/exchangedb/pg_insert_aml_decision.h | 2 +- src/exchangedb/pg_insert_aml_officer.c | 26 +++--- src/exchangedb/pg_persist_policy_details.c | 10 +- src/exchangedb/procedures.sql.in | 2 + 8 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 src/exchangedb/exchange_do_insert_aml_decision.sql create mode 100644 src/exchangedb/exchange_do_insert_aml_officer.sql (limited to 'src') diff --git a/src/exchangedb/0003-aml_history.sql b/src/exchangedb/0003-aml_history.sql index c2ab532da..1c737265b 100644 --- a/src/exchangedb/0003-aml_history.sql +++ b/src/exchangedb/0003-aml_history.sql @@ -110,7 +110,7 @@ BEGIN EXECUTE FORMAT ( 'CREATE INDEX ' || table_name || '_main_index ' 'ON ' || table_name || ' ' - '(h_payto ASC, decision_time ASC);' + '(h_payto, decision_time DESC);' ); END $$; diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql new file mode 100644 index 000000000..480736afa --- /dev/null +++ b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -0,0 +1,102 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2023 Taler Systems SA +-- +-- 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 +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see +-- + +CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision( + IN in_h_payto BYTEA, + IN in_new_threshold_val INT8, + IN in_new_threshold_frac INT4, + IN in_new_status INT4, + IN in_decision_time INT8, + IN in_justification VARCHAR, + IN in_decider_pub BYTEA, + IN in_decider_sig BYTEA, + OUT out_invalid_officer BOOLEAN, + OUT out_last_date INT8) +LANGUAGE plpgsql +AS $$ + +-- Check officer is eligible to make decisions. +PERFORM + FROM exchange.aml_staff + WHERE decider_pub=in_decider_pub + AND is_active + AND NOT read_only; +IF NOT FOUND +THEN + out_invalid_officer=TRUE; + out_last_date=0; + RETURN; +END IF; +out_invalid_officer=FALSE; + +-- Check no more recent decision exists. +SELECT decision_time + INTO out_last_date + FROM exchange.aml_history + WHERE h_payto=in_h_payto + ORDER BY decision_time DESC; +IF FOUND +THEN + IF out_last_date >= in_decision_time + THEN + -- Refuse to insert older decision. + RETURN; + END IF; + UPDATE exchange.aml_status + SET threshold_val=in_threshold_val + ,threshold_frac=in_threshold_frac + ,status=in_new_status + WHERE h_payto=in_h_payto; + ASSERT FOUND, 'cannot have AML decision history but no AML status'; +ELSE + out_last_date = 0; + INSERT INTO exchange.aml_status + (h_payto + ,threshold_val + ,threshold_frac + ,status) + VALUES + (in_h_payto + ,in_threshold_val + ,in_threshold_frac + ,in_new_status); +END IF; + + +INSERT INTO exchange.aml_history + (h_payto + ,new_threshold_val + ,new_threshold_frac + ,new_status + ,decision_time + ,justification + ,decider_pub + ,decider_sig + ) VALUES + (in_h_payto + ,in_new_threshold_val + ,in_new_threshold_frac + ,in_new_status + ,in_decision_time + ,in_justification + ,in_decider_pub + ,in_decider_sig); + +END $$; + + +COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, INT8, INT4, INT4, INT8, VARCHAR, BYTEA, BYTEA) + IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table'; diff --git a/src/exchangedb/exchange_do_insert_aml_officer.sql b/src/exchangedb/exchange_do_insert_aml_officer.sql new file mode 100644 index 000000000..72f813e89 --- /dev/null +++ b/src/exchangedb/exchange_do_insert_aml_officer.sql @@ -0,0 +1,74 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2023 Taler Systems SA +-- +-- 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 +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see +-- + +CREATE OR REPLACE FUNCTION exchange_do_insert_aml_officer( + IN in_decider_pub BYTEA, + IN in_master_sig BYTEA, + IN in_decider_name VARCHAR, + IN in_is_active BOOLEAN, + IN in_read_only BOOLEAN, + IN in_last_change INT8, + OUT out_last_change INT8) +LANGUAGE plpgsql +AS $$ + +INSERT INTO exchange.aml_staff + (decider_pub + ,master_sig + ,decider_name + ,is_active + ,read_only + ,last_change + ) VALUES + (in_decider_pub + ,in_master_sig + ,in_decider_name + ,in_is_active + ,in_read_only + ,in_last_change) + ON CONFLICT DO NOTHING; +IF FOUND +THEN + out_last_change=0; + RETURN; +END IF; + +-- Check update is most recent... +SELECT last_change + INTO out_last_change + FROM exchange.aml_staff + WHERE decider_pub=in_decider_pub; +ASSERT FOUND, 'cannot have INSERT conflict but no AML staff record'; + +IF out_last_change >= in_last_change +THEN + -- Refuse to insert older status + RETURN; +END IF; + +-- We are more recent, update existing record. +UPDATE exchange.aml_staff + SET master_sig=in_master_sig + ,decider_name=in_decider_name + ,is_active=in_is_active + ,read_only=in_read_only + ,last_change=in_last_change + WHERE decider_pub=in_decider_pub; +END $$; + + +COMMENT ON FUNCTION exchange_do_insert_aml_officer(BYTEA, BYTEA, VARCHAR, BOOL, BOOL, INT8) + IS 'Inserts or updates AML staff record, making sure the update is more recent than the previous change'; diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c index 421628f65..85570ed85 100644 --- a/src/exchangedb/pg_insert_aml_decision.c +++ b/src/exchangedb/pg_insert_aml_decision.c @@ -51,23 +51,23 @@ TEH_PG_insert_aml_decision ( GNUNET_PQ_query_param_auto_from_type (decider_sig), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_invalid_officer", + invalid_officer), + GNUNET_PQ_result_spec_timestamp ("out_last_date", + last_date), + GNUNET_PQ_result_spec_end + }; - // FIXME: set invalid_officer - // FIXME: set last_date! PREPARE (pg, - "insert_aml_decision", - "INSERT INTO aml_history " - "(h_payto" - ",new_threshold_val" - ",new_threshold_frac" - ",new_status" - ",decision_time" - ",justification" - ",decider_pub" - ",decider_sig" - ") VALUES " + "do_insert_aml_decision", + "SELECT" + " out_invalid_officer" + ",out_last_date" + " FROM exchange_do_insert_aml_decision" "($1, $2, $3, $4, $5, $6, $7, $8);"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_aml_decision", - params); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "do_insert_aml_decision", + params, + rs); } diff --git a/src/exchangedb/pg_insert_aml_decision.h b/src/exchangedb/pg_insert_aml_decision.h index 94f648fb1..b539945a7 100644 --- a/src/exchangedb/pg_insert_aml_decision.h +++ b/src/exchangedb/pg_insert_aml_decision.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 Taler Systems SA 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 diff --git a/src/exchangedb/pg_insert_aml_officer.c b/src/exchangedb/pg_insert_aml_officer.c index 33e6c86f7..c1f635a64 100644 --- a/src/exchangedb/pg_insert_aml_officer.c +++ b/src/exchangedb/pg_insert_aml_officer.c @@ -47,20 +47,20 @@ TEH_PG_insert_aml_officer ( GNUNET_PQ_query_param_timestamp (&last_change), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("out_last_change", + previous_change), + GNUNET_PQ_result_spec_end + }; - // FIXME: need to check for previous record! PREPARE (pg, - "insert_aml_staff", - "INSERT INTO aml_staff " - "(decider_pub" - ",master_sig" - ",decider_name" - ",is_active" - ",read_only" - ",last_change" - ") VALUES " + "do_insert_aml_staff", + "SELECT" + " out_last_change" + " FROM exchange_do_insert_aml_officer" "($1, $2, $3, $4, $5, $6);"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_aml_staff", - params); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "do_insert_aml_staff", + params, + rs); } diff --git a/src/exchangedb/pg_persist_policy_details.c b/src/exchangedb/pg_persist_policy_details.c index 2b7787878..3bc7afa98 100644 --- a/src/exchangedb/pg_persist_policy_details.c +++ b/src/exchangedb/pg_persist_policy_details.c @@ -59,7 +59,15 @@ TEH_PG_persist_policy_details ( GNUNET_PQ_result_spec_end }; - // FIXME: prepare missing!!?! + PREPARE (pg, + "call_insert_or_update_policy_details", + "SELECT" + " out_policy_details_serial_id AS policy_details_serial_id" + ",out_accumulated_total_val AS accumulated_total_val" + ",out_accumulated_total_frac AS accumulated_total_frac" + ",out_fulfillment_state AS fulfillment_state" + " FROM exchange_do_insert_or_update_policy_details" + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "call_insert_or_update_policy_details", params, diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in index 194830248..73b75af29 100644 --- a/src/exchangedb/procedures.sql.in +++ b/src/exchangedb/procedures.sql.in @@ -37,6 +37,8 @@ SET search_path TO exchange; #include "exchange_do_reserve_open_deposit.sql" #include "exchange_do_reserve_open.sql" #include "exchange_do_insert_or_update_policy_details.sql" +#include "exchange_do_insert_aml_decision.sql" +#include "exchange_do_insert_aml_officer.sql" #include "exchange_do_batch_reserves_in_insert.sql" #include "exchange_do_batch_reserves_update.sql" #include "exchange_do_batch2_reserves_in_insert.sql" -- cgit v1.2.3 From f221db1c035cf9e596300e79fe928c9edc5af2d0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 22 Jan 2023 15:24:45 +0100 Subject: -address FIXMEs --- src/exchange/taler-exchange-httpd_aml-decision.c | 14 +++++++++++--- .../taler-exchange-httpd_management_aml-officers.c | 10 +++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c index 4589c8eb9..ae2667c1d 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -30,6 +30,12 @@ #include "taler-exchange-httpd_responses.h" +/** + * How often do we try the DB operation at most? + */ +#define MAX_RETRIES 10 + + MHD_RESULT TEH_handler_post_aml_decision ( struct TEH_RequestContext *rc, @@ -80,7 +86,7 @@ TEH_handler_post_aml_decision ( &new_threshold, &h_payto, new_state, - &officer_pub, + officer_pub, &officer_sig)) { GNUNET_break_op (0); @@ -94,19 +100,21 @@ TEH_handler_post_aml_decision ( enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Timestamp last_date; bool invalid_officer; + unsigned int retries_left = MAX_RETRIES; do { - // FIXME: bound loop? qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls, &h_payto, &new_threshold, new_state, decision_time, justification, - &officer_pub, + officer_pub, &officer_sig, &invalid_officer, &last_date); + if (0 == --retries_left) + break; } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { diff --git a/src/exchange/taler-exchange-httpd_management_aml-officers.c b/src/exchange/taler-exchange-httpd_management_aml-officers.c index b82c18d47..abc7c3d84 100644 --- a/src/exchange/taler-exchange-httpd_management_aml-officers.c +++ b/src/exchange/taler-exchange-httpd_management_aml-officers.c @@ -31,6 +31,12 @@ #include "taler-exchange-httpd_responses.h" +/** + * How often do we try the DB operation at most? + */ +#define MAX_RETRIES 10 + + MHD_RESULT TEH_handler_management_aml_officers ( struct MHD_Connection *connection, @@ -90,9 +96,9 @@ TEH_handler_management_aml_officers ( { enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Timestamp last_date; + unsigned int retries_left = MAX_RETRIES; do { - // FIXME: bound loop! qs = TEH_plugin->insert_aml_officer (TEH_plugin->cls, &officer_pub, &master_sig, @@ -101,6 +107,8 @@ TEH_handler_management_aml_officers ( read_only, change_date, &last_date); + if (0 == --retries_left) + break; } while (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (qs < 0) { -- cgit v1.2.3 From d53dd753e147420c4553724de5a1b653b19dfda2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 22 Jan 2023 15:41:08 +0100 Subject: -typos --- src/exchange/taler-exchange-httpd_aml-decision.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/exchange/taler-exchange-httpd_aml-decision.h b/src/exchange/taler-exchange-httpd_aml-decision.h index e6e355524..8dd3bb3fc 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.h +++ b/src/exchange/taler-exchange-httpd_aml-decision.h @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see */ /** - * @file taler-exchange-httpd_aml_decision.h + * @file taler-exchange-httpd_aml-decision.h * @brief Handle /aml/$OFFICER_PUB/decision requests * @author Christian Grothoff */ @@ -27,7 +27,7 @@ /** * Handle an "/aml/$OFFICER_PUB/decision" request. Parses the decision - * details, checks the signatures and if appropriately authorized excecutes + * details, checks the signatures and if appropriately authorized executes * the decision. * * @param rc request context -- cgit v1.2.3 From c2eee251c2eda2ce544a808d77a9a5615a2e7c34 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 22 Jan 2023 19:40:47 +0100 Subject: add new subcommands for AML staff management and partner exchanges --- src/exchange-tools/taler-exchange-offline.c | 617 +++++++++++++++++++++++++++- src/include/taler_exchange_service.h | 2 +- 2 files changed, 617 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c index eb64a6c9d..660864b77 100644 --- a/src/exchange-tools/taler-exchange-offline.c +++ b/src/exchange-tools/taler-exchange-offline.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020, 2021, 2022 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA 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 @@ -112,6 +112,16 @@ */ #define OP_DRAIN_PROFITS "exchange-drain-profits-0" +/** + * Setup AML staff. + */ +#define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0" + +/** + * Setup partner exchange for wad transfers. + */ +#define OP_ADD_PARTNER "exchange-add-partner-0" + /** * Our private key, initialized in #load_offline_key(). */ @@ -498,6 +508,62 @@ struct UploadExtensionsRequest }; +/** + * Data structure for AML staff requests. + */ +struct AmlStaffRequest +{ + + /** + * Kept in a DLL. + */ + struct AmlStaffRequest *next; + + /** + * Kept in a DLL. + */ + struct AmlStaffRequest *prev; + + /** + * Operation handle. + */ + struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h; + + /** + * Array index of the associated command. + */ + size_t idx; +}; + + +/** + * Data structure for partner add requests. + */ +struct PartnerAddRequest +{ + + /** + * Kept in a DLL. + */ + struct PartnerAddRequest *next; + + /** + * Kept in a DLL. + */ + struct PartnerAddRequest *prev; + + /** + * Operation handle. + */ + struct TALER_EXCHANGE_ManagementAddPartner *h; + + /** + * Array index of the associated command. + */ + size_t idx; +}; + + /** * Next work item to perform. */ @@ -508,6 +574,27 @@ static struct GNUNET_SCHEDULER_Task *nxt; */ static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh; + +/** + * Active AML staff change requests. + */ +static struct AmlStaffRequest *asr_head; + +/** + * Active AML staff change requests. + */ +static struct AmlStaffRequest *asr_tail; + +/** + * Active partner add requests. + */ +static struct PartnerAddRequest *par_head; + +/** + * Active partner add requests. + */ +static struct PartnerAddRequest *par_tail; + /** * Active denomiantion revocation requests. */ @@ -629,6 +716,36 @@ do_shutdown (void *cls) { (void) cls; + { + struct AmlStaffRequest *asr; + + while (NULL != (asr = asr_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Aborting incomplete AML staff update #%u\n", + (unsigned int) asr->idx); + TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h); + GNUNET_CONTAINER_DLL_remove (asr_head, + asr_tail, + asr); + GNUNET_free (asr); + } + } + { + struct PartnerAddRequest *par; + + while (NULL != (par = par_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Aborting incomplete partner add request #%u\n", + (unsigned int) par->idx); + TALER_EXCHANGE_management_add_partner_cancel (par->h); + GNUNET_CONTAINER_DLL_remove (par_head, + par_tail, + par); + GNUNET_free (par); + } + } { struct DenomRevocationRequest *drr; @@ -842,6 +959,8 @@ static void test_shutdown (void) { if ( (NULL == drr_head) && + (NULL == par_head) && + (NULL == asr_head) && (NULL == srr_head) && (NULL == aar_head) && (NULL == adr_head) && @@ -2214,6 +2333,221 @@ upload_extensions (const char *exchange_url, } +/** + * Function called with information about the add partner operation. + * + * @param cls closure with a `struct PartnerAddRequest` + * @param hr HTTP response data + */ +static void +add_partner_cb ( + void *cls, + const struct TALER_EXCHANGE_HttpResponse *hr) +{ + struct PartnerAddRequest *par = cls; + + if (MHD_HTTP_NO_CONTENT != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Upload failed for command %u with status %u: %s (%s)\n", + (unsigned int) par->idx, + hr->http_status, + TALER_ErrorCode_get_hint (hr->ec), + hr->hint); + global_ret = EXIT_FAILURE; + } + GNUNET_CONTAINER_DLL_remove (par_head, + par_tail, + par); + GNUNET_free (par); + test_shutdown (); +} + + +/** + * Add partner action. + * + * @param exchange_url base URL of the exchange + * @param idx index of the operation we are performing (for logging) + * @param value arguments for add partner + */ +static void +add_partner (const char *exchange_url, + size_t idx, + const json_t *value) +{ + struct TALER_MasterPublicKeyP partner_pub; + struct GNUNET_TIME_Timestamp start_date; + struct GNUNET_TIME_Timestamp end_date; + struct GNUNET_TIME_Relative wad_frequency; + struct TALER_Amount wad_fee; + const char *partner_base_url; + struct TALER_MasterSignatureP master_sig; + struct PartnerAddRequest *par; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("partner_pub", + &partner_pub), + TALER_JSON_spec_amount ("wad_fee", + currency, + &wad_fee), + GNUNET_JSON_spec_relative_time ("wad_frequency", + &wad_frequency), + GNUNET_JSON_spec_timestamp ("start_date", + &start_date), + GNUNET_JSON_spec_timestamp ("end_date", + &end_date), + GNUNET_JSON_spec_string ("partner_base_url", + &partner_base_url), + GNUNET_JSON_spec_fixed_auto ("master_sig", + &master_sig), + GNUNET_JSON_spec_end () + }; + const char *err_name; + unsigned int err_line; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + &err_name, + &err_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid input to add partner: %s#%u at %u (skipping)\n", + err_name, + err_line, + (unsigned int) idx); + json_dumpf (value, + stderr, + JSON_INDENT (2)); + global_ret = EXIT_FAILURE; + test_shutdown (); + return; + } + par = GNUNET_new (struct PartnerAddRequest); + par->idx = idx; + par->h = + TALER_EXCHANGE_management_add_partner (ctx, + exchange_url, + &partner_pub, + start_date, + end_date, + wad_frequency, + &wad_fee, + partner_base_url, + &master_sig, + &add_partner_cb, + par); + GNUNET_CONTAINER_DLL_insert (par_head, + par_tail, + par); +} + + +/** + * Function called with information about the AML officer update operation. + * + * @param cls closure with a `struct AmlStaffRequest` + * @param hr HTTP response data + */ +static void +update_aml_officer_cb ( + void *cls, + const struct TALER_EXCHANGE_HttpResponse *hr) +{ + struct AmlStaffRequest *asr = cls; + + if (MHD_HTTP_NO_CONTENT != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Upload failed for command %u with status %u: %s (%s)\n", + (unsigned int) asr->idx, + hr->http_status, + TALER_ErrorCode_get_hint (hr->ec), + hr->hint); + global_ret = EXIT_FAILURE; + } + GNUNET_CONTAINER_DLL_remove (asr_head, + asr_tail, + asr); + GNUNET_free (asr); + test_shutdown (); +} + + +/** + * Upload AML staff action. + * + * @param exchange_url base URL of the exchange + * @param idx index of the operation we are performing (for logging) + * @param value arguments for AML staff change + */ +static void +update_aml_staff (const char *exchange_url, + size_t idx, + const json_t *value) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + const char *officer_name; + struct GNUNET_TIME_Timestamp change_date; + bool is_active; + bool read_only; + struct TALER_MasterSignatureP master_sig; + struct AmlStaffRequest *asr; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("officer_pub", + &officer_pub), + GNUNET_JSON_spec_timestamp ("change_date", + &change_date), + GNUNET_JSON_spec_bool ("is_active", + &is_active), + GNUNET_JSON_spec_bool ("read_only", + &read_only), + GNUNET_JSON_spec_string ("officer_name", + &officer_name), + GNUNET_JSON_spec_fixed_auto ("master_sig", + &master_sig), + GNUNET_JSON_spec_end () + }; + const char *err_name; + unsigned int err_line; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + &err_name, + &err_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid input to AML staff update: %s#%u at %u (skipping)\n", + err_name, + err_line, + (unsigned int) idx); + json_dumpf (value, + stderr, + JSON_INDENT (2)); + global_ret = EXIT_FAILURE; + test_shutdown (); + return; + } + asr = GNUNET_new (struct AmlStaffRequest); + asr->idx = idx; + asr->h = + TALER_EXCHANGE_management_update_aml_officer (ctx, + exchange_url, + &officer_pub, + officer_name, + change_date, + is_active, + read_only, + &master_sig, + &update_aml_officer_cb, + asr); + GNUNET_CONTAINER_DLL_insert (asr_head, + asr_tail, + asr); +} + + /** * Perform uploads based on the JSON in #out. * @@ -2267,6 +2601,14 @@ trigger_upload (const char *exchange_url) .key = OP_EXTENSIONS, .cb = &upload_extensions }, + { + .key = OP_UPDATE_AML_STAFF, + .cb = &update_aml_staff + }, + { + .key = OP_ADD_PARTNER, + .cb = &add_partner + }, /* array termination */ { .key = NULL @@ -3040,6 +3382,261 @@ do_drain (char *const *args) } +/** + * Add partner. + * + * @param args the array of command-line arguments to process next; + * args[0] must be the partner's master public key, args[1] the partner's + * API base URL, args[2] the wad fee, args[3] the wad frequency, and + * args[4] the year (including possibly 'now') + */ +static void +do_add_partner (char *const *args) +{ + struct TALER_MasterPublicKeyP partner_pub; + struct GNUNET_TIME_Timestamp start_date; + struct GNUNET_TIME_Timestamp end_date; + struct GNUNET_TIME_Relative wad_frequency; + struct TALER_Amount wad_fee; + const char *partner_base_url; + struct TALER_MasterSignatureP master_sig; + char dummy; + unsigned int year; + + if (NULL != in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Downloaded data was not consumed, not adding partner\n"); + test_shutdown (); + global_ret = EXIT_FAILURE; + return; + } + if ( (NULL == args[0]) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &partner_pub, + sizeof (partner_pub))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the partner master public key as first argument for this subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (NULL == args[1]) || + (0 != strncmp ("http", + args[1], + strlen ("http"))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + partner_base_url = args[1]; + if ( (NULL == args[2]) || + (GNUNET_OK != + TALER_string_to_amount (args[2], + &wad_fee)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid amount `%s' specified for wad fee of partner\n", + args[2]); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (NULL == args[3]) || + (GNUNET_OK != + GNUNET_STRINGS_fancy_time_to_relative (args[3], + &wad_frequency)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid wad frequency `%s' specified for add partner\n", + args[3]); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (NULL == args[4]) || + ( (1 != sscanf (args[4], + "%u%c", + &year, + &dummy)) && + (0 != strcasecmp ("now", + args[4])) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid year `%s' specified for add partner\n", + args[4]); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if (0 == strcasecmp ("now", + args[4])) + year = GNUNET_TIME_get_current_year (); + start_date = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_year_to_time (year)); + end_date = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_year_to_time (year + 1)); + + if (GNUNET_OK != + load_offline_key (GNUNET_NO)) + return; + TALER_exchange_offline_partner_details_sign (&partner_pub, + start_date, + end_date, + wad_frequency, + &wad_fee, + partner_base_url, + &master_priv, + &master_sig); + output_operation (OP_ADD_PARTNER, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("partner_base_url", + partner_base_url), + GNUNET_JSON_pack_time_rel ("wad_frequency", + wad_frequency), + GNUNET_JSON_pack_timestamp ("start_date", + start_date), + GNUNET_JSON_pack_timestamp ("end_date", + end_date), + GNUNET_JSON_pack_data_auto ("partner_pub", + &partner_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &master_sig))); + next (args + 5); +} + + +/** + * Enable or disable AML staff. + * + * @param is_active true to enable, false to disable + * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only) + */ +static void +do_set_aml_staff (bool is_active, + char *const *args) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + const char *officer_name; + bool read_only; + struct TALER_MasterSignatureP master_sig; + struct GNUNET_TIME_Timestamp now + = GNUNET_TIME_timestamp_get (); + + if (NULL != in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Downloaded data was not consumed, not updating AML staff status\n"); + test_shutdown (); + global_ret = EXIT_FAILURE; + return; + } + if ( (NULL == args[0]) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the AML officer's public key as first argument for this subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if (NULL == args[1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + officer_name = args[1]; + if (is_active) + { + if ( (NULL == args[2]) || + ( (0 != strcmp (args[2], + "ro")) && + (0 != strcmp (args[2], + "rw")) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", + args[2]); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + read_only = (0 == strcmp (args[2], + "ro")); + } + else + { + read_only = true; + } + if (GNUNET_OK != + load_offline_key (GNUNET_NO)) + return; + TALER_exchange_offline_aml_officer_status_sign (&officer_pub, + officer_name, + now, + is_active, + read_only, + &master_priv, + &master_sig); + output_operation (OP_UPDATE_AML_STAFF, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("officer_name", + officer_name), + GNUNET_JSON_pack_timestamp ("change_date", + now), + GNUNET_JSON_pack_bool ("is_active", + is_active), + GNUNET_JSON_pack_bool ("read_only", + read_only), + GNUNET_JSON_pack_data_auto ("officer_pub", + &officer_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &master_sig))); + next (args + (is_active ? 3 : 2)); +} + + +/** + * Disable AML staff. + * + * @param args the array of command-line arguments to process next; + * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) + */ +static void +disable_aml_staff (char *const *args) +{ + do_set_aml_staff (false, + args); +} + + +/** + * Enable AML staff. + * + * @param args the array of command-line arguments to process next; + * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) + */ +static void +enable_aml_staff (char *const *args) +{ + do_set_aml_staff (true, + args); +} + + /** * Function called with information about future keys. Dumps the JSON output * (on success), either into an internal buffer or to stdout (depending on @@ -4475,6 +5072,24 @@ work (void *cls) "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)", .cb = &do_drain }, + { + .name = "add-partner", + .help = + "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)", + .cb = &do_add_partner + }, + { + .name = "aml-enable", + .help = + "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", + .cb = &enable_aml_staff + }, + { + .name = "aml-disable", + .help = + "disable AML staff member (staff member public key and legal name must be given as arguments)", + .cb = &disable_aml_staff + }, { .name = "upload", .help = diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index ff1698cca..bc98e5329 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -4410,7 +4410,7 @@ TALER_EXCHANGE_management_add_partner ( /** - * Cancel #TALER_EXCHANGE_management_update_aml_officer() operation. + * Cancel #TALER_EXCHANGE_management_add_partner() operation. * * @param rh handle of the operation to cancel */ -- cgit v1.2.3 From a273b176da448cd27374acb94feee22c22dd8527 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 22 Jan 2023 21:51:46 +0100 Subject: -towards storing KYC attribute data --- src/exchange/taler-exchange-httpd_kyc-proof.c | 3 + src/include/taler_kyclogic_plugin.h | 2 + src/kyclogic/Makefile.am | 1 + src/kyclogic/kyclogic-oauth2.conf | 8 +++ src/kyclogic/plugin_kyclogic_kycaid.c | 1 + src/kyclogic/plugin_kyclogic_oauth2.c | 87 +++++++++++++++++++++++++++ src/kyclogic/plugin_kyclogic_persona.c | 2 + src/kyclogic/taler-exchange-kyc-tester.c | 6 ++ 8 files changed, 110 insertions(+) (limited to 'src') diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index d37164987..1904c4acb 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.c +++ b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -169,6 +169,7 @@ TEH_kyc_proof_cleanup (void) * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown * @param expiration until when is the KYC check valid + * @param attributes user attributes returned by the provider * @param http_status HTTP status code of @a response * @param[in] response to return to the HTTP client */ @@ -179,6 +180,7 @@ proof_cb ( const char *provider_user_id, const char *provider_legitimization_id, struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, unsigned int http_status, struct MHD_Response *response) { @@ -194,6 +196,7 @@ proof_cb ( { enum GNUNET_DB_QueryStatus qs; + // FIXME: also store 'attributes' in DB! qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls, kpc->process_row, kpc->provider_section, diff --git a/src/include/taler_kyclogic_plugin.h b/src/include/taler_kyclogic_plugin.h index c2266e1ae..1782af917 100644 --- a/src/include/taler_kyclogic_plugin.h +++ b/src/include/taler_kyclogic_plugin.h @@ -158,6 +158,7 @@ typedef void * @param status KYC status * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown + * @param attributes user attributes returned by the provider * @param expiration until when is the KYC check valid * @param http_status HTTP status code of @a response * @param[in] response to return to the HTTP client @@ -169,6 +170,7 @@ typedef void const char *provider_user_id, const char *provider_legitimization_id, struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, unsigned int http_status, struct MHD_Response *response); diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am index c45424178..858331f39 100644 --- a/src/kyclogic/Makefile.am +++ b/src/kyclogic/Makefile.am @@ -79,6 +79,7 @@ libtaler_plugin_kyclogic_oauth2_la_LIBADD = \ $(LTLIBINTL) libtaler_plugin_kyclogic_oauth2_la_LDFLAGS = \ $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/templating/libtalertemplating.la \ $(top_builddir)/src/mhd/libtalermhd.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ diff --git a/src/kyclogic/kyclogic-oauth2.conf b/src/kyclogic/kyclogic-oauth2.conf index 7ccf81c0d..d3df585d8 100644 --- a/src/kyclogic/kyclogic-oauth2.conf +++ b/src/kyclogic/kyclogic-oauth2.conf @@ -25,3 +25,11 @@ KYC_OAUTH2_POST_URL = http://example.com/thank-you # For authentication to the OAuth2.0 service KYC_OAUTH2_CLIENT_ID = testcase KYC_OAUTH2_CLIENT_SECRET = password + +# Mustach template that converts OAuth2.0 data about the user +# into GNU Taler standardized attribute data. +# +# This is just an example, details will depend on the +# provider! +# +KYC_ATTRIBUTE_TEMPLATE = "{"fullname":"{{first_name}} {{last_name}}","phone":"{{phone}}"}" \ No newline at end of file diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c index 8e9323171..6926135c3 100644 --- a/src/kyclogic/plugin_kyclogic_kycaid.c +++ b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -632,6 +632,7 @@ proof_reply (void *cls) NULL, /* user id */ NULL, /* provider legi ID */ GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ + NULL, /* attributes */ MHD_HTTP_BAD_REQUEST, resp); } diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index 5709b18f8..f5a08e92e 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -21,6 +21,7 @@ #include "platform.h" #include "taler_kyclogic_plugin.h" #include "taler_mhd_lib.h" +#include "taler_templating_lib.h" #include "taler_json_lib.h" #include #include "taler_util.h" @@ -105,6 +106,12 @@ struct TALER_KYCLOGIC_ProviderDetails */ char *post_kyc_redirect_url; + /** + * Template for converting user-data returned by + * the provider into our KYC attribute data. + */ + char *attribute_template; + /** * Validity time for a successful KYC process. */ @@ -194,6 +201,11 @@ struct TALER_KYCLOGIC_ProofHandle */ char *post_body; + /** + * KYC attributes returned about the user by the OAuth 2.0 server. + */ + json_t *attributes; + /** * Response to return. */ @@ -277,6 +289,7 @@ oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) GNUNET_free (pd->client_id); GNUNET_free (pd->client_secret); GNUNET_free (pd->post_kyc_redirect_url); + GNUNET_free (pd->attribute_template); GNUNET_free (pd); } @@ -443,6 +456,21 @@ oauth2_load_configuration (void *cls, } pd->post_kyc_redirect_url = s; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ps->cfg, + provider_section_name, + "KYC_OAUTH2_ATTRIBUTE_TEMPLATE", + &s)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + provider_section_name, + "KYC_OAUTH2_ATTRIBUTE_TEMPLATE"); + } + else + { + pd->attribute_template = s; + } + return pd; } @@ -566,9 +594,12 @@ return_proof_response (void *cls) ph->provider_user_id, ph->provider_legitimization_id, GNUNET_TIME_relative_to_absolute (ph->pd->validity), + ph->attributes, ph->http_status, ph->response); GNUNET_free (ph->provider_user_id); + if (NULL != ph->attributes) + json_decref (ph->attributes); GNUNET_free (ph); } @@ -640,6 +671,57 @@ handle_proof_error (struct TALER_KYCLOGIC_ProofHandle *ph, } +/** + * Convert user data returned by the provider into + * standardized attribute data. + * + * @param pd our provider configuration + * @param data user-data given by the provider + * @return converted KYC attribute data object + */ +static json_t * +data2attributes (const struct TALER_KYCLOGIC_ProviderDetails *pd, + const json_t *data) +{ + json_t *ret; + void *attr_data; + size_t attr_size; + int rv; + json_error_t err; + + if (NULL == pd->attribute_template) + return json_object (); + if (0 != + (rv = TALER_TEMPLATING_fill (pd->attribute_template, + data, + &attr_data, + &attr_size))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to convert KYC provider data to attributes: %d\n", + rv); + json_dumpf (data, + stderr, + JSON_INDENT (2)); + return NULL; + } + ret = json_loadb (attr_data, + attr_size, + JSON_REJECT_DUPLICATES, + &err); + GNUNET_free (attr_data); + if (NULL == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse converted KYC attributes as JSON: %s (at offset %d)\n", + err.text, + err.position); + return NULL; + } + return ret; +} + + /** * The request for @a ph succeeded (presumably). * Call continuation with the result. @@ -689,6 +771,7 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph, GNUNET_break_op (0); handle_proof_error (ph, j); + GNUNET_JSON_parse_free (spec); return; } { @@ -716,6 +799,7 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph, "Unexpected response from KYC gateway: data must contain id"); ph->http_status = MHD_HTTP_BAD_GATEWAY; + GNUNET_JSON_parse_free (spec); return; } ph->status = TALER_KYCLOGIC_STATUS_SUCCESS; @@ -731,6 +815,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph, ph->http_status = MHD_HTTP_SEE_OTHER; ph->provider_user_id = GNUNET_strdup (id); } + ph->attributes = data2attributes (ph->pd, + data); + GNUNET_JSON_parse_free (spec); } diff --git a/src/kyclogic/plugin_kyclogic_persona.c b/src/kyclogic/plugin_kyclogic_persona.c index abc8e78f5..9f3952558 100644 --- a/src/kyclogic/plugin_kyclogic_persona.c +++ b/src/kyclogic/plugin_kyclogic_persona.c @@ -890,6 +890,7 @@ proof_generic_reply (struct TALER_KYCLOGIC_ProofHandle *ph, account_id, inquiry_id, expiration, + NULL, /* FIXME: return attributes! */ http_status, resp); } @@ -1173,6 +1174,7 @@ handle_proof_finished (void *cls, account_id, inquiry_id, expiration, + NULL, /* FIXME: return attributes! */ MHD_HTTP_SEE_OTHER, resp); } diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c index d436ef7ee..bb473c682 100644 --- a/src/kyclogic/taler-exchange-kyc-tester.c +++ b/src/kyclogic/taler-exchange-kyc-tester.c @@ -688,6 +688,7 @@ handler_kyc_webhook_post ( * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown * @param expiration until when is the KYC check valid + * @param attributes attributes about the user * @param http_status HTTP status code of @a response * @param[in] response to return to the HTTP client */ @@ -698,6 +699,7 @@ proof_cb ( const char *provider_user_id, const char *provider_legitimization_id, struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, unsigned int http_status, struct MHD_Response *response) { @@ -710,6 +712,10 @@ proof_cb ( status, http_status, provider_user_id); + if (NULL != attributes) + json_dumpf (attributes, + stderr, + JSON_INDENT (2)); MHD_resume_connection (rs->rc->connection); TALER_MHD_daemon_trigger (); rs->rc->response = response; -- cgit v1.2.3 From 54fa07f5c7be025ee1a241deabe4a5dbcca61599 Mon Sep 17 00:00:00 2001 From: Joseph Date: Mon, 23 Jan 2023 07:57:34 -0500 Subject: tests for refunds_by_coin and ready_deposit --- src/exchangedb/Makefile.am | 10 +- src/exchangedb/exchange_do_get_ready_deposit.sql | 64 +++ src/exchangedb/exchange_do_refund_by_coin.sql | 94 ++++ src/exchangedb/pg_get_ready_deposit.c | 193 ++++++- src/exchangedb/pg_select_refunds_by_coin.c | 196 ++++++- src/exchangedb/procedures.sql.in | 2 + .../test_exchangedb_populate_link_data.c | 29 +- .../test_exchangedb_populate_ready_deposit.c | 39 +- ...st_exchangedb_populate_select_refunds_by_coin.c | 602 +++++++++++++++++++++ src/exchangedb/test_exchangedb_populate_table.c | 559 ------------------- 10 files changed, 1139 insertions(+), 649 deletions(-) create mode 100644 src/exchangedb/exchange_do_get_ready_deposit.sql create mode 100644 src/exchangedb/exchange_do_refund_by_coin.sql create mode 100644 src/exchangedb/test_exchangedb_populate_select_refunds_by_coin.c delete mode 100644 src/exchangedb/test_exchangedb_populate_table.c (limited to 'src') diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index feeb09cd1..2ae5fe426 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -296,7 +296,7 @@ check_PROGRAMS = \ perf-exchangedb-reserves-in-insert-postgres\ test-exchangedb-by-j-postgres\ test-exchangedb-batch-reserves-in-insert-postgres\ - test-exchangedb-populate-table-postgres\ + test-exchangedb-populate-select-refunds-by-coin-postgres\ test-exchangedb-populate-link-data-postgres\ test-exchangedb-populate-ready-deposit-postgres @@ -306,7 +306,7 @@ TESTS = \ test-exchangedb-by-j-postgres\ perf-exchangedb-reserves-in-insert-postgres\ test-exchangedb-batch-reserves-in-insert-postgres\ - test-exchangedb-populate-table-postgres\ + test-exchangedb-populate-select-refunds-by-coin-postgres\ test-exchangedb-populate-link-data-postgres\ test-exchangedb-populate-ready-deposit-postgres test_exchangedb_postgres_SOURCES = \ @@ -369,9 +369,9 @@ test_exchangedb_batch_reserves_in_insert_postgres_LDADD = \ -lgnunetutil \ $(XLIB) -test_exchangedb_populate_table_postgres_SOURCES = \ - test_exchangedb_populate_table.c -test_exchangedb_populate_table_postgres_LDADD = \ +test_exchangedb_populate_select_refunds_by_coin_postgres_SOURCES = \ + test_exchangedb_populate_select_refunds_by_coin.c +test_exchangedb_populate_select_refunds_by_coin_postgres_LDADD = \ libtalerexchangedb.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ diff --git a/src/exchangedb/exchange_do_get_ready_deposit.sql b/src/exchangedb/exchange_do_get_ready_deposit.sql new file mode 100644 index 000000000..b887571e4 --- /dev/null +++ b/src/exchangedb/exchange_do_get_ready_deposit.sql @@ -0,0 +1,64 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2014--2022 Taler Systems SA +-- +-- 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 +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see +-- +CREATE OR REPLACE FUNCTION exchange_do_get_ready_deposit( + IN in_now INT8, + IN in_start_shard_now INT8, + IN in_end_shard_now INT8, + OUT out_payto_uri VARCHAR, + OUT out_merchant_pub BYTEA +) +LANGUAGE plpgsql +AS $$ +DECLARE + var_wire_target_h_payto BYTEA; +DECLARE + var_coin_pub BYTEA; +DECLARE + var_deposit_serial_id INT8; +BEGIN + +SELECT + coin_pub + ,deposit_serial_id + INTO + var_coin_pub + ,var_deposit_serial_id + FROM deposits_by_ready + WHERE wire_deadline <= in_now + AND shard >= in_start_shard_now + AND shard <=in_end_shard_now + ORDER BY + wire_deadline ASC + ,shard ASC; + +SELECT + merchant_pub + ,wire_target_h_payto + INTO + out_merchant_pub + ,var_wire_target_h_payto + FROM deposits + WHERE coin_pub=var_coin_pub + AND deposit_serial_id=var_deposit_serial_id; + +SELECT + payto_uri + INTO out_payto_uri + FROM wire_targets + WHERE wire_target_h_payto=var_wire_target_h_payto; + +RETURN; +END $$; diff --git a/src/exchangedb/exchange_do_refund_by_coin.sql b/src/exchangedb/exchange_do_refund_by_coin.sql new file mode 100644 index 000000000..ee00e2b5a --- /dev/null +++ b/src/exchangedb/exchange_do_refund_by_coin.sql @@ -0,0 +1,94 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2014--2022 Taler Systems SA +-- +-- 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 +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see +-- +/*DROP FUNCTION exchange_do_refund_by_coin( + IN in_coin_pub BYTEA, + IN in_merchant_pub BYTEA, + IN in_h_contract BYTEA +);*/ +CREATE OR REPLACE FUNCTION exchange_do_refund_by_coin( + IN in_coin_pub BYTEA, + IN in_merchant_pub BYTEA, + IN in_h_contract BYTEA +) +RETURNS SETOF record +LANGUAGE plpgsql +AS $$ +DECLARE + curs CURSOR + FOR + SELECT + amount_with_fee_val + ,amount_with_fee_frac + ,deposit_serial_id + FROM refunds + WHERE coin_pub=in_coin_pub; +DECLARE + i RECORD; +BEGIN +OPEN curs; +LOOP + FETCH NEXT FROM curs INTO i; + EXIT WHEN NOT FOUND; + RETURN QUERY + SELECT + i.amount_with_fee_val + ,i.amount_with_fee_frac + FROM deposits + WHERE + coin_pub=in_coin_pub + AND merchant_pub=in_merchant_pub + AND h_contract_terms=in_h_contract + AND i.deposit_serial_id = deposit_serial_id; +END LOOP; +CLOSE curs; +END $$; + +/*RETURNS TABLE(amount_with_fee_val INT8, amount_with_fee_frac INT4) +LANGUAGE plpgsql +AS $$ +DECLARE + curs CURSOR + FOR + SELECT + r.amount_with_fee_val + ,r.amount_with_fee_frac + ,r.deposit_serial_id + FROM refunds r + WHERE r.coin_pub=in_coin_pub; +DECLARE + i RECORD; +BEGIN +OPEN curs; +LOOP + FETCH NEXT FROM curs INTO i; + IF FOUND + THEN + RETURN QUERY + SELECT + i.amount_with_fee_val + ,i.amount_with_fee_frac + FROM deposits + WHERE + merchant_pub=in_merchant_pub + AND h_contract_terms=in_h_contract + AND i.deposit_serial_id = deposit_serial_id; + END IF; + EXIT WHEN NOT FOUND; +END LOOP; +CLOSE curs; + +END $$; +*/ diff --git a/src/exchangedb/pg_get_ready_deposit.c b/src/exchangedb/pg_get_ready_deposit.c index af1235293..73ac9e47b 100644 --- a/src/exchangedb/pg_get_ready_deposit.c +++ b/src/exchangedb/pg_get_ready_deposit.c @@ -41,6 +41,7 @@ TEH_PG_get_ready_deposit (void *cls, GNUNET_PQ_query_param_uint64 (&end_shard_row), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", merchant_pub), @@ -57,26 +58,180 @@ TEH_PG_get_ready_deposit (void *cls, "Finding ready deposits by deadline %s (%llu)\n", GNUNET_TIME_absolute2s (now), (unsigned long long) now.abs_value_us); - PREPARE (pg, - "deposits_get_ready", - "SELECT" - " payto_uri" - ",merchant_pub" - " FROM deposits_by_ready dbr" - " JOIN deposits dep" - " ON (dbr.coin_pub = dep.coin_pub AND" - " dbr.deposit_serial_id = dep.deposit_serial_id)" - " JOIN wire_targets wt" - " USING (wire_target_h_payto)" - " WHERE dbr.wire_deadline<=$1" - " AND dbr.shard >= $2" - " AND dbr.shard <= $3" - " ORDER BY " - " dbr.wire_deadline ASC" - " ,dbr.shard ASC" - " LIMIT 1;"); + int choose_mode =-2; + const char *query; + + if (-2 == choose_mode) + { + const char *mode = getenv ("NEW_LOGIC"); + char dummy; + if ( (NULL==mode) || + (1 != sscanf (mode, + "%d%c", + &choose_mode, + &dummy)) ) + { + if (NULL != mode) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Bad mode `%s' specified\n", + mode); + } + if (NULL==mode) + choose_mode=0; + + + } + switch (choose_mode) + { + case 0: + query="deposits_get_ready"; + PREPARE (pg, + query, + "SELECT" + " payto_uri" + ",merchant_pub" + " FROM deposits_by_ready dbr" + " JOIN deposits dep" + " ON (dbr.coin_pub = dep.coin_pub AND" + " dbr.deposit_serial_id = dep.deposit_serial_id)" + " JOIN wire_targets wt" + " USING (wire_target_h_payto)" + " WHERE dbr.wire_deadline<=$1" + " AND dbr.shard >= $2" + " AND dbr.shard <= $3" + " ORDER BY " + " dbr.wire_deadline ASC" + " ,dbr.shard ASC" + " LIMIT 1;"); + break; + case 1: + query="deposits_get_ready_v1"; + PREPARE (pg, + query, + "WITH rc AS MATERIALIZED (" + " SELECT" + " coin_pub" + ",deposit_serial_id" + " FROM deposits_by_ready" + " WHERE" + " wire_deadline<=$1" + " AND shard >= $2" + " AND shard <= $3" + " ORDER BY " + " wire_deadline ASC" + " ,shard ASC" + " LIMIT 1" + ")" + "SELECT" + " wt.payto_uri" + ",dep.merchant_pub" + " FROM (" + " SELECT" + " wire_target_h_payto" + ",merchant_pub" + " FROM deposits" + " WHERE coin_pub=(SELECT coin_pub FROM rc)" + " AND deposit_serial_id=(SELECT deposit_serial_id FROM rc)" + ") dep" + " JOIN wire_targets wt" + " ON (dep.wire_target_h_payto = wt.wire_target_h_payto)" + ); + + break; + case 2: + query = "stored_procedure_get_ready_deposit"; + PREPARE (pg, + query, + "SELECT" + " out_payto_uri AS payto_uri" + ",out_merchant_pub AS merchant_pub" + " FROM" + " exchange_do_get_ready_deposit" + " ($1, $2, $3) "); + break; + case 3: + query="deposits_get_ready_v3"; + PREPARE (pg, + query, + "WITH rc AS MATERIALIZED (" + " SELECT" + " coin_pub" + ",deposit_serial_id" + " FROM deposits_by_ready" + " WHERE" + " wire_deadline<=$1" + " AND shard >= $2" + " AND shard <= $3" + " ORDER BY " + " wire_deadline ASC" + " ,shard ASC" + " LIMIT 1" + ")" + "SELECT" + " wt.payto_uri" + ",dep.merchant_pub" + " FROM (" + " SELECT" + " wire_target_h_payto" + ",merchant_pub" + ",coin_pub" + " FROM deposits" + " WHERE coin_pub=(SELECT coin_pub FROM rc)" + " AND deposit_serial_id=(SELECT deposit_serial_id FROM rc)" + ") dep" + " JOIN wire_targets wt" + " ON (dep.wire_target_h_payto = wt.wire_target_h_payto)" + " JOIN rc" + " ON (dep.coin_pub=rc.coin_pub)" + ); + + break; + case 4: + query="deposits_get_ready_v4"; + PREPARE (pg, + query, + "WITH rc AS MATERIALIZED (" + " SELECT" + " coin_pub" + ",deposit_serial_id" + " FROM deposits_by_ready" + " WHERE" + " wire_deadline<=$1" + " AND shard >= $2" + " AND shard <= $3" + " ORDER BY " + " wire_deadline ASC" + " ,shard ASC" + " LIMIT 1" + ")," + "WITH rv AS MATERIALIZED (" + " SELECT" + " payto_uri" + ",wire_target_h_payto" + " FROM wire_targets" + ")" + "SELECT" + " rv.payto_uri" + ",dep.merchant_pub" + " FROM (" + " SELECT" + " wire_target_h_payto" + ",merchant_pub" + " FROM deposits" + " WHERE coin_pub=(SELECT coin_pub FROM rc)" + " AND deposit_serial_id=(SELECT deposit_serial_id FROM rc)" + ") dep" + " JOIN rv" + " ON (rv.wire_target_h_payto=dep.wire_target_h_payto)" + ); + break; + default: + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "deposits_get_ready", + query, params, rs); } diff --git a/src/exchangedb/pg_select_refunds_by_coin.c b/src/exchangedb/pg_select_refunds_by_coin.c index b9db2b709..255cac342 100644 --- a/src/exchangedb/pg_select_refunds_by_coin.c +++ b/src/exchangedb/pg_select_refunds_by_coin.c @@ -112,17 +112,47 @@ TEH_PG_select_refunds_by_coin ( GNUNET_PQ_query_param_auto_from_type (h_contract), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_QueryParam params5[] = { + GNUNET_PQ_query_param_auto_from_type (coin_pub), + GNUNET_PQ_query_param_end + }; + struct SelectRefundContext srctx = { .cb = cb, .cb_cls = cb_cls, .pg = pg, .status = GNUNET_OK }; + static int percent_refund = -2; + const char *query; + struct GNUNET_PQ_QueryParam *xparams = params; - if (NULL == getenv ("NEW_LOGIC")) + if (-2 == percent_refund) { + const char *mode = getenv ("NEW_LOGIC"); + char dummy; + + if ( (NULL==mode) || + (1 != sscanf (mode, + "%d%c", + &percent_refund, + &dummy)) ) + { + if (NULL != mode) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Bad mode `%s' specified\n", + mode); + } + if (NULL==mode) + percent_refund=0; + } + + switch (percent_refund) + { + case 0: + query = "get_refunds_by_coin_and_contract-v0"; PREPARE (pg, - "get_refunds_by_coin_and_contract", + query, "SELECT" " ref.amount_with_fee_val" ",ref.amount_with_fee_frac" @@ -132,12 +162,26 @@ TEH_PG_select_refunds_by_coin ( " WHERE ref.coin_pub=$1" " AND dep.merchant_pub=$2" " AND dep.h_contract_terms=$3;"); - } - else - { - // FIXME-Joseph + break; + case 1: + query = "get_refunds_by_coin_and_contract-v1"; + PREPARE (pg, + query, + "SELECT" + " ref.amount_with_fee_val" + ",ref.amount_with_fee_frac" + " FROM refunds ref" + " LEFT JOIN deposits dep" + " ON dep.coin_pub = ref.coin_pub" + " AND ref.deposit_serial_id = dep.deposit_serial_id" + " WHERE ref.coin_pub=$1" + " AND dep.merchant_pub=$2" + " AND dep.h_contract_terms=$3;"); + break; + case 2: + query = "get_refunds_by_coin_and_contract-v2"; PREPARE (pg, - "get_refunds_by_coin_and_contract", + query, "WITH rc AS MATERIALIZED(" "SELECT" " amount_with_fee_val" @@ -149,21 +193,139 @@ TEH_PG_select_refunds_by_coin ( "SELECT" " rc.amount_with_fee_val" " ,rc.amount_with_fee_frac" - " FROM " - "(SELECT" - " rc.amount_with_fee_val" - " ,rc.amount_with_fee_frac" " FROM deposits dep" + " JOIN rc" + " ON rc.deposit_serial_id = dep.deposit_serial_id" " WHERE" - " dep.coin_pub = $1" // optional... + " dep.coin_pub = $1" " AND dep.merchant_pub = $2" - " AND dep.h_contract_terms = $3) dep" - " JOIN rc" - " USING (coin_pub,deposit_serial_id)"); + " AND dep.h_contract_terms = $3"); + break; + case 3: + query = "get_refunds_by_coin_and_contract-v3"; + PREPARE (pg, + query, + "WITH rc AS MATERIALIZED(" + "SELECT" + " amount_with_fee_val" + ",amount_with_fee_frac" + ",deposit_serial_id" + " FROM refunds" + " WHERE coin_pub=$1)" + "SELECT" + " rc.amount_with_fee_val" + " ,rc.amount_with_fee_frac" + " FROM (" + "SELECT" + " amount_with_fee_val" + ",amount_with_fee_frac" + " FROM deposits depos" + " WHERE" + " depos.coin_pub = $1" + " AND depos.merchant_pub = $2" + " AND depos.h_contract_terms = $3) dep, rc;"); + break; + case 4: + query = "get_refunds_by_coin_and_contract-v4"; + PREPARE (pg, + query, + "WITH rc AS MATERIALIZED(" + "SELECT" + " amount_with_fee_val" + ",amount_with_fee_frac" + ",coin_pub" + ",deposit_serial_id" + " FROM refunds ref" + " WHERE ref.coin_pub=$1)" + "SELECT" + " rc.amount_with_fee_val" + " ,rc.amount_with_fee_frac" + " ,deposit_serial_id" + " FROM (" + "SELECT" + " amount_with_fee_val" + ",amount_with_fee_frac" + " FROM deposits depos" + " WHERE" + " depos.merchant_pub = $2" + " AND depos.h_contract_terms = $3) dep JOIN rc " + "USING(deposit_serial_id, coin_pub);"); + break; + case 5: + query = "get_refunds_by_coin_and_contract-v-broken"; + xparams = params5; + PREPARE (pg, + query, + "SELECT" + " amount_with_fee_val" + ",amount_with_fee_frac" + ",coin_pub" + ",deposit_serial_id" + " FROM refunds" + " WHERE coin_pub=$1;"); + break; + case 8: + query = "get_refunds_by_coin_and_contract-v8"; + PREPARE (pg, + query, + "WITH" + " rc AS MATERIALIZED(" + " SELECT" + " amount_with_fee_val" + " ,amount_with_fee_frac" + " ,coin_pub" + " ,deposit_serial_id" + " FROM refunds" + " WHERE coin_pub=$1)," + " dep AS MATERIALIZED(" + " SELECT" + " deposit_serial_id" + " FROM deposits" + " WHERE coin_pub = $1" + " AND merchant_pub = $2" + " AND h_contract_terms = $3" + ")" + "SELECT" + " rc.amount_with_fee_val" + " ,rc.amount_with_fee_frac" + " FROM " + " rc JOIN dep USING (deposit_serial_id);"); + break; + case 9: + query = "get_refunds_by_coin_and_contract-v9-broken"; + PREPARE (pg, + query, + "SELECT" + " ref.amount_with_fee_val" + " ,ref.amount_with_fee_frac" + " FROM deposits dep" + " JOIN refunds ref USING(deposit_serial_id)" + " WHERE dep.coin_pub IN (" + " SELECT coin_pub" + " FROM refunds" + " WHERE coin_pub=$1)" + " AND merchant_pub = $2" + " AND h_contract_terms = $3;"); + break; + case 10: + query = "get_refunds_by_coin_and_contract-v10-broken"; + PREPARE (pg, + query, + "SELECT" + " *" + " FROM" + " exchange_do_refund_by_coin" + " ($1, $2, $3) " + " AS (amount_with_fee_val INT8, amount_with_fee_frac INT4);"); + break; + default: + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; } + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "get_refunds_by_coin_and_contract", - params, + query, + xparams, &get_refunds_cb, &srctx); if (GNUNET_SYSERR == srctx.status) diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in index 73b75af29..ef6341a1b 100644 --- a/src/exchangedb/procedures.sql.in +++ b/src/exchangedb/procedures.sql.in @@ -44,5 +44,7 @@ SET search_path TO exchange; #include "exchange_do_batch2_reserves_in_insert.sql" #include "exchange_do_batch4_reserves_in_insert.sql" #include "exchange_do_batch8_reserves_in_insert.sql" +#include "exchange_do_refund_by_coin.sql" +#include "exchange_do_get_ready_deposit.sql" COMMIT; diff --git a/src/exchangedb/test_exchangedb_populate_link_data.c b/src/exchangedb/test_exchangedb_populate_link_data.c index ff294874a..1323f3b3e 100644 --- a/src/exchangedb/test_exchangedb_populate_link_data.c +++ b/src/exchangedb/test_exchangedb_populate_link_data.c @@ -24,14 +24,6 @@ #include "taler_exchangedb_plugin.h" #include "math.h" - -#define NUM_ROWS 1000 - -/** - * Global result from the testcase. - */ -static int result; - /** * Report line of error if @a cond is true, and jump to label "drop". */ @@ -55,22 +47,10 @@ static int result; #define ZR_BLK(ptr) \ memset (ptr, 0, sizeof (*ptr)) - -/** - * Currency we use. Must match test-exchange-db-*.conf. - */ #define CURRENCY "EUR" - -/** - * How big do we make the RSA keys? - */ #define RSA_KEY_SIZE 1024 -static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; - -static struct TALER_TransferPrivateKeyP tprivs[TALER_CNC_KAPPA]; - -static struct TALER_TransferPublicKeyP tpub; #define ROUNDS 10 +#define NUM_ROWS 1000 #define MELT_NEW_COINS 5 #define MELT_NOREVEAL_INDEX 1 /** @@ -82,7 +62,10 @@ static struct TALER_DenomFeeSet fees; * Denomination keys used for fresh coins in melt test. */ static struct DenomKeyPair **new_dkp; - +static int result; +static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; +static struct TALER_TransferPrivateKeyP tprivs[TALER_CNC_KAPPA]; +static struct TALER_TransferPublicKeyP tpub; struct DenomKeyPair { struct TALER_DenominationPrivateKey priv; @@ -222,8 +205,6 @@ handle_link_data_cb (void *cls, } } - - /** * Main function that will be run by the scheduler. * diff --git a/src/exchangedb/test_exchangedb_populate_ready_deposit.c b/src/exchangedb/test_exchangedb_populate_ready_deposit.c index 49b1bf5d2..97273fc9e 100644 --- a/src/exchangedb/test_exchangedb_populate_ready_deposit.c +++ b/src/exchangedb/test_exchangedb_populate_ready_deposit.c @@ -24,9 +24,6 @@ #include "taler_exchangedb_plugin.h" #include "math.h" - -#define NUM_ROWS 1000 - /** * Global result from the testcase. */ @@ -55,20 +52,13 @@ static int result; #define ZR_BLK(ptr) \ memset (ptr, 0, sizeof (*ptr)) - /** * Currency we use. Must match test-exchange-db-*.conf. */ #define CURRENCY "EUR" - -/** - * How big do we make the RSA keys? - */ #define RSA_KEY_SIZE 1024 -static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; - - -#define ROUNDS 100 +#define NUM_ROWS 1000000 +#define ROUNDS 10000 #define MELT_NEW_COINS 5 #define MELT_NOREVEAL_INDEX 1 /** @@ -81,7 +71,7 @@ static struct TALER_MerchantWireHashP h_wire_wt; * Denomination keys used for fresh coins in melt test. */ static struct DenomKeyPair **new_dkp; - +static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; struct DenomKeyPair { struct TALER_DenominationPrivateKey priv; @@ -389,7 +379,6 @@ run (void *cls) &nonce_ok, &ruuid)); } - { /* ENSURE_COIN_KNOWN */ uint64_t known_coin_id; @@ -408,23 +397,23 @@ run (void *cls) refresh.noreveal_index = MELT_NOREVEAL_INDEX; } /*STORE INTO DEPOSIT*/ - { - struct GNUNET_TIME_Timestamp now; - now = GNUNET_TIME_timestamp_get (); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_deposit (plugin->cls, - now, - &depos[i])); - } - if (ROUNDS == i) - TALER_denom_sig_free (&depos[i].coin.denom_sig); + { + struct GNUNET_TIME_Timestamp now; + now = GNUNET_TIME_timestamp_get (); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_deposit (plugin->cls, + now, + &depos[i])); + } + if (ROUNDS == i) + TALER_denom_sig_free (&depos[i].coin.denom_sig); } /* End of benchmark setup */ GNUNET_free(perm); // commit FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->commit (plugin->cls)); - /**** CALL GET LINK DATA ****/ + /**** CALL GET READY DEPOSIT ****/ for (unsigned int r=0; r< ROUNDS; r++) { struct GNUNET_TIME_Absolute time; diff --git a/src/exchangedb/test_exchangedb_populate_select_refunds_by_coin.c b/src/exchangedb/test_exchangedb_populate_select_refunds_by_coin.c new file mode 100644 index 000000000..c094b2048 --- /dev/null +++ b/src/exchangedb/test_exchangedb_populate_select_refunds_by_coin.c @@ -0,0 +1,602 @@ +/* + This file is part of TALER + Copyright (C) 2014-2022 Taler Systems SA + + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file exchangedb/test_exchangedb_populate_table.c + * @brief test cases for DB interaction functions + * @author Joseph Xu + */ +#include "platform.h" +#include "taler_exchangedb_lib.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" +#include "math.h" + +/** + * Global result from the testcase. + */ +static int result; + +/** + * Report line of error if @a cond is true, and jump to label "drop". + */ +#define FAILIF(cond) \ + do { \ + if (! (cond)) {break;} \ + GNUNET_break (0); \ + goto drop; \ + } while (0) + +/** + * Initializes @a ptr with random data. + */ +#define RND_BLK(ptr) \ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) + +/** + * Initializes @a ptr with zeros. + */ +#define ZR_BLK(ptr) \ + memset (ptr, 0, sizeof (*ptr)) + +/** + * Currency we use. Must match test-exchange-db-*.conf. + */ +#define CURRENCY "EUR" +#define RSA_KEY_SIZE 1024 +#define ROUNDS 10000 +#define NUM_ROWS 1000000 +#define MELT_NEW_COINS 5 +#define MELT_NOREVEAL_INDEX 1 +/** + * Database plugin under test. + */ +static struct TALER_EXCHANGEDB_Plugin *plugin; +static struct TALER_DenomFeeSet fees; +static struct TALER_MerchantWireHashP h_wire_wt; +static struct DenomKeyPair **new_dkp; +static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; +struct DenomKeyPair +{ + struct TALER_DenominationPrivateKey priv; + struct TALER_DenominationPublicKey pub; +}; + +/** + * Destroy a denomination key pair. The key is not necessarily removed from the DB. + * + * @param dkp the key pair to destroy + */ +static void +destroy_denom_key_pair (struct DenomKeyPair *dkp) +{ + TALER_denom_pub_free (&dkp->pub); + TALER_denom_priv_free (&dkp->priv); + GNUNET_free (dkp); +} + + +/** + * Create a denomination key pair by registering the denomination in the DB. + * + * @param size the size of the denomination key + * @param now time to use for key generation, legal expiration will be 3h later. + * @param fees fees to use + * @return the denominaiton key pair; NULL upon error + */ +static struct DenomKeyPair * +create_denom_key_pair (unsigned int size, + struct GNUNET_TIME_Timestamp now, + const struct TALER_Amount *value, + const struct TALER_DenomFeeSet *fees) +{ + struct DenomKeyPair *dkp; + struct TALER_EXCHANGEDB_DenominationKey dki; + struct TALER_EXCHANGEDB_DenominationKeyInformation issue2; + + dkp = GNUNET_new (struct DenomKeyPair); + GNUNET_assert (GNUNET_OK == + TALER_denom_priv_create (&dkp->priv, + &dkp->pub, + TALER_DENOMINATION_RSA, + size)); + memset (&dki, + 0, + sizeof (struct TALER_EXCHANGEDB_DenominationKey)); + dki.denom_pub = dkp->pub; + dki.issue.start = now; + dki.issue.expire_withdraw + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add ( + now.abs_time, + GNUNET_TIME_UNIT_HOURS)); + dki.issue.expire_deposit + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add ( + now.abs_time, + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_HOURS, 2))); + dki.issue.expire_legal + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add ( + now.abs_time, + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_HOURS, 3))); + dki.issue.value = *value; + dki.issue.fees = *fees; + TALER_denom_pub_hash (&dkp->pub, + &dki.issue.denom_hash); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_denomination_info (plugin->cls, + &dki.denom_pub, + &dki.issue)) + { + GNUNET_break (0); + destroy_denom_key_pair (dkp); + return NULL; + } + memset (&issue2, 0, sizeof (issue2)); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->get_denomination_info (plugin->cls, + &dki.issue.denom_hash, + &issue2)) + { + GNUNET_break (0); + destroy_denom_key_pair (dkp); + return NULL; + } + if (0 != GNUNET_memcmp (&dki.issue, + &issue2)) + { + GNUNET_break (0); + destroy_denom_key_pair (dkp); + return NULL; + } + return dkp; +} +/** + * Callback invoked with information about refunds applicable + * to a particular coin. + * + * @param cls closure with the `struct TALER_EXCHANGEDB_Refund *` we expect to get + * @param amount_with_fee amount being refunded + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +check_refund_cb (void *cls, + const struct TALER_Amount *amount_with_fee) +{ + const struct TALER_EXCHANGEDB_Refund *refund = cls; + if (0 != TALER_amount_cmp (amount_with_fee, + &refund->details.refund_amount)) + { + GNUNET_break (0); + result = 66; + } + return GNUNET_OK; +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure with config + */ + +static void +run (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + const uint32_t num_partitions = 10; + struct GNUNET_TIME_Timestamp ts; + struct TALER_EXCHANGEDB_Deposit *depos=NULL; + struct GNUNET_TIME_Timestamp deadline; + struct TALER_Amount value; + union TALER_DenominationBlindingKeyP bks; + struct TALER_CoinPubHashP c_hash; + struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; + struct TALER_ExchangeWithdrawValues alg_values = { + .cipher = TALER_DENOMINATION_RSA + }; + struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; + unsigned long long sqrs = 0; + struct TALER_EXCHANGEDB_Refund *ref=NULL; + unsigned int *perm; + unsigned long long duration_sq; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; + struct TALER_DenominationPublicKey *new_denom_pubs = NULL; + unsigned int count=0; + + ref = GNUNET_new_array (ROUNDS +1, + struct TALER_EXCHANGEDB_Refund); + depos = GNUNET_new_array (ROUNDS +1, + struct TALER_EXCHANGEDB_Deposit); + ZR_BLK (&cbc); + + if (NULL == + (plugin = TALER_EXCHANGEDB_plugin_load (cfg))) + { + GNUNET_break (0); + result = 77; + return; + } + (void) plugin->drop_tables (plugin->cls); + if (GNUNET_OK != + plugin->create_tables (plugin->cls, + true, + num_partitions)) + { + GNUNET_break (0); + result = 77; + goto cleanup; + } + if (GNUNET_OK != + plugin->preflight (plugin->cls)) + { + GNUNET_break (0); + goto cleanup; + } + + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":1.000010", + &value)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.000010", + &fees.withdraw)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.000010", + &fees.deposit)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.000010", + &fees.refresh)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.000010", + &fees.refund)); + GNUNET_assert (NUM_ROWS >= ROUNDS); + + ts = GNUNET_TIME_timestamp_get (); + deadline = GNUNET_TIME_timestamp_get (); + //DENOMINATION + { + //PAIR KEY LIST + new_dkp = GNUNET_new_array (MELT_NEW_COINS, + struct DenomKeyPair *); + //PUBLIC KEY LIST + new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, + struct TALER_DenominationPublicKey); + //REFRESH REVEAL COIN LIST + revealed_coins + = GNUNET_new_array (MELT_NEW_COINS, + struct TALER_EXCHANGEDB_RefreshRevealedCoin); + for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) + { + struct GNUNET_TIME_Timestamp now; + struct TALER_BlindedRsaPlanchet *rp; + struct TALER_BlindedPlanchet *bp; + + now = GNUNET_TIME_timestamp_get (); + //5 KEY PAIR + new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, + now, + &value, + &fees); + GNUNET_assert (NULL != new_dkp[cnt]); + new_denom_pubs[cnt] = new_dkp[cnt]->pub; + ccoin = &revealed_coins[cnt]; + bp = &ccoin->blinded_planchet; + bp->cipher = TALER_DENOMINATION_RSA; + rp = &bp->details.rsa_blinded_planchet; + rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 ( + GNUNET_CRYPTO_QUALITY_WEAK, + (RSA_KEY_SIZE / 8) - 1); + rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + rp->blinded_msg, + rp->blinded_msg_size); + TALER_denom_pub_hash (&new_dkp[cnt]->pub, + &ccoin->h_denom_pub); + ccoin->exchange_vals = alg_values; + TALER_coin_ev_hash (bp, + &ccoin->h_denom_pub, + &ccoin->coin_envelope_hash); + GNUNET_assert (GNUNET_OK == + TALER_denom_sign_blinded (&ccoin->coin_sig, + &new_dkp[cnt]->priv, + true, + bp)); + GNUNET_assert (GNUNET_OK == + TALER_coin_ev_hash (bp, + &cbc.denom_pub_hash, + &cbc.h_coin_envelope)); + GNUNET_assert ( + GNUNET_OK == + TALER_denom_sign_blinded ( + &cbc.sig, + &new_dkp[cnt]->priv, + false, + bp)); + } + } + + perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, + NUM_ROWS); + // begin + FAILIF (GNUNET_OK != + plugin->start (plugin->cls, + "Transaction")); + for (unsigned int j=0; j< NUM_ROWS; j++) + { + unsigned int i = perm[j]; + unsigned int k = (unsigned int)rand()%5; + if (i >= ROUNDS) + i = ROUNDS; /* throw-away slot, do not keep around */ + RND_BLK (&coin_pub); + RND_BLK (&c_hash); + depos[i].deposit_fee = fees.deposit; + RND_BLK (&depos[i].coin.coin_pub); + TALER_denom_pub_hash (&new_dkp[k]->pub, + &depos[i].coin.denom_pub_hash); + // TALER_denom_pub_hash (&dkp->pub, + // &ref.coin.denom_pub_hash); + GNUNET_assert (GNUNET_OK == + TALER_denom_sig_unblind (&depos[i].coin.denom_sig, + &cbc.sig, + &bks, + &c_hash, + &alg_values, + &new_dkp[k]->pub)); + RND_BLK (&depos[i].merchant_pub); + RND_BLK (&depos[i].csig); + RND_BLK (&depos[i].h_contract_terms); + RND_BLK (&depos[i].wire_salt); + depos[i].amount_with_fee = value; + depos[i].refund_deadline = deadline; + depos[i].wire_deadline = deadline; + depos[i].receiver_wire_account = + "payto://iban/DE67830654080004822650?receiver-name=Test"; + TALER_merchant_wire_signature_hash ( + "payto://iban/DE67830654080004822650?receiver-name=Test", + &depos[i].wire_salt, + &h_wire_wt); + depos[i].timestamp = ts; + uint64_t known_coin_id; + {//ENSURE_COIN_KNOWN + + struct TALER_DenominationHashP dph; + struct TALER_AgeCommitmentHash agh; + FAILIF (TALER_EXCHANGEDB_CKS_ADDED != + plugin->ensure_coin_known (plugin->cls, + &depos[i].coin, + &known_coin_id, + &dph, + &agh)); + } + /*STORE INTO DEPOSIT*/ + { + struct GNUNET_TIME_Timestamp now; + now = GNUNET_TIME_timestamp_get (); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_deposit (plugin->cls, + now, + &depos[i])); + } + { + bool not_found; + bool refund_ok; + bool gone; + bool conflict; + unsigned int refund_percent=0; + switch (refund_percent){ + case 2 ://100% refund + ref[i].coin = depos[i].coin; + ref[i].details.merchant_pub = depos[i].merchant_pub; + RND_BLK(&ref[i].details.merchant_sig); + ref[i].details.h_contract_terms = depos[i].h_contract_terms; + ref[i].coin.coin_pub = depos[i].coin.coin_pub; + ref[i].details.rtransaction_id = i; + ref[i].details.refund_amount = value; + ref[i].details.refund_fee = fees.refund; + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->do_refund (plugin->cls, + &ref[i], + &fees.deposit, + known_coin_id, + ¬_found, + &refund_ok, + &gone, + &conflict)); + break; + case 1 ://10% refund + if (count < (NUM_ROWS/10)) + { + ref[i].coin = depos[i].coin; + ref[i].details.merchant_pub = depos[i].merchant_pub; + RND_BLK(&ref[i].details.merchant_sig); + ref[i].details.h_contract_terms = depos[i].h_contract_terms; + ref[i].coin.coin_pub = depos[i].coin.coin_pub; + ref[i].details.rtransaction_id = i; + ref[i].details.refund_amount = value; + ref[i].details.refund_fee = fees.refund; + } + else + { + ref[i].coin = depos[i].coin; + RND_BLK(&ref[i].details.merchant_pub); + RND_BLK(&ref[i].details.merchant_sig); + RND_BLK(&ref[i].details.h_contract_terms); + RND_BLK(&ref[i].coin.coin_pub); + ref[i].details.rtransaction_id = i; + ref[i].details.refund_amount = value; + ref[i].details.refund_fee = fees.refund; + } + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->do_refund (plugin->cls, + &ref[i], + &fees.deposit, + known_coin_id, + ¬_found, + &refund_ok, + &gone, + &conflict)); + count++; + break; + case 0://no refund + ref[i].coin=depos[i].coin; + RND_BLK(&ref[i].details.merchant_pub); + RND_BLK(&ref[i].details.merchant_sig); + RND_BLK(&ref[i].details.h_contract_terms); + RND_BLK(&ref[i].coin.coin_pub); + ref[i].details.rtransaction_id = i; + ref[i].details.refund_amount = value; + ref[i].details.refund_fee = fees.refund; + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->do_refund (plugin->cls, + &ref[i], + &fees.deposit, + known_coin_id, + ¬_found, + &refund_ok, + &gone, + &conflict)); + break; + }/* END OF SWITCH CASE */ + } + if (ROUNDS == i) + TALER_denom_sig_free (&depos[i].coin.denom_sig); + } + /* End of benchmark setup */ + GNUNET_free (perm); + // commit + FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + plugin->commit (plugin->cls)); + for (unsigned int r = 0; r < ROUNDS; r++) + { + struct GNUNET_TIME_Absolute time; + struct GNUNET_TIME_Relative duration; + + time = GNUNET_TIME_absolute_get (); + FAILIF (0 > + plugin->select_refunds_by_coin (plugin->cls, + &ref[r].coin.coin_pub, + &ref[r].details.merchant_pub, + &ref[r].details.h_contract_terms, + &check_refund_cb, + &ref[r])); + duration = GNUNET_TIME_absolute_get_duration (time); + times = GNUNET_TIME_relative_add (times, + duration); + duration_sq = duration.rel_value_us * duration.rel_value_us; + GNUNET_assert (duration_sq / duration.rel_value_us == duration.rel_value_us); + GNUNET_assert (sqrs + duration_sq >= sqrs); + sqrs += duration_sq; + } + /* evaluation of performance */ + { + struct GNUNET_TIME_Relative avg; + double avg_dbl; + double variance; + + avg = GNUNET_TIME_relative_divide (times, + ROUNDS); + avg_dbl = avg.rel_value_us; + variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); + fprintf(stdout, + "%8llu ± %6.0f\n", + (unsigned long long) avg.rel_value_us, + sqrt (variance / (ROUNDS-1))); + } + result = 0; +drop: + GNUNET_break (GNUNET_OK == + plugin->drop_tables (plugin->cls)); +cleanup: + if (NULL != revealed_coins) + { + for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) + { + TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig); + TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet); + } + GNUNET_free (revealed_coins); + revealed_coins = NULL; + } + GNUNET_free (new_denom_pubs); + for (unsigned int cnt = 0; + (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]); + cnt++) + destroy_denom_key_pair (new_dkp[cnt]); + GNUNET_free (new_dkp); + for (unsigned int i=0; i< ROUNDS +1 ; i++) + { + TALER_denom_sig_free (&depos[i].coin.denom_sig); + } + GNUNET_free(depos); + GNUNET_free(ref); + TALER_EXCHANGEDB_plugin_unload (plugin); + plugin = NULL; +} + + +int +main (int argc, + char *const argv[]) +{ + const char *plugin_name; + char *config_filename; + char *testname; + struct GNUNET_CONFIGURATION_Handle *cfg; + + (void) argc; + result = -1; + if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) + { + GNUNET_break (0); + return -1; + } + GNUNET_log_setup (argv[0], + "WARNING", + NULL); + plugin_name++; + (void) GNUNET_asprintf (&testname, + "test-exchange-db-%s", + plugin_name); + (void) GNUNET_asprintf (&config_filename, + "%s.conf", + testname); + fprintf (stdout, + "Using config: %s\n", + config_filename); + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_parse (cfg, + config_filename)) + { + GNUNET_break (0); + GNUNET_free (config_filename); + GNUNET_free (testname); + return 2; + } + GNUNET_SCHEDULER_run (&run, + cfg); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (config_filename); + GNUNET_free (testname); + return result; +} + + +/* end of test_exchangedb_by_j.c */ diff --git a/src/exchangedb/test_exchangedb_populate_table.c b/src/exchangedb/test_exchangedb_populate_table.c deleted file mode 100644 index face454fd..000000000 --- a/src/exchangedb/test_exchangedb_populate_table.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA - - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see -*/ -/** - * @file exchangedb/test_exchangedb_populate_table.c - * @brief test cases for DB interaction functions - * @author Joseph Xu - */ -#include "platform.h" -#include "taler_exchangedb_lib.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" -#include "math.h" - -#define NUM_ROWS 10000 - -/** - * Global result from the testcase. - */ -static int result; - -/** - * Report line of error if @a cond is true, and jump to label "drop". - */ -#define FAILIF(cond) \ - do { \ - if (! (cond)) {break;} \ - GNUNET_break (0); \ - goto drop; \ - } while (0) - - -/** - * Initializes @a ptr with random data. - */ -#define RND_BLK(ptr) \ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) - -/** - * Initializes @a ptr with zeros. - */ -#define ZR_BLK(ptr) \ - memset (ptr, 0, sizeof (*ptr)) - - -/** - * Currency we use. Must match test-exchange-db-*.conf. - */ -#define CURRENCY "EUR" -/** - * How big do we make the RSA keys? - */ -#define RSA_KEY_SIZE 1024 -#define ROUNDS 1000 -#define MELT_NEW_COINS 5 -#define MELT_NOREVEAL_INDEX 1 -/** - * Database plugin under test. - */ -static struct TALER_EXCHANGEDB_Plugin *plugin; -static struct TALER_DenomFeeSet fees; -static struct TALER_MerchantWireHashP h_wire_wt; - -struct DenomKeyPair -{ - struct TALER_DenominationPrivateKey priv; - struct TALER_DenominationPublicKey pub; -}; -static struct DenomKeyPair **new_dkp; -static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; -/** - * Destroy a denomination key pair. The key is not necessarily removed from the DB. - * - * @param dkp the key pair to destroy - */ -static void -destroy_denom_key_pair (struct DenomKeyPair *dkp) -{ - TALER_denom_pub_free (&dkp->pub); - TALER_denom_priv_free (&dkp->priv); - GNUNET_free (dkp); -} - - -/** - * Create a denomination key pair by registering the denomination in the DB. - * - * @param size the size of the denomination key - * @param now time to use for key generation, legal expiration will be 3h later. - * @param fees fees to use - * @return the denominaiton key pair; NULL upon error - */ -static struct DenomKeyPair * -create_denom_key_pair (unsigned int size, - struct GNUNET_TIME_Timestamp now, - const struct TALER_Amount *value, - const struct TALER_DenomFeeSet *fees) -{ - struct DenomKeyPair *dkp; - struct TALER_EXCHANGEDB_DenominationKey dki; - struct TALER_EXCHANGEDB_DenominationKeyInformation issue2; - - dkp = GNUNET_new (struct DenomKeyPair); - GNUNET_assert (GNUNET_OK == - TALER_denom_priv_create (&dkp->priv, - &dkp->pub, - TALER_DENOMINATION_RSA, - size)); - memset (&dki, - 0, - sizeof (struct TALER_EXCHANGEDB_DenominationKey)); - dki.denom_pub = dkp->pub; - dki.issue.start = now; - dki.issue.expire_withdraw - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add ( - now.abs_time, - GNUNET_TIME_UNIT_HOURS)); - dki.issue.expire_deposit - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add ( - now.abs_time, - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_HOURS, 2))); - dki.issue.expire_legal - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add ( - now.abs_time, - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_HOURS, 3))); - dki.issue.value = *value; - dki.issue.fees = *fees; - TALER_denom_pub_hash (&dkp->pub, - &dki.issue.denom_hash); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_denomination_info (plugin->cls, - &dki.denom_pub, - &dki.issue)) - { - GNUNET_break (0); - destroy_denom_key_pair (dkp); - return NULL; - } - memset (&issue2, 0, sizeof (issue2)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_denomination_info (plugin->cls, - &dki.issue.denom_hash, - &issue2)) - { - GNUNET_break (0); - destroy_denom_key_pair (dkp); - return NULL; - } - if (0 != GNUNET_memcmp (&dki.issue, - &issue2)) - { - GNUNET_break (0); - destroy_denom_key_pair (dkp); - return NULL; - } - return dkp; -} -/** - * Callback invoked with information about refunds applicable - * to a particular coin. - * - * @param cls closure with the `struct TALER_EXCHANGEDB_Refund *` we expect to get - * @param amount_with_fee amount being refunded - * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop - */ -static enum GNUNET_GenericReturnValue -check_refund_cb (void *cls, - const struct TALER_Amount *amount_with_fee) -{ - const struct TALER_EXCHANGEDB_Refund *refund = cls; - - if (0 != TALER_amount_cmp (amount_with_fee, - &refund->details.refund_amount)) - { - GNUNET_break (0); - result = 66; - } - return GNUNET_OK; -} - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure with config - */ - -static void -run (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - const uint32_t num_partitions = 10; - struct DenomKeyPair *dkp = NULL; - struct GNUNET_TIME_Timestamp ts; - struct TALER_EXCHANGEDB_Deposit *depos=NULL; - struct GNUNET_TIME_Timestamp deadline; - struct TALER_Amount value; - union TALER_DenominationBlindingKeyP bks; - struct TALER_CoinPubHashP c_hash; - struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; - struct TALER_ExchangeWithdrawValues alg_values = { - .cipher = TALER_DENOMINATION_RSA - }; - struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; - unsigned long long sqrs = 0; - struct TALER_EXCHANGEDB_Refund *ref=NULL; - unsigned int *perm; - unsigned long long duration_sq; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; - struct TALER_DenominationPublicKey *new_denom_pubs = NULL; - ref = GNUNET_new_array (ROUNDS +1, - struct TALER_EXCHANGEDB_Refund); - depos = GNUNET_new_array (ROUNDS +1, - struct TALER_EXCHANGEDB_Deposit); - ZR_BLK (&cbc); - - if (NULL == - (plugin = TALER_EXCHANGEDB_plugin_load (cfg))) - { - GNUNET_break (0); - result = 77; - return; - } - (void) plugin->drop_tables (plugin->cls); - if (GNUNET_OK != - plugin->create_tables (plugin->cls, - true, - num_partitions)) - { - GNUNET_break (0); - result = 77; - goto cleanup; - } - if (GNUNET_OK != - plugin->preflight (plugin->cls)) - { - GNUNET_break (0); - goto cleanup; - } - - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":1.000010", - &value)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.withdraw)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.deposit)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.refresh)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &fees.refund)); - GNUNET_assert (NUM_ROWS >= ROUNDS); - - ts = GNUNET_TIME_timestamp_get (); - deadline = GNUNET_TIME_timestamp_get (); - //DENOMINATION - { - //PAIR KEY LIST - new_dkp = GNUNET_new_array (MELT_NEW_COINS, - struct DenomKeyPair *); - //PUBLIC KEY LIST - new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_DenominationPublicKey); - //REFRESH REVEAL COIN LIST - revealed_coins - = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_EXCHANGEDB_RefreshRevealedCoin); - for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) - { - struct GNUNET_TIME_Timestamp now; - struct TALER_BlindedRsaPlanchet *rp; - struct TALER_BlindedPlanchet *bp; - - now = GNUNET_TIME_timestamp_get (); - //5 KEY PAIR - new_dkp[cnt] = create_denom_key_pair (RSA_KEY_SIZE, - now, - &value, - &fees); - GNUNET_assert (NULL != new_dkp[cnt]); - new_denom_pubs[cnt] = new_dkp[cnt]->pub; - ccoin = &revealed_coins[cnt]; - bp = &ccoin->blinded_planchet; - bp->cipher = TALER_DENOMINATION_RSA; - rp = &bp->details.rsa_blinded_planchet; - rp->blinded_msg_size = 1 + (size_t) GNUNET_CRYPTO_random_u64 ( - GNUNET_CRYPTO_QUALITY_WEAK, - (RSA_KEY_SIZE / 8) - 1); - rp->blinded_msg = GNUNET_malloc (rp->blinded_msg_size); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - rp->blinded_msg, - rp->blinded_msg_size); - TALER_denom_pub_hash (&new_dkp[cnt]->pub, - &ccoin->h_denom_pub); - ccoin->exchange_vals = alg_values; - TALER_coin_ev_hash (bp, - &ccoin->h_denom_pub, - &ccoin->coin_envelope_hash); - GNUNET_assert (GNUNET_OK == - TALER_denom_sign_blinded (&ccoin->coin_sig, - &new_dkp[cnt]->priv, - true, - bp)); - GNUNET_assert (GNUNET_OK == - TALER_coin_ev_hash (bp, - &cbc.denom_pub_hash, - &cbc.h_coin_envelope)); - GNUNET_assert ( - GNUNET_OK == - TALER_denom_sign_blinded ( - &cbc.sig, - &new_dkp[cnt]->priv, - false, - bp)); - } - } - - perm = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_NONCE, - NUM_ROWS); - // begin - FAILIF (GNUNET_OK != - plugin->start (plugin->cls, - "Transaction")); - for (unsigned int j=0; j< NUM_ROWS; j++) - { - unsigned int i = perm[j]; - unsigned int k = (unsigned int)rand()%5; - if (i >= ROUNDS) - i = ROUNDS; /* throw-away slot, do not keep around */ - RND_BLK (&coin_pub); - RND_BLK (&c_hash); - depos[i].deposit_fee = fees.deposit; - RND_BLK (&depos[i].coin.coin_pub); - TALER_denom_pub_hash (&new_dkp[k]->pub, - &depos[i].coin.denom_pub_hash); - // TALER_denom_pub_hash (&dkp->pub, - // &ref.coin.denom_pub_hash); - GNUNET_assert (GNUNET_OK == - TALER_denom_sig_unblind (&depos[i].coin.denom_sig, - &cbc.sig, - &bks, - &c_hash, - &alg_values, - &new_dkp[k]->pub)); - RND_BLK (&depos[i].merchant_pub); - RND_BLK (&depos[i].csig); - RND_BLK (&depos[i].h_contract_terms); - RND_BLK (&depos[i].wire_salt); - depos[i].amount_with_fee = value; - depos[i].refund_deadline = deadline; - depos[i].wire_deadline = deadline; - depos[i].receiver_wire_account = - "payto://iban/DE67830654080004822650?receiver-name=Test"; - TALER_merchant_wire_signature_hash ( - "payto://iban/DE67830654080004822650?receiver-name=Test", - &depos[i].wire_salt, - &h_wire_wt); - depos[i].timestamp = ts; - uint64_t known_coin_id; - {//ENSURE_COIN_KNOWN - - struct TALER_DenominationHashP dph; - struct TALER_AgeCommitmentHash agh; - FAILIF (TALER_EXCHANGEDB_CKS_ADDED != - plugin->ensure_coin_known (plugin->cls, - &depos[i].coin, - &known_coin_id, - &dph, - &agh)); - } - /*STORE INTO DEPOSIT*/ - { - struct GNUNET_TIME_Timestamp now; - now = GNUNET_TIME_timestamp_get (); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_deposit (plugin->cls, - now, - &depos[i])); - } - - /* 100% Refund */ - { - bool not_found; - bool refund_ok; - bool gone; - bool conflict; - ref[i].coin = depos[i].coin; - ref[i].details.merchant_pub = depos[i].merchant_pub; - RND_BLK(&ref[i].details.merchant_sig); - ref[i].details.h_contract_terms = depos[i].h_contract_terms; - ref[i].coin.coin_pub = depos[i].coin.coin_pub; - ref[i].details.rtransaction_id = i; - ref[i].details.refund_amount = value; - ref[i].details.refund_fee = fees.refund; - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->do_refund (plugin->cls, - &ref[i], - &fees.deposit, - known_coin_id, - ¬_found, - &refund_ok, - &gone, - &conflict)); - - /* FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_refund (plugin->cls, - &ref[i]));*/ - } - if (ROUNDS == i) - TALER_denom_sig_free (&depos[i].coin.denom_sig); - } - /* End of benchmark setup */ - GNUNET_free (perm); - // commit - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->commit (plugin->cls)); - for (unsigned int r = 0; r < ROUNDS; r++) - { - struct GNUNET_TIME_Absolute time; - struct GNUNET_TIME_Relative duration; - - time = GNUNET_TIME_absolute_get (); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->select_refunds_by_coin (plugin->cls, - &ref[r].coin.coin_pub, - &ref[r].details.merchant_pub, - &ref[r].details.h_contract_terms, - &check_refund_cb, - &ref[r])); - duration = GNUNET_TIME_absolute_get_duration (time); - times = GNUNET_TIME_relative_add (times, - duration); - duration_sq = duration.rel_value_us * duration.rel_value_us; - GNUNET_assert (duration_sq / duration.rel_value_us == duration.rel_value_us); - GNUNET_assert (sqrs + duration_sq >= sqrs); - sqrs += duration_sq; - } - /* evaluation of performance */ - { - struct GNUNET_TIME_Relative avg; - double avg_dbl; - double variance; - - avg = GNUNET_TIME_relative_divide (times, - ROUNDS); - avg_dbl = avg.rel_value_us; - variance = sqrs - (avg_dbl * avg_dbl * ROUNDS); - fprintf(stdout, - "%8llu ± %6.0f\n", - (unsigned long long) avg.rel_value_us, - sqrt (variance / (ROUNDS-1))); - } - result = 0; -drop: - GNUNET_break (GNUNET_OK == - plugin->drop_tables (plugin->cls)); -cleanup: - if (NULL != dkp) - destroy_denom_key_pair (dkp); - if (NULL != revealed_coins) - { - for (unsigned int cnt = 0; cnt < MELT_NEW_COINS; cnt++) - { - TALER_blinded_denom_sig_free (&revealed_coins[cnt].coin_sig); - TALER_blinded_planchet_free (&revealed_coins[cnt].blinded_planchet); - } - GNUNET_free (revealed_coins); - revealed_coins = NULL; - } - GNUNET_free (new_denom_pubs); - for (unsigned int cnt = 0; - (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]); - cnt++) - destroy_denom_key_pair (new_dkp[cnt]); - GNUNET_free (new_dkp); - for (unsigned int i=0; i< ROUNDS +1 ; i++) - { - TALER_denom_sig_free (&depos[i].coin.denom_sig); - } - GNUNET_free(depos); - GNUNET_free(ref); - dkp = NULL; - TALER_EXCHANGEDB_plugin_unload (plugin); - plugin = NULL; -} - - -int -main (int argc, - char *const argv[]) -{ - const char *plugin_name; - char *config_filename; - char *testname; - struct GNUNET_CONFIGURATION_Handle *cfg; - - (void) argc; - result = -1; - if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) - { - GNUNET_break (0); - return -1; - } - GNUNET_log_setup (argv[0], - "WARNING", - NULL); - plugin_name++; - (void) GNUNET_asprintf (&testname, - "test-exchange-db-%s", - plugin_name); - (void) GNUNET_asprintf (&config_filename, - "%s.conf", - testname); - fprintf (stdout, - "Using config: %s\n", - config_filename); - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_parse (cfg, - config_filename)) - { - GNUNET_break (0); - GNUNET_free (config_filename); - GNUNET_free (testname); - return 2; - } - GNUNET_SCHEDULER_run (&run, - cfg); - GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_free (config_filename); - GNUNET_free (testname); - return result; -} - - -/* end of test_exchangedb_by_j.c */ -- cgit v1.2.3 From 10cf3b3b65c89b3304a4d4f40c4b1a7eb5e47d14 Mon Sep 17 00:00:00 2001 From: Joseph Date: Mon, 23 Jan 2023 09:14:25 -0500 Subject: fix SQL syntax --- src/exchangedb/exchange_do_insert_aml_decision.sql | 2 +- src/exchangedb/exchange_do_insert_aml_officer.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql index 480736afa..b3f77c8c4 100644 --- a/src/exchangedb/exchange_do_insert_aml_decision.sql +++ b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -27,7 +27,7 @@ CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision( OUT out_last_date INT8) LANGUAGE plpgsql AS $$ - +BEGIN -- Check officer is eligible to make decisions. PERFORM FROM exchange.aml_staff diff --git a/src/exchangedb/exchange_do_insert_aml_officer.sql b/src/exchangedb/exchange_do_insert_aml_officer.sql index 72f813e89..5cb926c07 100644 --- a/src/exchangedb/exchange_do_insert_aml_officer.sql +++ b/src/exchangedb/exchange_do_insert_aml_officer.sql @@ -24,7 +24,7 @@ CREATE OR REPLACE FUNCTION exchange_do_insert_aml_officer( OUT out_last_change INT8) LANGUAGE plpgsql AS $$ - +BEGIN INSERT INTO exchange.aml_staff (decider_pub ,master_sig -- cgit v1.2.3