exchange

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

commit 9c672c08f021c4a58b10b42e931742e45f21cbad
parent b3c40059d72a690653ecffbd64370e66bf2fd5e7
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 17 Jun 2025 23:20:21 +0200

first version of exchange API to do GET on /aml//legitimizations (towards fixing #9027), plus some renames for consistency

Diffstat:
Msrc/exchange/Makefile.am | 2+-
Msrc/exchange/taler-exchange-httpd.c | 4++--
Asrc/exchange/taler-exchange-httpd_aml-legitimization-measures-get.c | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchange/taler-exchange-httpd_aml-legitimization-measures-get.h | 43+++++++++++++++++++++++++++++++++++++++++++
Dsrc/exchange/taler-exchange-httpd_legitimization-measures-get.c | 175-------------------------------------------------------------------------------
Dsrc/exchange/taler-exchange-httpd_legitimization-measures-get.h | 43-------------------------------------------
Msrc/include/taler-exchange/Makefile.am | 1+
Asrc/include/taler-exchange/aml_attributes_get.h | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/taler-exchange/aml_decisions_get.h | 483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/taler-exchange/aml_legitimizations_get.h | 115++++++++++++++++++++++++++++++++++---------------------------------------------
Asrc/include/taler-exchange/aml_transfers_kycauth_get.h | 342+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/taler-exchange/common.h | 7+++++++
Asrc/include/taler-exchange/reserves_get.h | 476+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/taler_exchange_service.h | 453+------------------------------------------------------------------------------
Msrc/lib/Makefile.am | 1+
Asrc/lib/exchange_api_aml_legitimizations_get.c | 501+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
16 files changed, 2375 insertions(+), 738 deletions(-)

diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am @@ -148,6 +148,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_aml-attributes-get.c taler-exchange-httpd_aml-attributes-get.h \ taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \ taler-exchange-httpd_aml-decisions-get.c \ + taler-exchange-httpd_aml-legitimization-measures-get.c taler-exchange-httpd_aml-legitimization-measures-get.h \ taler-exchange-httpd_aml-statistics-get.c taler-exchange-httpd_aml-statistics-get.h \ taler-exchange-httpd_aml-measures-get.c taler-exchange-httpd_aml-measures-get.h \ taler-exchange-httpd_aml-transfer-get.c taler-exchange-httpd_aml-transfer-get.h \ @@ -170,7 +171,6 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_kyc-upload.c taler-exchange-httpd_kyc-upload.h \ taler-exchange-httpd_kyc-wallet.c taler-exchange-httpd_kyc-wallet.h \ taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \ - taler-exchange-httpd_legitimization-measures-get.c taler-exchange-httpd_legitimization-measures-get.h \ taler-exchange-httpd_management.h \ taler-exchange-httpd_management_aml-officers.c \ taler-exchange-httpd_management_auditors.c \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -34,6 +34,7 @@ #include "taler-exchange-httpd_withdraw.h" #include "taler-exchange-httpd_aml-attributes-get.h" #include "taler-exchange-httpd_aml-decision.h" +#include "taler-exchange-httpd_aml-legitimization-measures-get.h" #include "taler-exchange-httpd_aml-statistics-get.h" #include "taler-exchange-httpd_aml-transfer-get.h" #include "taler-exchange-httpd_aml-measures-get.h" @@ -54,7 +55,6 @@ #include "taler-exchange-httpd_kyc-wallet.h" #include "taler-exchange-httpd_kyc-webhook.h" #include "taler-exchange-httpd_aml-decision.h" -#include "taler-exchange-httpd_legitimization-measures-get.h" #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_melt_v27.h" #include "taler-exchange-httpd_metrics.h" @@ -589,7 +589,7 @@ handle_get_aml (struct TEH_RequestContext *rc, }, { .op = "legitimizations", - .handler = &TEH_handler_legitimization_measures_get + .handler = &TEH_handler_aml_legitimization_measures_get }, { .op = "kyc-statistics", diff --git a/src/exchange/taler-exchange-httpd_aml-legitimization-measures-get.c b/src/exchange/taler-exchange-httpd_aml-legitimization-measures-get.c @@ -0,0 +1,175 @@ +/* + This file is part of TALER + Copyright (C) 2024 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-exchange-httpd_aml-legitimization-measures-get.c + * @brief Return information about legitimization measures + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include "taler_signatures.h" +#include "taler-exchange-httpd.h" +#include "taler_exchangedb_plugin.h" +#include "taler-exchange-httpd_aml-legitimization-measures-get.h" +#include "taler-exchange-httpd_metrics.h" + +/** + * Maximum number of measures we return in one request. + */ +#define MAX_MEASURES 1024 + +/** + * Return LEGITIMIZATION measure. + * + * @param cls closure + * @param h_payto hash of account the measure applies to + * @param start_time when was the process started + * @param jmeasures object of type ``LegitimizationMeasures`` + * @param is_finished true if the measure was finished + * @param measure_serial_id row ID of the measure in the exchange table + */ +static void +record_cb ( + void *cls, + struct TALER_NormalizedPaytoHashP *h_payto, + struct GNUNET_TIME_Timestamp start_time, + const json_t *jmeasures, + bool is_finished, + uint64_t measure_serial_id) +{ + json_t *measures = cls; + + GNUNET_assert ( + 0 == + json_array_append_new ( + measures, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_payto", + h_payto), + GNUNET_JSON_pack_uint64 ("rowid", + measure_serial_id), + GNUNET_JSON_pack_timestamp ("start_time", + start_time), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("measures", + (json_t *) jmeasures)), + GNUNET_JSON_pack_bool ("is_finished", + is_finished) + ))); +} + + +MHD_RESULT +TEH_handler_aml_legitimization_measures_get ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]) +{ + int64_t limit = -20; + uint64_t offset; + struct TALER_NormalizedPaytoHashP h_payto; + bool have_payto = false; + enum TALER_EXCHANGE_YesNoAll active_filter; + + if (NULL != args[0]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + args[0]); + } + TALER_MHD_parse_request_snumber (rc->connection, + "limit", + &limit); + if (limit > 0) + offset = 0; + else + offset = INT64_MAX; + TALER_MHD_parse_request_number (rc->connection, + "offset", + &offset); + if (offset > INT64_MAX) + { + GNUNET_break_op (0); /* broken client */ + offset = INT64_MAX; + } + TALER_MHD_parse_request_arg_auto (rc->connection, + "h_payto", + &h_payto, + have_payto); + TALER_MHD_parse_request_yna (rc->connection, + "active", + TALER_EXCHANGE_YNA_ALL, + &active_filter); + { + json_t *measures; + enum GNUNET_DB_QueryStatus qs; + + measures = json_array (); + GNUNET_assert (NULL != measures); + if (limit > MAX_MEASURES) + limit = MAX_MEASURES; + if (limit < -MAX_MEASURES) + limit = -MAX_MEASURES; + qs = TEH_plugin->select_aml_measures ( + TEH_plugin->cls, + have_payto + ? &h_payto + : NULL, + active_filter, + offset, + limit, + &record_cb, + measures); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + json_decref (measures); + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_aml_measures"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + json_decref (measures); + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("measures", + measures)); + } +} + + +/* end of taler-exchange-httpd_legitimization-measures_get.c */ diff --git a/src/exchange/taler-exchange-httpd_aml-legitimization-measures-get.h b/src/exchange/taler-exchange-httpd_aml-legitimization-measures-get.h @@ -0,0 +1,43 @@ +/* + 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-exchange-httpd_aml-legitimization-measures-get.h + * @brief Handle /aml/$OFFICER_PUB/legitimizations requests + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_AML_LEGITIMIZATION_MEASURES_GET_H +#define TALER_EXCHANGE_HTTPD_AML_LEGITIMIZATION_MEASURES_GET_H + +#include <microhttpd.h> +#include "taler-exchange-httpd.h" + +/** + * Handle a GET "/aml/$OFFICER_PUB/legitimizations" request. Parses the request + * details, checks the signatures and if appropriately authorized returns + * the matching measuress. + * + * @param rc request context + * @param officer_pub public key of the AML officer who made the request + * @param args GET arguments (should be the state) + * @return MHD result code + */ +MHD_RESULT +TEH_handler_aml_legitimization_measures_get ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]); + +#endif diff --git a/src/exchange/taler-exchange-httpd_legitimization-measures-get.c b/src/exchange/taler-exchange-httpd_legitimization-measures-get.c @@ -1,175 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_legitimization-measures-get.c - * @brief Return information about legitimization measures - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include "taler_json_lib.h" -#include "taler_mhd_lib.h" -#include "taler_signatures.h" -#include "taler-exchange-httpd.h" -#include "taler_exchangedb_plugin.h" -#include "taler-exchange-httpd_legitimization-measures-get.h" -#include "taler-exchange-httpd_metrics.h" - -/** - * Maximum number of measures we return in one request. - */ -#define MAX_MEASURES 1024 - -/** - * Return LEGITIMIZATION measure. - * - * @param cls closure - * @param h_payto hash of account the measure applies to - * @param start_time when was the process started - * @param jmeasures object of type ``LegitimizationMeasures`` - * @param is_finished true if the measure was finished - * @param measure_serial_id row ID of the measure in the exchange table - */ -static void -record_cb ( - void *cls, - struct TALER_NormalizedPaytoHashP *h_payto, - struct GNUNET_TIME_Timestamp start_time, - const json_t *jmeasures, - bool is_finished, - uint64_t measure_serial_id) -{ - json_t *measures = cls; - - GNUNET_assert ( - 0 == - json_array_append_new ( - measures, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("h_payto", - h_payto), - GNUNET_JSON_pack_uint64 ("rowid", - measure_serial_id), - GNUNET_JSON_pack_timestamp ("start_time", - start_time), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("measures", - (json_t *) jmeasures)), - GNUNET_JSON_pack_bool ("is_finished", - is_finished) - ))); -} - - -MHD_RESULT -TEH_handler_legitimization_measures_get ( - struct TEH_RequestContext *rc, - const struct TALER_AmlOfficerPublicKeyP *officer_pub, - const char *const args[]) -{ - int64_t limit = -20; - uint64_t offset; - struct TALER_NormalizedPaytoHashP h_payto; - bool have_payto = false; - enum TALER_EXCHANGE_YesNoAll active_filter; - - if (NULL != args[0]) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_GENERIC_ENDPOINT_UNKNOWN, - args[0]); - } - TALER_MHD_parse_request_snumber (rc->connection, - "limit", - &limit); - if (limit > 0) - offset = 0; - else - offset = INT64_MAX; - TALER_MHD_parse_request_number (rc->connection, - "offset", - &offset); - if (offset > INT64_MAX) - { - GNUNET_break_op (0); /* broken client */ - offset = INT64_MAX; - } - TALER_MHD_parse_request_arg_auto (rc->connection, - "h_payto", - &h_payto, - have_payto); - TALER_MHD_parse_request_yna (rc->connection, - "active", - TALER_EXCHANGE_YNA_ALL, - &active_filter); - { - json_t *measures; - enum GNUNET_DB_QueryStatus qs; - - measures = json_array (); - GNUNET_assert (NULL != measures); - if (limit > MAX_MEASURES) - limit = MAX_MEASURES; - if (limit < -MAX_MEASURES) - limit = -MAX_MEASURES; - qs = TEH_plugin->select_aml_measures ( - TEH_plugin->cls, - have_payto - ? &h_payto - : NULL, - active_filter, - offset, - limit, - &record_cb, - measures); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - json_decref (measures); - GNUNET_break (0); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "select_aml_measures"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - json_decref (measures); - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_array_steal ("measures", - measures)); - } -} - - -/* end of taler-exchange-httpd_legitimization-measures_get.c */ diff --git a/src/exchange/taler-exchange-httpd_legitimization-measures-get.h b/src/exchange/taler-exchange-httpd_legitimization-measures-get.h @@ -1,43 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-exchange-httpd_legitimization-measures-get.h - * @brief Handle /aml/$OFFICER_PUB/legitimizations requests - * @author Christian Grothoff - */ -#ifndef TALER_EXCHANGE_HTTPD_LEGITIMIZATION_MEASURES_GET_H -#define TALER_EXCHANGE_HTTPD_LEGITIMIZATION_MEASURES_GET_H - -#include <microhttpd.h> -#include "taler-exchange-httpd.h" - -/** - * Handle a GET "/aml/$OFFICER_PUB/legitimizations" request. Parses the request - * details, checks the signatures and if appropriately authorized returns - * the matching measuress. - * - * @param rc request context - * @param officer_pub public key of the AML officer who made the request - * @param args GET arguments (should be the state) - * @return MHD result code - */ -MHD_RESULT -TEH_handler_legitimization_measures_get ( - struct TEH_RequestContext *rc, - const struct TALER_AmlOfficerPublicKeyP *officer_pub, - const char *const args[]); - -#endif diff --git a/src/include/taler-exchange/Makefile.am b/src/include/taler-exchange/Makefile.am @@ -3,4 +3,5 @@ talerincludedir = $(includedir)/taler/taler-exchange talerinclude_HEADERS = \ aml_legitimizations_get.h \ + reserves_get.h \ common.h diff --git a/src/include/taler-exchange/aml_attributes_get.h b/src/include/taler-exchange/aml_attributes_get.h @@ -0,0 +1,292 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + 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 <http://www.gnu.org/licenses/> + */ +/** + * @file include/taler-exchange/aml_attributes_get.h + * @brief C interface for the GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO endpoint + * @author Christian Grothoff + */ +#ifndef _TALER_EXCHANGE__AML_ATTRIBUTES_GET_H +#define _TALER_EXCHANGE__AML_ATTRIBUTES_GET_H + +#include <taler-exchange/common.h> + +/** + * Possible options we can set for the GET attributes request. + */ +enum TALER_EXCHANGE_AmlAttributesGetOption +{ + /** + * End of list of options. + */ + TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_END = 0, + + /** + * Return at most N values, default is -20 to return + * the last 20 entries before start. Negative values + * to return before limit, positive to return after limit. + */ + TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_LIMIT, + + /** + * Row number threshold, defaults to INT64_MAX, namely + * the biggest row id possible in the database. + */ + TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_OFFSET + +}; + + +/** + * Possible options we can set for the GET attributes request. + */ +struct TALER_EXCHANGE_AmlAttributesGetOptionValue +{ + + /** + * Type of the option being set. + */ + enum TALER_EXCHANGE_AmlAttributesGetOption option; + + /** + * Specific option value. + */ + union + { + + /** + * Value of if @e option is TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_LIMIT. + */ + int64_t limit; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_OFFSET. + * Note that in practice the maximum value is INT64_MAX, even though + * this value is unsigned. + */ + uint64_t offset; + + } details; + +}; + + +/** + * Handle for an operation to GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO. + */ +struct TALER_EXCHANGE_AmlAttributesGetHandle; + + +/** + * Set up GET /aml/$OPUB/attributes/$H_NORMALIZED_PAYTO operation. + * Note that you must explicitly start the operation after + * possibly setting options. + * + * @param ctx the context + * @param url base URL of the exchange + * @param officer_priv private key of the officer + * @param h_payto normalized payto URI hash of the account to get attributes for + * @return handle to operation + */ +struct TALER_EXCHANGE_AmlAttributesGetHandle * +TALER_EXCHANGE_aml_attributes_get_create ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + const struct TALER_NormalizedPaytoHashP *h_payto); + + +/** + * Terminate the list of the options. + * + * @return the terminating object of struct TALER_EXCHANGE_AmlAttributesGetOptionValue + */ +#define TALER_EXCHANGE_aml_attributes_get_option_end_() \ + (const struct TALER_EXCHANGE_AmlAttributesGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_END \ + } + +/** + * Set limit @a l on the number of results to return. + * + * @param l limit on the number of results to return + * @return representation of the option as a struct TALER_EXCHANGE_AmlAttributesGetOptionValue + */ +#define TALER_EXCHANGE_aml_attributes_get_option_limit_(l) \ + (const struct TALER_EXCHANGE_AmlAttributesGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_LIMIT, \ + .details.limit = (l) \ + } + + +/** + * Set row offset from which to return results. + * + * @param o offset to use + * @return representation of the option as a struct TALER_EXCHANGE_AmlAttributesGetOptionValue + */ +#define TALER_EXCHANGE_aml_attributes_get_option_offset_(o) \ + (const struct TALER_EXCHANGE_AmlAttributesGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_ATTRIBUTES_GET_OPTION_OFFSET, \ + .details.offset = (o) \ + } + + +enum TALER_ErrorCode +TALER_EXCHANGE_aml_attributes_get_set_options_ ( + struct TALER_EXCHANGE_AmlAttributesGetHandle *aagh, + unsigned int num_options, + struct TALER_EXCHANGE_AmlAttributesGetOptionValue options[ + static num_options]); + + +/** + * Set the requested options for the operation. + * + * If any option fail other options may be or may be not applied. + * + * It should be used with helpers that creates required options, for example: + * + * TALER_EXCHANGE_aml_attributes_get_set_options ( + * aagh, + * TALER_EXCHANGE_aml_attributes_get_option_limit_(10)); + * + * @param aagh the request to set the options for + * @param ... the list of the options, each option must be created + * by helpers TALER_EXCHANGE_aml_attributes_get_option_NAME(VALUE) + * @return ::TALER_EC_NONE on success, + * error code otherwise + */ +#define TALER_EXCHANGE_aml_attributes_get_set_options(aagh,...) \ + TALER_EXCHANGE_aml_attributes_get_set_options_ ( \ + aagh, \ + ((const struct TALER_EXCHANGE_AmlAttributesGetOptionValue[]) \ + {__VA_ARGS__, TALER_EXCHANGE_aml_attributes_get_option_end_ () } \ + ), \ + TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE) + + +/** + * Entry in the set of KYC attribute collection events that are returned + * by the server in a single request. + */ +struct TALER_EXCHANGE_AmlAttributesGetCollectionEvent +{ + /** + * Row ID of the record. Used to filter by offset. + */ + uint64_t rowid; + + /** + * The collected KYC data. NULL if the attribute data could not + * be decrypted (internal error of the exchange, likely the + * attribute key was changed). + */ + const json_t *attributes; + + /** + * Time when the KYC data was collected. + */ + struct GNUNET_TIME_Timestamp collection_time; +}; + +/** + * Information returned from the exchange for a + * GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO request. + */ +struct TALER_EXCHANGE_AmlAttributesGetResult +{ + /** + * HTTP response data + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on the HTTP status code. + */ + union + { + + /** + * Details on #MHD_HTTP_OK. + */ + struct + { + /** + * Length of the @e details array. + */ + size_t details_length; + + /** + * Matching KYC attribute history of the account. + */ + const struct TALER_EXCHANGE_AmlAttributesGetCollectionEvent *details; + + } ok; + + } details; +}; + + +#ifndef TALER_EXCHANGE__AML_ATTRIBUTES_GET_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_AmlAttributesGetCallback. + */ +#define TALER_EXCHANGE__AML_ATTRIBUTES_GET_RESULT_CLOSURE void +#endif + +/** + * Type of the function that receives the result of a + * GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO request. + * + * @param cls closure + * @param result result returned by the HTTP server + */ +typedef void +(*TALER_EXCHANGE_AmlAttributesGetCallback)( + TALER_EXCHANGE__AML_ATTRIBUTES_GET_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_AmlAttributesGetResult *result); + + +/** + * Start GET /aml/$OPUB/attributes/$H_NORMALIZED_PAYTO operation. + * + * @param[in,out] aagh operation to start + * @param cb function to call with the exchange's result + * @param cb_cls closure for @a cb + * @return status code + */ +enum TALER_ErrorCode +TALER_EXCHANGE_aml_attributes_get_start ( + struct TALER_EXCHANGE_AmlAttributesGetHandle *aagh, + TALER_EXCHANGE_AmlAttributesGetCallback cb, + TALER_EXCHANGE__AML_ATTRIBUTES_GET_RESULT_CLOSURE *cb_cls); + + +/** + * Cancel GET /aml/$OPUB/attributes/$H_NORMALIZED_PAYTO operation. + * + * @param[in] aagh operation to cancel + */ +void +TALER_EXCHANGE_aml_attributes_get_cancel ( + struct TALER_EXCHANGE_AmlAttributesGetHandle *aagh); + + +#endif diff --git a/src/include/taler-exchange/aml_decisions_get.h b/src/include/taler-exchange/aml_decisions_get.h @@ -0,0 +1,483 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + 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 <http://www.gnu.org/licenses/> + */ +/** + * @file include/taler-exchange/aml_decisions_get.h + * @brief C interface for the GET /aml/$OFFICER_PUB/decisions endpoint + * @author Christian Grothoff + */ +#ifndef _TALER_EXCHANGE__AML_DECISIONS_GET_H +#define _TALER_EXCHANGE__AML_DECISIONS_GET_H + +#include <taler-exchange/common.h> + +/** + * Possible options we can set for the GET decisions request. + */ +enum TALER_EXCHANGE_AmlDecisionsGetOption +{ + /** + * End of list of options. + */ + TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_END = 0, + + /** + * Return at most N values, default is -20 to return + * the last 20 entries before start. Negative values + * to return before limit, positive to return after limit. + */ + TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_LIMIT, + + /** + * Row number threshold, defaults to INT64_MAX, namely + * the biggest row id possible in the database. + */ + TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_OFFSET, + + /** + * Filter by account using a normalized payto URI hash. + */ + TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_H_PAYTO, + + /** + * If set to #TALER_EXCHANGE_YNA_YES, only return active + * results, #TALER_EXCHANGE_YNA_NO, only return inactive + * results, #TALER_EXCHANGE_YNA_ALL, to return all + * decisions. Default is all. + */ + TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_ACTIVE, + + /** + * If set to #TALER_EXCHANGE_YNA_YES, only return accounts + * under investigation, #TALER_EXCHANGE_YNA_NO, only return + * accounts not under investigation, #TALER_EXCHANGE_YNA_ALL, + * to return all accounts. Default is all. + */ + TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_INVESTIGATION + +}; + + +/** + * Possible options we can set for the GET decisions request. + */ +struct TALER_EXCHANGE_AmlDecisionsGetOptionValue +{ + + /** + * Type of the option being set. + */ + enum TALER_EXCHANGE_AmlDecisionsGetOption option; + + /** + * Specific option value. + */ + union + { + + /** + * Value of if @e option is TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_LIMIT. + */ + int64_t limit; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_OFFSET. + * Note that in practice the maximum value is INT64_MAX, even though + * this value is unsigned. + */ + uint64_t offset; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_H_PAYTO. + */ + const struct TALER_NormalizedPaytoHashP *h_payto; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_ACTIVE. + */ + enum TALER_EXCHANGE_YesNoAll active; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_INVESTIGATION. + */ + enum TALER_EXCHANGE_YesNoAll investigation; + + } details; + +}; + + +/** + * Handle for an operation to GET /aml/$OFFICER_PUB/decisions. + */ +struct TALER_EXCHANGE_AmlDecisionsGetHandle; + + +/** + * Information about a KYC rule. + */ +struct TALER_EXCHANGE_AmlDecisionsGetKycRule +{ + /** + * Type of operation to which the rule applies. + */ + enum TALER_KYCLOGIC_KycTriggerEvent operation_type; + + /** + * Name of the configuration section this rule originates from. + * Can be NULL. + */ + const char *rule_name; + + /** + * The threshold amount that triggers the measures. + */ + struct TALER_Amount threshold; + + /** + * Over which duration should the threshold be computed. + */ + struct GNUNET_TIME_Relative timeframe; + + /** + * Length of the @e measures array. + */ + size_t measures_length; + + /** + * Array of names of measures to apply. + */ + const char **measures; + + /** + * Display priority for this rule. + */ + int64_t display_priority; + + /** + * True if the rule should be exposed to the client. + */ + bool exposed; + + /** + * True if all measures need to be satisfied (AND combinator), + * false if any measure is sufficient (OR combinator). + */ + bool is_and_combinator; +}; + + +/** + * Legitimization rule set associated with an AML decision. + */ +struct TALER_EXCHANGE_AmlDecisionsGetLegitimizationRuleSet +{ + /** + * When does this set of rules expire? + */ + struct GNUNET_TIME_Timestamp expiration_time; + + /** + * Name of the successor measure to apply when expiration time is reached. + * Can be NULL. + */ + const char *successor_measure; + + /** + * Length of the @e rules array. + */ + size_t rules_length; + + /** + * Array of KYC rules. + */ + struct TALER_EXCHANGE_AmlDecisionsGetKycRule *rules; + + /** + * Custom measures. + */ + const json_t *custom_measures; +}; + + +/** + * Entry in the set of AML decisions that are returned + * by the server in a single request. + */ +struct TALER_EXCHANGE_AmlDecisionsGetDecision +{ + /** + * Hash of the normalized payto:// URI of the account the + * decision applies to. + */ + struct TALER_NormalizedPaytoHashP h_payto; + + /** + * True if the underlying payto://-URI is for a wallet. + */ + bool is_wallet; + + /** + * Row ID of the decision in the exchange database. + */ + uint64_t rowid; + + /** + * Justification for the decision. Can be NULL. + */ + const char *justification; + + /** + * When was the decision made? + */ + struct GNUNET_TIME_Timestamp decision_time; + + /** + * Account properties. Can be NULL if no properties are set. + */ + const json_t *properties; + + /** + * Legitimization rules that apply to this account. + */ + struct TALER_EXCHANGE_AmlDecisionsGetLegitimizationRuleSet limits; + + /** + * True if the account is under investigation by AML staff. + */ + bool to_investigate; + + /** + * True if this is the active decision for the account. + */ + bool is_active; +}; + + +/** + * Information returned from the exchange for a + * GET /aml/$OFFICER_PUB/decisions request. + */ +struct TALER_EXCHANGE_AmlDecisionsGetResult +{ + /** + * HTTP response data + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on the HTTP status code. + */ + union + { + + /** + * Details on #MHD_HTTP_OK. + */ + struct + { + /** + * Length of the @e records array. + */ + size_t records_length; + + /** + * AML decisions. + */ + const struct TALER_EXCHANGE_AmlDecisionsGetDecision *records; + + } ok; + + } details; +}; + + +/** + * Set up GET /aml/$OPUB/decisions operation. + * Note that you must explicitly start the operation after + * possibly setting options. + * + * @param ctx the context + * @param url base URL of the exchange + * @param officer_priv private key of the officer + * @return handle to operation + */ +struct TALER_EXCHANGE_AmlDecisionsGetHandle * +TALER_EXCHANGE_aml_decisions_get_create ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv); + + +/** + * Terminate the list of the options. + * + * @return the terminating object of struct TALER_EXCHANGE_AmlDecisionsGetOptionValue + */ +#define TALER_EXCHANGE_aml_decisions_get_option_end_() \ + (const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_END \ + } + +/** + * Set limit @a l on the number of results to return. + * + * @param l limit on the number of results to return + * @return representation of the option as a struct TALER_EXCHANGE_AmlDecisionsGetOptionValue + */ +#define TALER_EXCHANGE_aml_decisions_get_option_limit_(l) \ + (const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_LIMIT, \ + .details.limit = (l) \ + } + + +/** + * Set row offset from which to return results. + * + * @param o offset to use + * @return representation of the option as a struct TALER_EXCHANGE_AmlDecisionsGetOptionValue + */ +#define TALER_EXCHANGE_aml_decisions_get_option_offset_(o) \ + (const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_OFFSET, \ + .details.offset = (o) \ + } + + +/** + * Set filter on which account to filter AML decisions by. + * + * @param p normalized payto URI hash of the account to filter by + * @return representation of the option as a struct TALER_EXCHANGE_AmlDecisionsGetOptionValue + */ +#define TALER_EXCHANGE_aml_decisions_get_option_h_payto_(p) \ + (const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_H_PAYTO, \ + .details.h_payto = (p) \ + } + +/** + * Set filter on active (or inactive) results. + * + * @param a activity filter to use + * @return representation of the option as a struct TALER_EXCHANGE_AmlDecisionsGetOptionValue + */ +#define TALER_EXCHANGE_aml_decisions_get_option_active_(a) \ + (const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_ACTIVE, \ + .details.active = (a) \ + } + +/** + * Set filter on investigation status. + * + * @param i investigation filter to use + * @return representation of the option as a struct TALER_EXCHANGE_AmlDecisionsGetOptionValue + */ +#define TALER_EXCHANGE_aml_decisions_get_option_investigation_(i) \ + (const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_DECISIONS_GET_OPTION_INVESTIGATION, \ + .details.investigation = (i) \ + } + + +enum TALER_ErrorCode +TALER_EXCHANGE_aml_decisions_get_set_options_ ( + struct TALER_EXCHANGE_AmlDecisionsGetHandle *adgh, + unsigned int num_options, + struct TALER_EXCHANGE_AmlDecisionsGetOptionValue options[ + static num_options]); + + +/** + * Set the requested options for the operation. + * + * If any option fail other options may be or may be not applied. + * + * It should be used with helpers that creates required options, for example: + * + * TALER_EXCHANGE_aml_decisions_get_set_options ( + * adgh, + * TALER_EXCHANGE_aml_decisions_get_option_h_payto_(&h_payto)); + * + * @param adgh the request to set the options for + * @param ... the list of the options, each option must be created + * by helpers TALER_EXCHANGE_aml_decisions_get_option_NAME(VALUE) + * @return ::TALER_EC_NONE on success, + * error code otherwise + */ +#define TALER_EXCHANGE_aml_decisions_get_set_options(adgh,...) \ + TALER_EXCHANGE_aml_decisions_get_set_options_ ( \ + adgh, \ + ((const struct TALER_EXCHANGE_AmlDecisionsGetOptionValue[]) \ + {__VA_ARGS__, TALER_EXCHANGE_aml_decisions_get_option_end_ () } \ + ), \ + TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE) + + +#ifndef TALER_EXCHANGE__AML_DECISIONS_GET_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_AmlDecisionsGetCallback. + */ +#define TALER_EXCHANGE__AML_DECISIONS_GET_RESULT_CLOSURE void +#endif + +/** + * Type of the function that receives the result of a + * GET /aml/$OFFICER_PUB/decisions request. + * + * @param cls closure + * @param result result returned by the HTTP server + */ +typedef void +(*TALER_EXCHANGE_AmlDecisionsGetCallback)( + TALER_EXCHANGE__AML_DECISIONS_GET_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_AmlDecisionsGetResult *result); + + +/** + * Start GET /aml/$OPUB/decisions operation. + * + * @param[in,out] adgh operation to start + * @param cb function to call with the exchange's result + * @param cb_cls closure for @a cb + * @return status code + */ +enum TALER_ErrorCode +TALER_EXCHANGE_aml_decisions_get_start ( + struct TALER_EXCHANGE_AmlDecisionsGetHandle *adgh, + TALER_EXCHANGE_AmlDecisionsGetCallback cb, + TALER_EXCHANGE__AML_DECISIONS_GET_RESULT_CLOSURE *cb_cls); + + +/** + * Cancel GET /aml/$OPUB/decisions operation. + * + * @param[in] adgh operation to cancel + */ +void +TALER_EXCHANGE_aml_decisions_get_cancel ( + struct TALER_EXCHANGE_AmlDecisionsGetHandle *adgh); + + +#endif diff --git a/src/include/taler-exchange/aml_legitimizations_get.h b/src/include/taler-exchange/aml_legitimizations_get.h @@ -191,11 +191,30 @@ TALER_EXCHANGE_aml_legitimizations_get_create ( (const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue) \ { \ .option = TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_ACTIVE, \ - .details.active = (o) \ + .details.active = (a) \ } -enum TALER_ErrorCode +/** + * Set the requested options for the operation. + * + * If any option fail other options may be or may be not applied. + * + * It should be used with helpers that creates required options, for example: + * + * TALER_EXCHANGE_aml_legitimizations_get_set_options ( + * algh, + * TALER_EXCHANGE_aml_legitimizations_get_option_h_payto_(&h_payto)); + * + * @param algh the request to set the options for + * @param num_options length of the @a options array + * @param options array of options, each option must be created + * by helpers TALER_EXCHANGE_aml_legitimizations_get_option_NAME(VALUE) + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error + */ +enum GNUNET_GenericReturnValue TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, unsigned int num_options, @@ -217,8 +236,9 @@ TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( * @param algh the request to set the options for * @param ... the list of the options, each option must be created * by helpers TALER_EXCHANGE_aml_legitimizations_get_option_NAME(VALUE) - * @return ::TALER_EC_NONE on success, - * error code otherwise + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure, + * #GNUNET_SYSERR on internal error */ #define TALER_EXCHANGE_aml_legitimizations_get_set_options(algh,...) \ TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( \ @@ -230,86 +250,34 @@ TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( /** - * Information about legitimization measures that apply to the - * customer at a particular point in time. - */ -struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureInformation -{ - /** - * Name of the KYC check. - */ - const char *check_name; - - /** - * Name of the AML program to run on the KYC check's result. - */ - const char *prog_name; - - /** - * Context for the check and AML program. Can be NULL! - */ - const json_t *context; - - /** - * Type of operation that triggered this measure, - * #TALER_KYCLOGIC_KYC_TRIGGER_NONE if unknown / not applicable. - */ - enum TALER_KYCLOGIC_KycTriggerEvent operation_type; - - /** - * True if the customer can voluntarily do this measure. - */ - bool voluntary; -}; - - -/** * Entry in the set of legitimization measures that are returned * by the server in a single request. */ struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails { /** - * Account the measure applies to. + * Hash of the normalized payto:// URI of the account the + * measure applies to. */ struct TALER_NormalizedPaytoHashP h_payto; /** - * Row ID of the measure in the exchange. + * Row ID of the measure in the exchange database. */ uint64_t rowid; /** - * When was the measure started/triggered? + * When was the measure started / triggered? */ struct GNUNET_TIME_Timestamp start_time; /** - * Length of the @e mi array. - */ - size_t mi_length; - - /** - * Array of legitimization measures that are to be applied. + * Object with the legitimization measures that are to be applied. */ - struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureInformation *mi; + const json_t *measures; /** - * True if the entries in the - * the @e mi are expected to be all satisfied, - * false if the customer has to chose one of them. - */ - bool is_and_combinator; - - /** - * Was some operation categorically forbidden and hence - * the customer cannot even do something here? If true, - * then the @e mi array will be empty. - */ - bool verboten; - - /** - * Was this measure finished by the customer (or obsoleted + * Was this measure finished by the customer? (or obsoleted * by a subsequent other measure taken)? */ bool is_finished; @@ -343,7 +311,7 @@ struct TALER_EXCHANGE_AmlLegitimizationsGetResult size_t measures_length; /** - * Legitimization measures that apply to the account(s). + * Legitimization measures. */ const struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *measures; @@ -380,12 +348,27 @@ typedef void * @param[in,out] algh operation to start * @param cb function to call with the exchange's result * @param cb_cls closure for @a cb + * @return status code */ -void +enum TALER_EXCHANGE_AmlLegitimizationsGetStartError +{ + /** + * Success. + */ + TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_OK = 0, + /** + * Only allowed to be started once. + */ + TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_AGAIN = 1, + /** + * Internal logic failure. + */ + TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_INTERNAL = 2, +} TALER_EXCHANGE_aml_legitimizations_get_start ( struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, TALER_EXCHANGE_AmlLegitimizationsGetCallback cb, - TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cb_cls); + TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE * cb_cls); /** diff --git a/src/include/taler-exchange/aml_transfers_kycauth_get.h b/src/include/taler-exchange/aml_transfers_kycauth_get.h @@ -0,0 +1,342 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + 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 <http://www.gnu.org/licenses/> + */ +/** + * @file include/taler-exchange/aml_transfers_kycauth_get.h + * @brief C interface for the GET /aml/$OFFICER_PUB/transfers-kycauth endpoint + * @author Christian Grothoff + */ +#ifndef _TALER_EXCHANGE__AML_TRANSFERS_KYCAUTH_GET_H +#define _TALER_EXCHANGE__AML_TRANSFERS_KYCAUTH_GET_H + +#include <taler-exchange/common.h> + +/** + * Possible options we can set for the GET transfers-kycauth request. + */ +enum TALER_EXCHANGE_AmlTransfersKycauthGetOption +{ + /** + * End of list of options. + */ + TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_END = 0, + + /** + * Minimum amount threshold to return. All amounts below + * the given threshold will be filtered. + */ + TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_THRESHOLD, + + /** + * Return at most N values, default is -20 to return + * the last 20 entries before start. Negative values + * to return before limit, positive to return after limit. + */ + TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_LIMIT, + + /** + * Row number threshold, defaults to INT64_MAX, namely + * the biggest row id possible in the database. + */ + TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_OFFSET, + + /** + * Filter by account using a normalized payto URI hash. + */ + TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_H_PAYTO + +}; + + +/** + * Possible options we can set for the GET transfers-kycauth request. + */ +struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue +{ + + /** + * Type of the option being set. + */ + enum TALER_EXCHANGE_AmlTransfersKycauthGetOption option; + + /** + * Specific option value. + */ + union + { + + /** + * Value of if @e option is TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_THRESHOLD. + */ + const struct TALER_Amount *threshold; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_LIMIT. + */ + int64_t limit; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_OFFSET. + * Note that in practice the maximum value is INT64_MAX, even though + * this value is unsigned. + */ + uint64_t offset; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_H_PAYTO. + */ + const struct TALER_NormalizedPaytoHashP *h_payto; + + } details; + +}; + + +/** + * Handle for an operation to GET /aml/$OFFICER_PUB/transfers-kycauth. + */ +struct TALER_EXCHANGE_AmlTransfersKycauthGetHandle; + + +/** + * Set up GET /aml/$OPUB/transfers-kycauth operation. + * Note that you must explicitly start the operation after + * possibly setting options. + * + * @param ctx the context + * @param url base URL of the exchange + * @param officer_priv private key of the officer + * @return handle to operation + */ +struct TALER_EXCHANGE_AmlTransfersKycauthGetHandle * +TALER_EXCHANGE_aml_transfers_kycauth_get_create ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv); + + +/** + * Terminate the list of the options. + * + * @return the terminating object of struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue + */ +#define TALER_EXCHANGE_aml_transfers_kycauth_get_option_end_() \ + (const struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_END \ + } + +/** + * Set minimum amount threshold @a t for transfers to return. + * + * @param t minimum amount threshold + * @return representation of the option as a struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue + */ +#define TALER_EXCHANGE_aml_transfers_kycauth_get_option_threshold_(t) \ + (const struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_THRESHOLD, \ + .details.threshold = (t) \ + } + +/** + * Set limit @a l on the number of results to return. + * + * @param l limit on the number of results to return + * @return representation of the option as a struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue + */ +#define TALER_EXCHANGE_aml_transfers_kycauth_get_option_limit_(l) \ + (const struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_LIMIT, \ + .details.limit = (l) \ + } + + +/** + * Set row offset from which to return results. + * + * @param o offset to use + * @return representation of the option as a struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue + */ +#define TALER_EXCHANGE_aml_transfers_kycauth_get_option_offset_(o) \ + (const struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_OFFSET, \ + .details.offset = (o) \ + } + + +/** + * Set filter on which account to filter transfers by. + * + * @param p normalized payto URI hash of the account to filter by + * @return representation of the option as a struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue + */ +#define TALER_EXCHANGE_aml_transfers_kycauth_get_option_h_payto_(p) \ + (const struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_TRANSFERS_KYCAUTH_GET_OPTION_H_PAYTO, \ + .details.h_payto = (p) \ + } + + +enum TALER_ErrorCode +TALER_EXCHANGE_aml_transfers_kycauth_get_set_options_ ( + struct TALER_EXCHANGE_AmlTransfersKycauthGetHandle *atkgh, + unsigned int num_options, + struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue options[ + static num_options]); + + +/** + * Set the requested options for the operation. + * + * If any option fail other options may be or may be not applied. + * + * It should be used with helpers that creates required options, for example: + * + * TALER_EXCHANGE_aml_transfers_kycauth_get_set_options ( + * atkgh, + * TALER_EXCHANGE_aml_transfers_kycauth_get_option_h_payto_(&h_payto)); + * + * @param atkgh the request to set the options for + * @param ... the list of the options, each option must be created + * by helpers TALER_EXCHANGE_aml_transfers_kycauth_get_option_NAME(VALUE) + * @return ::TALER_EC_NONE on success, + * error code otherwise + */ +#define TALER_EXCHANGE_aml_transfers_kycauth_get_set_options(atkgh,...) \ + TALER_EXCHANGE_aml_transfers_kycauth_get_set_options_ ( \ + atkgh, \ + ((const struct TALER_EXCHANGE_AmlTransfersKycauthGetOptionValue[]) \ + {__VA_ARGS__, TALER_EXCHANGE_aml_transfers_kycauth_get_option_end_ () \ + } \ + ), \ + TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE) + + +/** + * Entry in the set of transfers that are returned + * by the server in a single request. + */ +struct TALER_EXCHANGE_AmlTransfersKycauthGetTransferEntry +{ + /** + * Row ID of the record. Used to filter by offset. + */ + uint64_t rowid; + + /** + * payto://-URI of the other account. + */ + const char *payto_uri; + + /** + * The amount involved. + */ + struct TALER_Amount amount; + + /** + * Time when the transfer was made. + */ + struct GNUNET_TIME_Timestamp execution_time; +}; + +/** + * Information returned from the exchange for a + * GET /aml/$OFFICER_PUB/transfers-kycauth request. + */ +struct TALER_EXCHANGE_AmlTransfersKycauthGetResult +{ + /** + * HTTP response data + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on the HTTP status code. + */ + union + { + + /** + * Details on #MHD_HTTP_OK. + */ + struct + { + /** + * Length of the @e transfers array. + */ + size_t transfers_length; + + /** + * Matching transactions of the exchange. + */ + const struct TALER_EXCHANGE_AmlTransfersKycauthGetTransferEntry * + transfers; + + } ok; + + } details; +}; + + +#ifndef TALER_EXCHANGE__AML_TRANSFERS_KYCAUTH_GET_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_AmlTransfersKycauthGetCallback. + */ +#define TALER_EXCHANGE__AML_TRANSFERS_KYCAUTH_GET_RESULT_CLOSURE void +#endif + +/** + * Type of the function that receives the result of a + * GET /aml/$OFFICER_PUB/transfers-kycauth request. + * + * @param cls closure + * @param result result returned by the HTTP server + */ +typedef void +(*TALER_EXCHANGE_AmlTransfersKycauthGetCallback)( + TALER_EXCHANGE__AML_TRANSFERS_KYCAUTH_GET_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_AmlTransfersKycauthGetResult *result); + + +/** + * Start GET /aml/$OPUB/transfers-kycauth operation. + * + * @param[in,out] atkgh operation to start + * @param cb function to call with the exchange's result + * @param cb_cls closure for @a cb + */ +void +TALER_EXCHANGE_aml_transfers_kycauth_get_start ( + struct TALER_EXCHANGE_AmlTransfersKycauthGetHandle *atkgh, + TALER_EXCHANGE_AmlTransfersKycauthGetCallback cb, + TALER_EXCHANGE__AML_TRANSFERS_KYCAUTH_GET_RESULT_CLOSURE *cb_cls); + + +/** + * Cancel GET /aml/$OPUB/transfers-kycauth operation. + * + * @param[in] atkgh operation to cancel + */ +void +TALER_EXCHANGE_aml_transfers_kycauth_get_cancel ( + struct TALER_EXCHANGE_AmlTransfersKycauthGetHandle *atkgh); + + +#endif diff --git a/src/include/taler-exchange/common.h b/src/include/taler-exchange/common.h @@ -32,6 +32,13 @@ /** + * Maximum number of options that can be set in one set_options + * call. Used as a dummy for "infinite" in the varargs case. + */ +#define TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE SIZE_MAX + + +/** * General information about the HTTP response we obtained * from the exchange for a request. */ diff --git a/src/include/taler-exchange/reserves_get.h b/src/include/taler-exchange/reserves_get.h @@ -0,0 +1,476 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + 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 <http://www.gnu.org/licenses/> + */ +/** + * @file include/taler-exchange/reserves_get.h + * @brief C interface for the GET /reserves/$RESERVE_PUB endpoint + * @author Christian Grothoff + */ +#ifndef _TALER_EXCHANGE__RESERVES_GET_H +#define _TALER_EXCHANGE__RESERVES_GET_H + +#include <taler-exchange/common.h> + + +/** + * Ways how a reserve's balance may change. + */ +enum TALER_EXCHANGE_ReserveTransactionType +{ + + /** + * Deposit into the reserve. + */ + TALER_EXCHANGE_RTT_CREDIT, + + /** + * Withdrawal from the reserve. + */ + TALER_EXCHANGE_RTT_WITHDRAWAL, + + /** + * /recoup operation. + */ + TALER_EXCHANGE_RTT_RECOUP, + + /** + * Reserve closed operation. + */ + TALER_EXCHANGE_RTT_CLOSING, + + /** + * Reserve purse merge operation. + */ + TALER_EXCHANGE_RTT_MERGE, + + /** + * Reserve open request operation. + */ + TALER_EXCHANGE_RTT_OPEN, + + /** + * Reserve close request operation. + */ + TALER_EXCHANGE_RTT_CLOSE + +}; + + +/** + * @brief Entry in the reserve's transaction history. + */ +struct TALER_EXCHANGE_ReserveHistoryEntry +{ + + /** + * Type of the transaction. + */ + enum TALER_EXCHANGE_ReserveTransactionType type; + + /** + * Amount transferred (in or out). + */ + struct TALER_Amount amount; + + /** + * Details depending on @e type. + */ + union + { + + /** + * Information about a deposit that filled this reserve. + * @e type is #TALER_EXCHANGE_RTT_CREDIT. + */ + struct + { + /** + * Sender account payto://-URL of the incoming transfer. + */ + struct TALER_FullPayto sender_url; + + /** + * Information that uniquely identifies the wire transfer. + */ + uint64_t wire_reference; + + /** + * When did the wire transfer happen? + */ + struct GNUNET_TIME_Timestamp timestamp; + + } in_details; + + /** + * Information about withdraw operation with age-restriction. + * @e type is #TALER_EXCHANGE_RTT_WITHDRAWAL. + */ + struct + { + /** + * Signature authorizing the withdrawal for outgoing transaction. + */ + json_t *out_authorization_sig; + + /** + * The running hash over all hashes of blinded planchets of the withrdawal + */ + struct TALER_HashBlindedPlanchetsP planchets_h; + + /** + * If age restriction was required during the protocol + */ + bool age_restricted; + + /** + * Maximum age committed, if age_restricted is true + */ + uint8_t max_age; + + /** + * If @e age_restricted is true, the index that is not to be revealed + * after the initial commitment in /withdraw + */ + uint8_t noreveal_index; + + /** + * If @e age_restricted is true, the hash of the selected blinded planchets + */ + struct TALER_HashBlindedPlanchetsP selected_h; + + /** + * True, if no blinding_seed was provided. The value of + * @e blinding_seed is then zero. + */ + bool no_blinding_seed; + + /** + * In case of denominations of cipher type Clause-Schnorr, the + * seed for the prior call to /blinding-prepare + */ + struct TALER_BlindingMasterSeedP blinding_seed; + + /** + * Fee that was charged for the withdrawal. + */ + struct TALER_Amount fee; + + /** + * Number of coins withdrawn + */ + uint16_t num_coins; + + } withdraw; + + /** + * Information provided if the reserve was filled via /recoup. + * @e type is #TALER_EXCHANGE_RTT_RECOUP. + */ + struct + { + + /** + * Public key of the coin that was paid back. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * Signature of the coin of type + * #TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Public key of the exchange that was used for @e exchange_sig. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * When did the /recoup operation happen? + */ + struct GNUNET_TIME_Timestamp timestamp; + + } recoup_details; + + /** + * Information about a close operation of the reserve. + * @e type is #TALER_EXCHANGE_RTT_CLOSE. + */ + struct + { + /** + * Receiver account information for the outgoing wire transfer as a payto://-URI. + */ + struct TALER_FullPayto receiver_account_details; + + /** + * Wire transfer details for the outgoing wire transfer. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /** + * Signature of the coin of type + * #TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Public key of the exchange that was used for @e exchange_sig. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * When did the wire transfer happen? + */ + struct GNUNET_TIME_Timestamp timestamp; + + /** + * Fee that was charged for the closing. + */ + struct TALER_Amount fee; + + } close_details; + + /** + * Information about a merge operation on the reserve. + * @e type is #TALER_EXCHANGE_RTT_MERGE. + */ + struct + { + + /** + * Fee paid for the purse. + */ + struct TALER_Amount purse_fee; + + /** + * Hash over the contract. + */ + struct TALER_PrivateContractHashP h_contract_terms; + + /** + * Merge capability key. + */ + struct TALER_PurseMergePublicKeyP merge_pub; + + /** + * Purse public key. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * Signature by the reserve approving the merge. + */ + struct TALER_ReserveSignatureP reserve_sig; + + /** + * When was the merge made. + */ + struct GNUNET_TIME_Timestamp merge_timestamp; + + /** + * When was the purse set to expire. + */ + struct GNUNET_TIME_Timestamp purse_expiration; + + /** + * Minimum age required for depositing into the purse. + */ + uint32_t min_age; + + /** + * Flags of the purse. + */ + enum TALER_WalletAccountMergeFlags flags; + + /** + * True if the purse was actually merged, false + * if only the @e purse_fee was charged. + */ + bool merged; + + } merge_details; + + /** + * Information about an open request operation on the reserve. + * @e type is #TALER_EXCHANGE_RTT_OPEN. + */ + struct + { + + /** + * Signature by the reserve approving the open. + */ + struct TALER_ReserveSignatureP reserve_sig; + + /** + * Amount to be paid from the reserve balance to open + * the reserve. + */ + struct TALER_Amount reserve_payment; + + /** + * When was the request created. + */ + struct GNUNET_TIME_Timestamp request_timestamp; + + /** + * For how long should the reserve be kept open. + * (Determines amount to be paid.) + */ + struct GNUNET_TIME_Timestamp reserve_expiration; + + /** + * How many open purses should be included with the + * open reserve? + * (Determines amount to be paid.) + */ + uint32_t purse_limit; + + } open_request; + + /** + * Information about an close request operation on the reserve. + * @e type is #TALER_EXCHANGE_RTT_CLOSE. + */ + struct + { + + /** + * Signature by the reserve approving the close. + */ + struct TALER_ReserveSignatureP reserve_sig; + + /** + * When was the request created. + */ + struct GNUNET_TIME_Timestamp request_timestamp; + + /** + * Hash of the payto://-URI of the target account + * for the closure, or all zeros for the reserve + * origin account. + */ + struct TALER_FullPaytoHashP target_account_h_payto; + + } close_request; + + + } details; + +}; + + +/** + * @brief A /reserves/ GET Handle + */ +struct TALER_EXCHANGE_ReservesGetHandle; + + +/** + * @brief Reserve summary. + */ +struct TALER_EXCHANGE_ReserveSummary +{ + + /** + * High-level HTTP response details. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on @e hr.http_status. + */ + union + { + + /** + * Information returned on success, if + * @e hr.http_status is #MHD_HTTP_OK + */ + struct + { + + /** + * Reserve balance. + */ + struct TALER_Amount balance; + + /** + * payto://-URI of the last bank account that wired funds + * to the reserve, NULL for none (can happen if reserve + * was funded via P2P merge). + */ + struct TALER_FullPayto last_origin; + } ok; + + } details; + +}; + + +/** + * Callbacks of this type are used to serve the result of submitting a + * reserve status request to a exchange. + * + * @param cls closure + * @param rs HTTP response data + */ +typedef void +(*TALER_EXCHANGE_ReservesGetCallback) ( + void *cls, + const struct TALER_EXCHANGE_ReserveSummary *rs); + + +/** + * Submit a request to obtain the transaction history of a reserve + * from the exchange. Note that while we return the full response to the + * caller for further processing, we do already verify that the + * response is well-formed (i.e. that signatures included in the + * response are all valid and add up to the balance). If the exchange's + * reply is not well-formed, we return an HTTP status code of zero to + * @a cb. + * + * @param ctx curl context + * @param url exchange base URL + * @param reserve_pub public key of the reserve to inspect + * @param timeout how long to wait for an affirmative reply + * (enables long polling if the reserve does not yet exist) + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_EXCHANGE_ReservesGetHandle * +TALER_EXCHANGE_reserves_get ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_ReservePublicKeyP *reserve_pub, + struct GNUNET_TIME_Relative timeout, + TALER_EXCHANGE_ReservesGetCallback cb, + void *cb_cls); + + +/** + * Cancel a reserve GET request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param rgh the reserve request handle + */ +void +TALER_EXCHANGE_reserves_get_cancel ( + struct TALER_EXCHANGE_ReservesGetHandle *rgh); + +#endif diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h @@ -27,6 +27,7 @@ #include <taler-exchange/common.h> #include <taler-exchange/aml_legitimizations_get.h> +#include <taler-exchange/reserves_get.h> /** * Version of the Taler Exchange API, in hex. @@ -1896,457 +1897,7 @@ TALER_EXCHANGE_coins_history_cancel ( struct TALER_EXCHANGE_CoinsHistoryHandle *rsh); -/* ********************* GET /reserves/$RESERVE_PUB *********************** */ - -/** - * Ways how a reserve's balance may change. - */ -enum TALER_EXCHANGE_ReserveTransactionType -{ - - /** - * Deposit into the reserve. - */ - TALER_EXCHANGE_RTT_CREDIT, - - /** - * Withdrawal from the reserve. - */ - TALER_EXCHANGE_RTT_WITHDRAWAL, - - /** - * /recoup operation. - */ - TALER_EXCHANGE_RTT_RECOUP, - - /** - * Reserve closed operation. - */ - TALER_EXCHANGE_RTT_CLOSING, - - /** - * Reserve purse merge operation. - */ - TALER_EXCHANGE_RTT_MERGE, - - /** - * Reserve open request operation. - */ - TALER_EXCHANGE_RTT_OPEN, - - /** - * Reserve close request operation. - */ - TALER_EXCHANGE_RTT_CLOSE - -}; - - -/** - * @brief Entry in the reserve's transaction history. - */ -struct TALER_EXCHANGE_ReserveHistoryEntry -{ - - /** - * Type of the transaction. - */ - enum TALER_EXCHANGE_ReserveTransactionType type; - - /** - * Amount transferred (in or out). - */ - struct TALER_Amount amount; - - /** - * Details depending on @e type. - */ - union - { - - /** - * Information about a deposit that filled this reserve. - * @e type is #TALER_EXCHANGE_RTT_CREDIT. - */ - struct - { - /** - * Sender account payto://-URL of the incoming transfer. - */ - struct TALER_FullPayto sender_url; - - /** - * Information that uniquely identifies the wire transfer. - */ - uint64_t wire_reference; - - /** - * When did the wire transfer happen? - */ - struct GNUNET_TIME_Timestamp timestamp; - - } in_details; - - /** - * Information about withdraw operation with age-restriction. - * @e type is #TALER_EXCHANGE_RTT_WITHDRAWAL. - */ - struct - { - /** - * Signature authorizing the withdrawal for outgoing transaction. - */ - json_t *out_authorization_sig; - - /** - * The running hash over all hashes of blinded planchets of the withrdawal - */ - struct TALER_HashBlindedPlanchetsP planchets_h; - - /** - * If age restriction was required during the protocol - */ - bool age_restricted; - - /** - * Maximum age committed, if age_restricted is true - */ - uint8_t max_age; - - /** - * If @e age_restricted is true, the index that is not to be revealed - * after the initial commitment in /withdraw - */ - uint8_t noreveal_index; - - /** - * If @e age_restricted is true, the hash of the selected blinded planchets - */ - struct TALER_HashBlindedPlanchetsP selected_h; - - /** - * True, if no blinding_seed was provided. The value of - * @e blinding_seed is then zero. - */ - bool no_blinding_seed; - - /** - * In case of denominations of cipher type Clause-Schnorr, the - * seed for the prior call to /blinding-prepare - */ - struct TALER_BlindingMasterSeedP blinding_seed; - - /** - * Fee that was charged for the withdrawal. - */ - struct TALER_Amount fee; - - /** - * Number of coins withdrawn - */ - uint16_t num_coins; - - } withdraw; - - /** - * Information provided if the reserve was filled via /recoup. - * @e type is #TALER_EXCHANGE_RTT_RECOUP. - */ - struct - { - - /** - * Public key of the coin that was paid back. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Signature of the coin of type - * #TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP. - */ - struct TALER_ExchangeSignatureP exchange_sig; - - /** - * Public key of the exchange that was used for @e exchange_sig. - */ - struct TALER_ExchangePublicKeyP exchange_pub; - - /** - * When did the /recoup operation happen? - */ - struct GNUNET_TIME_Timestamp timestamp; - - } recoup_details; - - /** - * Information about a close operation of the reserve. - * @e type is #TALER_EXCHANGE_RTT_CLOSE. - */ - struct - { - /** - * Receiver account information for the outgoing wire transfer as a payto://-URI. - */ - struct TALER_FullPayto receiver_account_details; - - /** - * Wire transfer details for the outgoing wire transfer. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * Signature of the coin of type - * #TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED. - */ - struct TALER_ExchangeSignatureP exchange_sig; - - /** - * Public key of the exchange that was used for @e exchange_sig. - */ - struct TALER_ExchangePublicKeyP exchange_pub; - - /** - * When did the wire transfer happen? - */ - struct GNUNET_TIME_Timestamp timestamp; - - /** - * Fee that was charged for the closing. - */ - struct TALER_Amount fee; - - } close_details; - - /** - * Information about a merge operation on the reserve. - * @e type is #TALER_EXCHANGE_RTT_MERGE. - */ - struct - { - - /** - * Fee paid for the purse. - */ - struct TALER_Amount purse_fee; - - /** - * Hash over the contract. - */ - struct TALER_PrivateContractHashP h_contract_terms; - - /** - * Merge capability key. - */ - struct TALER_PurseMergePublicKeyP merge_pub; - - /** - * Purse public key. - */ - struct TALER_PurseContractPublicKeyP purse_pub; - - /** - * Signature by the reserve approving the merge. - */ - struct TALER_ReserveSignatureP reserve_sig; - - /** - * When was the merge made. - */ - struct GNUNET_TIME_Timestamp merge_timestamp; - - /** - * When was the purse set to expire. - */ - struct GNUNET_TIME_Timestamp purse_expiration; - - /** - * Minimum age required for depositing into the purse. - */ - uint32_t min_age; - - /** - * Flags of the purse. - */ - enum TALER_WalletAccountMergeFlags flags; - - /** - * True if the purse was actually merged, false - * if only the @e purse_fee was charged. - */ - bool merged; - - } merge_details; - - /** - * Information about an open request operation on the reserve. - * @e type is #TALER_EXCHANGE_RTT_OPEN. - */ - struct - { - - /** - * Signature by the reserve approving the open. - */ - struct TALER_ReserveSignatureP reserve_sig; - - /** - * Amount to be paid from the reserve balance to open - * the reserve. - */ - struct TALER_Amount reserve_payment; - - /** - * When was the request created. - */ - struct GNUNET_TIME_Timestamp request_timestamp; - - /** - * For how long should the reserve be kept open. - * (Determines amount to be paid.) - */ - struct GNUNET_TIME_Timestamp reserve_expiration; - - /** - * How many open purses should be included with the - * open reserve? - * (Determines amount to be paid.) - */ - uint32_t purse_limit; - - } open_request; - - /** - * Information about an close request operation on the reserve. - * @e type is #TALER_EXCHANGE_RTT_CLOSE. - */ - struct - { - - /** - * Signature by the reserve approving the close. - */ - struct TALER_ReserveSignatureP reserve_sig; - - /** - * When was the request created. - */ - struct GNUNET_TIME_Timestamp request_timestamp; - - /** - * Hash of the payto://-URI of the target account - * for the closure, or all zeros for the reserve - * origin account. - */ - struct TALER_FullPaytoHashP target_account_h_payto; - - } close_request; - - - } details; - -}; - - -/** - * @brief A /reserves/ GET Handle - */ -struct TALER_EXCHANGE_ReservesGetHandle; - - -/** - * @brief Reserve summary. - */ -struct TALER_EXCHANGE_ReserveSummary -{ - - /** - * High-level HTTP response details. - */ - struct TALER_EXCHANGE_HttpResponse hr; - - /** - * Details depending on @e hr.http_status. - */ - union - { - - /** - * Information returned on success, if - * @e hr.http_status is #MHD_HTTP_OK - */ - struct - { - - /** - * Reserve balance. - */ - struct TALER_Amount balance; - - /** - * payto://-URI of the last bank account that wired funds - * to the reserve, NULL for none (can happen if reserve - * was funded via P2P merge). - */ - struct TALER_FullPayto last_origin; - } ok; - - } details; - -}; - - -/** - * Callbacks of this type are used to serve the result of submitting a - * reserve status request to a exchange. - * - * @param cls closure - * @param rs HTTP response data - */ -typedef void -(*TALER_EXCHANGE_ReservesGetCallback) ( - void *cls, - const struct TALER_EXCHANGE_ReserveSummary *rs); - - -/** - * Submit a request to obtain the transaction history of a reserve - * from the exchange. Note that while we return the full response to the - * caller for further processing, we do already verify that the - * response is well-formed (i.e. that signatures included in the - * response are all valid and add up to the balance). If the exchange's - * reply is not well-formed, we return an HTTP status code of zero to - * @a cb. - * - * @param ctx curl context - * @param url exchange base URL - * @param reserve_pub public key of the reserve to inspect - * @param timeout how long to wait for an affirmative reply - * (enables long polling if the reserve does not yet exist) - * @param cb the callback to call when a reply for this request is available - * @param cb_cls closure for the above callback - * @return a handle for this request; NULL if the inputs are invalid (i.e. - * signatures fail to verify). In this case, the callback is not called. - */ -struct TALER_EXCHANGE_ReservesGetHandle * -TALER_EXCHANGE_reserves_get ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct GNUNET_TIME_Relative timeout, - TALER_EXCHANGE_ReservesGetCallback cb, - void *cb_cls); - - -/** - * Cancel a reserve GET request. This function cannot be used - * on a request handle if a response is already served for it. - * - * @param rgh the reserve request handle - */ -void -TALER_EXCHANGE_reserves_get_cancel ( - struct TALER_EXCHANGE_ReservesGetHandle *rgh); - +/* ********************* GET /reserves/$RESERVE_PUB/history *********************** */ /** * @brief A /reserves/$RID/history Handle diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -22,6 +22,7 @@ libtalerexchange_la_LDFLAGS = \ -no-undefined libtalerexchange_la_SOURCES = \ exchange_api_add_aml_decision.c \ + exchange_api_aml_legitimizations_get.c \ exchange_api_auditor_add_denomination.c \ exchange_api_batch_deposit.c \ exchange_api_blinding_prepare.c \ diff --git a/src/lib/exchange_api_aml_legitimizations_get.c b/src/lib/exchange_api_aml_legitimizations_get.c @@ -0,0 +1,501 @@ +/* + This file is part of TALER + Copyright (C) 2025 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 + <http://www.gnu.org/licenses/> +*/ +/** + * @file lib/exchange_api_aml_legitimizations_get.c + * @brief Implementation of the GET /aml/$OFFICER_PUB/legitimizations requests + * @author Christian Grothoff + */ +#include "platform.h" +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_exchange_service.h" +#include "taler_json_lib.h" +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "exchange_api_curl_defaults.h" + + +/** + * Handle for an operation to GET /aml/$OFFICER_PUB/legitimizations. + */ +struct TALER_EXCHANGE_AmlLegitimizationsGetHandle +{ + + /** + * The exchange base URL for this request. + */ + char *exchange_base_url; + + /** + * Our execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Signature of the AML officer. + */ + struct TALER_AmlOfficerSignatureP officer_sig; + + /** + * Public key of the AML officer. + */ + struct TALER_AmlOfficerPublicKeyP officer_pub; + + /** + * Function to call with the result. + */ + TALER_EXCHANGE_AmlLegitimizationsGetCallback cb; + + /** + * Closure for @a cb. + */ + TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cb_cls; + + /** + * The url for this request. + */ + char *url; + + /** + * HTTP headers for the job. + */ + struct curl_slist *job_headers; + + /** + * Request options. + */ + struct + { + /** + * Limit on number of results. + */ + int64_t limit; + + /** + * Row offset from which to return results. + */ + uint64_t offset; + + /** + * Hash of payto URI to filter by, NULL for no filter. + */ + const struct TALER_NormalizedPaytoHashP *h_payto; + + /** + * Activity filter. + */ + enum TALER_EXCHANGE_YesNoAll active; + + } options; + +}; + + +/** + * Parse a single measure details entry from JSON. + * + * @param md_json JSON object to parse + * @param[out] md where to store the result + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_measure_details ( + const json_t *md_json, + struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *md) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_payto", + &md->h_payto), + GNUNET_JSON_spec_uint64 ("rowid", + &md->rowid), + GNUNET_JSON_spec_timestamp ("start_time", + &md->start_time), + GNUNET_JSON_spec_object_const ("measures", + &md->measures), + GNUNET_JSON_spec_bool ("is_finished", + &md->is_finished), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (md_json, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * We received an #MHD_HTTP_OK status code. Handle the JSON + * response. + * + * @param algh handle of the request + * @param j JSON response + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_aml_legitimizations_get_ok ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, + const json_t *j) +{ + struct TALER_EXCHANGE_AmlLegitimizationsGetResult result = { + .hr.reply = j, + .hr.http_status = MHD_HTTP_OK + }; + const json_t *measures_array; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("measures", + &measures_array), + GNUNET_JSON_spec_end () + }; + struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *measures; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + result.details.ok.measures_length = json_array_size (measures_array); + if (0 == result.details.ok.measures_length) + { + measures = NULL; + } + else + { + measures + = GNUNET_new_array ( + result.details.ok.measures_length, + struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails); + } + for (size_t i = 0; i < result.details.ok.measures_length; i++) + { + const json_t *measure_json = json_array_get (measures_array, + i); + + if (GNUNET_OK != + parse_measure_details (measure_json, + &measures[i])) + { + GNUNET_free (measures); + return GNUNET_SYSERR; + } + } + result.details.ok.measures = measures; + algh->cb (algh->cb_cls, + &result); + algh->cb = NULL; + GNUNET_free (measures); + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /aml/$OFFICER_PUB/legitimizations GET request. + * + * @param cls the `struct TALER_EXCHANGE_AmlLegitimizationsGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_aml_legitimizations_get_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh = cls; + const json_t *j = response; + struct TALER_EXCHANGE_AmlLegitimizationsGetResult result = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + algh->job = NULL; + switch (response_code) + { + case 0: + result.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + handle_aml_legitimizations_get_ok (algh, + j)) + { + result.hr.http_status = 0; + result.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the exchange is buggy + (or API version conflict); just pass JSON reply to the application */ + result.hr.ec = TALER_JSON_get_error_code (j); + result.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_UNAUTHORIZED: + /* Invalid officer credentials */ + result.hr.ec = TALER_JSON_get_error_code (j); + result.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_FORBIDDEN: + /* Officer not authorized for this operation */ + result.hr.ec = TALER_JSON_get_error_code (j); + result.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Officer not found */ + result.hr.ec = TALER_JSON_get_error_code (j); + result.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + result.hr.ec = TALER_JSON_get_error_code (j); + result.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + result.hr.ec = TALER_JSON_get_error_code (j); + result.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for GET %s\n", + (unsigned int) response_code, + (int) result.hr.ec, + algh->url); + break; + } + if (NULL != algh->cb) + { + algh->cb (algh->cb_cls, + &result); + algh->cb = NULL; + } + TALER_EXCHANGE_aml_legitimizations_get_cancel (algh); +} + + +struct TALER_EXCHANGE_AmlLegitimizationsGetHandle * +TALER_EXCHANGE_aml_legitimizations_get_create ( + struct GNUNET_CURL_Context *ctx, + const char *exchange_base_url, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv) +{ + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh; + + algh = GNUNET_new (struct TALER_EXCHANGE_AmlLegitimizationsGetHandle); + algh->ctx = ctx; + algh->exchange_base_url = GNUNET_strdup (exchange_base_url); + GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, + &algh->officer_pub.eddsa_pub); + TALER_officer_aml_query_sign (officer_priv, + &algh->officer_sig); + algh->options.limit = -20; /* Default to last 20 entries */ + algh->options.offset = UINT64_MAX; /* Default to maximum row id */ + algh->options.active = TALER_EXCHANGE_YNA_ALL; /* Default to all */ + return algh; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, + unsigned int num_options, + struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue options[ + static num_options]) +{ + for (unsigned int i = 0; i < num_options; i++) + { + switch (options[i].option) + { + case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_END: + return GNUNET_OK; + case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_LIMIT: + algh->options.limit = options[i].details.limit; + break; + case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_OFFSET: + algh->options.offset = options[i].details.offset; + break; + case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_H_PAYTO: + algh->options.h_payto = options[i].details.h_payto; + break; + case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_ACTIVE: + algh->options.active = options[i].details.active; + break; + default: + GNUNET_break (0); + return GNUNET_NO; + } + } + return GNUNET_OK; +} + + +enum TALER_EXCHANGE_AmlLegitimizationsGetStartError +TALER_EXCHANGE_aml_legitimizations_get_start ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, + TALER_EXCHANGE_AmlLegitimizationsGetCallback cb, + TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cb_cls) +{ + char officer_pub_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2]; + char arg_str[sizeof (officer_pub_str) + 64]; + char limit_str[24]; + char offset_str[24]; + char paytoh_str[sizeof (struct TALER_NormalizedPaytoHashP) * 2]; + + if (NULL != algh->job) + { + GNUNET_break (0); + return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_AGAIN; + } + algh->cb = cb; + algh->cb_cls = cb_cls; + { + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &algh->officer_pub, + sizeof (algh->officer_pub), + officer_pub_str, + sizeof (officer_pub_str)); + *end = '\0'; + } + if (NULL != algh->options.h_payto) + { + char *end; + + end = GNUNET_STRINGS_data_to_string ( + algh->options.h_payto, + sizeof (struct TALER_NormalizedPaytoHashP), + paytoh_str, + sizeof (paytoh_str)); + *end = '\0'; + } + /* Build query parameters */ + GNUNET_snprintf (offset_str, + sizeof (offset_str), + "%llu", + (unsigned long long) algh->options.offset); + GNUNET_snprintf (limit_str, + sizeof (limit_str), + "%lld", + (long long) algh->options.limit); + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "aml/%s/legitimizations", + officer_pub_str); + algh->url = TALER_url_join (algh->exchange_base_url, + arg_str, + "limit", + limit_str, + "offset", + offset_str, + "h_payto", + NULL == algh->options.h_payto + ? NULL + : offset_str, + "active", + TALER_EXCHANGE_YNA_ALL == algh->options.active + ? NULL + : TALER_yna_to_string (algh->options.active), + NULL); + if (NULL == algh->url) + { + GNUNET_break (0); + return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_INTERNAL; + } + + { + CURL *eh; + + eh = TALER_EXCHANGE_curl_easy_get_ (algh->url); + if (NULL == eh) + { + GNUNET_break (0); + GNUNET_free (algh->url); + algh->url = NULL; + return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_INTERNAL; + } + + /* Add authentication header for AML officer */ + { + char *hdr; + char sig_str[sizeof (algh->officer_sig) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string ( + &algh->officer_sig, + sizeof (algh->officer_sig), + sig_str, + sizeof (sig_str)); + *end = '\0'; + GNUNET_asprintf (&hdr, + "%s: %s", + TALER_AML_OFFICER_SIGNATURE_HEADER, + sig_str); + algh->job_headers = curl_slist_append (NULL, + hdr); + GNUNET_free (hdr); + GNUNET_break (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_HTTPHEADER, + algh->job_headers)); + } + algh->job + = GNUNET_CURL_job_add ( + algh->ctx, + eh, + &handle_aml_legitimizations_get_finished, + algh); + } + return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_OK; +} + + +void +TALER_EXCHANGE_aml_legitimizations_get_cancel ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh) +{ + if (NULL != algh->job) + { + GNUNET_CURL_job_cancel (algh->job); + algh->job = NULL; + } + curl_slist_free_all (algh->job_headers); + GNUNET_free (algh->exchange_base_url); + GNUNET_free (algh->url); + GNUNET_free (algh); +} + + +/* end of exchange_api_aml_legitimizations_get.c */