exchange

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

commit 6d9c605740f77f629152977900138a37af168392
parent 47590e7159d5be7b252160ede32a9810f222e9f6
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 17 Jun 2025 17:02:44 +0200

draft API for #9027

Diffstat:
Mconfigure.ac | 1+
Msrc/include/Makefile.am | 4++--
Dsrc/include/gauger.h | 110-------------------------------------------------------------------------------
Asrc/include/taler-exchange/Makefile.am | 6++++++
Asrc/include/taler-exchange/aml_legitimizations_get.h | 401+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/taler-exchange/common.h | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/taler_exchange_service.h | 49+++----------------------------------------------
Msrc/include/taler_util.h | 4++--
8 files changed, 489 insertions(+), 160 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -534,6 +534,7 @@ AC_CONFIG_FILES([Makefile src/testing/Makefile src/benchmark/Makefile src/include/Makefile + src/include/taler-exchange/Makefile src/json/Makefile src/mhd/Makefile src/pq/Makefile diff --git a/src/include/Makefile.am b/src/include/Makefile.am @@ -1,4 +1,5 @@ # This Makefile.am is in the public domain +SUBDIRS = taler-exchange . talerincludedir = $(includedir)/taler talerinclude_HEADERS = \ @@ -37,5 +38,4 @@ talerinclude_HEADERS += \ endif EXTRA_DIST = \ - backoff.h \ - gauger.h + backoff.h diff --git a/src/include/gauger.h b/src/include/gauger.h @@ -1,110 +0,0 @@ -/** --------------------------------------------------------------------------- - * This software is in the public domain, furnished "as is", without technical - * support, and with no warranty, express or implied, as to its usefulness for - * any purpose. - * - * gauger.h - * Interface for C programs to log remotely to a gauger server - * - * Author: Bartlomiej Polot - * -------------------------------------------------------------------------*/ -#ifndef __GAUGER_H__ -#define __GAUGER_H__ - -#ifndef WINDOWS - -#include <unistd.h> -#include <stdio.h> -#include <sys/wait.h> - -#define GAUGER(category, counter, value, unit) \ - { \ - const char *__gauger_v[10]; \ - char __gauger_s[32]; \ - pid_t __gauger_p; \ - if (! (__gauger_p = fork ())) { \ - if (! fork ()) { \ - sprintf (__gauger_s,"%Lf", (long double) (value)); \ - __gauger_v[0] = "gauger"; \ - __gauger_v[1] = "-n"; \ - __gauger_v[2] = (char *) counter; \ - __gauger_v[3] = "-d"; \ - __gauger_v[4] = __gauger_s; \ - __gauger_v[5] = "-u"; \ - __gauger_v[6] = (char *) unit; \ - __gauger_v[7] = "-c"; \ - __gauger_v[8] = (char *) category; \ - __gauger_v[9] = (char *) NULL; \ - execvp ("gauger", (char *const *) __gauger_v); \ - perror ("gauger"); \ - _exit (1); \ - }else{ \ - _exit (0); \ - } \ - }else{ \ - waitpid (__gauger_p,NULL,0); \ - } \ - } - -#define GAUGER_ID(category, counter, value, unit, id) \ - { \ - char*__gauger_v[12]; \ - char __gauger_s[32]; \ - pid_t __gauger_p; \ - if (! (__gauger_p = fork ())) { \ - if (! fork ()) { \ - sprintf (__gauger_s,"%Lf", (long double) (value)); \ - __gauger_v[0] = "gauger"; \ - __gauger_v[1] = "-n"; \ - __gauger_v[2] = (char *) counter; \ - __gauger_v[3] = "-d"; \ - __gauger_v[4] = __gauger_s; \ - __gauger_v[5] = "-u"; \ - __gauger_v[6] = (char *) unit; \ - __gauger_v[7] = "-i"; \ - __gauger_v[8] = id; \ - __gauger_v[9] = "-c"; \ - __gauger_v[10] = (char *) category; \ - __gauger_v[11] = (char *) NULL; \ - execvp ("gauger",__gauger_v); \ - perror ("gauger"); \ - _exit (1); \ - }else{ \ - _exit (0); \ - } \ - }else{ \ - waitpid (__gauger_p,NULL,0); \ - } \ - } - -#else /* WINDOWS */ - -#include <stdlib.h> -#include <stdio.h> -#include <windef.h> - -#define GAUGER(category, counter, value, unit) \ - { \ - char __gauger_commandline[MAX_PATH]; \ - \ - snprintf (__gauger_commandline, MAX_PATH, \ - "gauger.py -n \"%s\" -d \"%Lf\" -u \"%s\" -c \"%s\"", \ - counter, (long double) (value), unit, category); \ - __gauger_commandline[MAX_PATH - 1] = '\0'; \ - system (__gauger_commandline); \ - } - -#define GAUGER_ID(category, counter, value, unit, id) \ - { \ - char __gauger_commandline[MAX_PATH]; \ - \ - snprintf (__gauger_commandline, MAX_PATH, \ - "gauger.py -n \"%s\" -d \"%Lf\" -u \"%s\" -i \"%s\" -c \"%s\"", \ - counter, (long double) (value), unit, id, category); \ - __gauger_commandline[MAX_PATH - 1] = '\0'; \ - system (__gauger_commandline); \ - } - -#endif // WINDOWS - -#endif diff --git a/src/include/taler-exchange/Makefile.am b/src/include/taler-exchange/Makefile.am @@ -0,0 +1,6 @@ +# This Makefile.am is in the public domain +talerincludedir = $(includedir)/taler/taler-exchange + +talerinclude_HEADERS = \ + aml_legitimizations_get.h \ + common.h diff --git a/src/include/taler-exchange/aml_legitimizations_get.h b/src/include/taler-exchange/aml_legitimizations_get.h @@ -0,0 +1,401 @@ +/* + 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_legitimizations_get.h + * @brief C interface for the GET /aml/$OFFICER_PUB/legitimizations endpoint + * @author Christian Grothoff + */ +#ifndef _TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_H +#define _TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_H + +#include <taler-exchange/common.h> + +/** + * Possible options we can set for the GET legitimizations request. + */ +enum TALER_EXCHANGE_AmlLegitimizationsGetOption +{ + /** + * End of list of options. + */ + TALER_EXCHANGE_AML_LEGITIMIZATIONS_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_LEGITIMIZATIONS_GET_OPTION_LIMIT, + + /** + * Row number threshold, defaults to INT64_MAX, namely + * the biggest row id possible in the database. + */ + TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_OFFSET, + + /** + * Filter by account using a normalized payto URI hash. + */ + TALER_EXCHANGE_AML_LEGITIMIZATIONS_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_LEGITIMIZATIONS_GET_OPTION_ACTIVE + +}; + + +/** + * Possible options we can set for the GET legitimizations request. + */ +struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue +{ + + /** + * Type of the option being set. + */ + enum TALER_EXCHANGE_AmlLegitimizationsGetOption option; + + /** + * Specific option value. + */ + union + { + + /** + * Value of if @e option is TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_LIMIT. + */ + int64_t limit; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_LEGITIMIZATIONS_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_LEGITIMIZATIONS_GET_OPTION_H_PAYTO. + */ + const struct TALER_NormalizedPaytoHashP *h_payto; + + /** + * Value of if @e option is TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_ACTIVE. + */ + enum TALER_EXCHANGE_YesNoAll active; + + } details; + +}; + + +/** + * Handle for an operation to GET /aml/$OFFICER_PUB/legitimizations. + */ +struct TALER_EXCHANGE_AmlLegitimizationsGetHandle; + + +/** + * Set up GET /aml/$OPUB/legitimizations 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_AmlLegitimizationsGetHandle * +TALER_EXCHANGE_aml_legitimizations_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_AmlLegitimizationsGetOptionValue + */ +#define TALER_EXCHANGE_aml_legitimizations_get_option_end_() \ + (const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_LEGITIMIZATIONS_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_AmlLegitimizationsGetOptionValue + */ +#define TALER_EXCHANGE_aml_legitimizations_get_option_limit_(l) \ + (const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_LEGITIMIZATIONS_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_AmlLegitimizationsGetOptionValue + */ +#define TALER_EXCHANGE_aml_legitimizations_get_option_offset_(o) \ + (const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_OFFSET, \ + .details.offset = (o) \ + } + + +/** + * Set filter on which account to filter legitimization measures by. + * + * @param p normalized payto URI hash of the account to filter by + * @return representation of the option as a struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue + */ +#define TALER_EXCHANGE_aml_legitimizations_get_option_h_payto_(p) \ + (const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_LEGITIMIZATIONS_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_AmlLegitimizationsGetOptionValue + */ +#define TALER_EXCHANGE_aml_legitimizations_get_option_active_(a) \ + (const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue) \ + { \ + .option = TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_ACTIVE, \ + .details.active = (o) \ + } + + +enum TALER_ErrorCode +TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, + unsigned int num_options, + struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue 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_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 ... 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 + */ +#define TALER_EXCHANGE_aml_legitimizations_get_set_options(algh,...) \ + TALER_EXCHANGE_aml_legitimizations_get_set_options_ ( \ + algh, \ + ((const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue[]) \ + {__VA_ARGS__, TALER_EXCHANGE_aml_legitimizations_get_option_end_ () } \ + ), \ + TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE) + + +/** + * 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. + */ + struct TALER_NormalizedPaytoHashP h_payto; + + /** + * Row ID of the measure in the exchange. + */ + uint64_t rowid; + + /** + * 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. + */ + struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureInformation *mi; + + /** + * 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 + * by a subsequent other measure taken)? + */ + bool is_finished; +}; + +/** + * Information returned from the exchange for a + * GET /aml/$OFFICER_PUB/legitimizations request. + */ +struct TALER_EXCHANGE_AmlLegitimizationsGetResult +{ + /** + * 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 measures array. + */ + size_t measures_length; + + /** + * Legitimization measures that apply to the account(s). + */ + const struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *measures; + + } ok; + + } details; +}; + + +#ifndef TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_AmlLegitimizationsGetCallback. + */ +#define TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE void +#endif + +/** + * Type of the function that receives the result of a + * GET /aml/$OFFICER_PUB/legitimizations request. + * + * @param cls closure + * @param result result returned by the HTTP server + */ +typedef void +(*TALER_EXCHANGE_AmlLegitimizationsGetCallback)( + TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_AmlLegitimizationsGetResult *result); + + +/** + * Start GET /aml/$OPUB/legitimizations operation. + * + * @param[in,out] algh operation to start + * @param cb function to call with the exchange's result + * @param cb_cls closure for @a cb + */ +void +TALER_EXCHANGE_aml_legitimizations_get_start ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh, + TALER_EXCHANGE_AmlLegitimizationsGetCallback cb, + TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cb_cls); + + +/** + * Cancel GET /aml/$OPUB/legitimizations operation. + * + * @param[in] algh operation to cancel + */ +void +TALER_EXCHANGE_aml_legitimizations_get_cancel ( + struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh); + + +#endif diff --git a/src/include/taler-exchange/common.h b/src/include/taler-exchange/common.h @@ -0,0 +1,74 @@ +/* + This file is part of TALER + Copyright (C) 2014-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/common.h + * @brief C interface of libtalerexchange, a C library to use exchange's HTTP API + * This library is not thread-safe, all APIs must only be used from a single thread. + * This library calls abort() if it runs out of memory. Be aware of these limitations. + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @author Christian Grothoff + * @author Özgür Kesim + */ +#ifndef _TALER_EXCHANGE__COMMON_H +#define _TALER_EXCHANGE__COMMON_H + +#include <jansson.h> +#include "taler_util.h" +#include "taler_error_codes.h" +#include <gnunet/gnunet_curl_lib.h> + + +/** + * General information about the HTTP response we obtained + * from the exchange for a request. + */ +struct TALER_EXCHANGE_HttpResponse +{ + + /** + * The complete JSON reply. NULL if we failed to parse the + * reply (too big, invalid JSON). + */ + const json_t *reply; + + /** + * Set to the human-readable 'hint' that is optionally + * provided by the exchange together with errors. NULL + * if no hint was provided or if there was no error. + */ + const char *hint; + + /** + * HTTP status code for the response. 0 if the + * HTTP request failed and we did not get any answer, or + * if the answer was invalid and we set @a ec to a + * client-side error code. + */ + unsigned int http_status; + + /** + * Taler error code. #TALER_EC_NONE if everything was + * OK. Usually set to the "code" field of an error + * response, but may be set to values created at the + * client side, for example when the response was + * not in JSON format or was otherwise ill-formed. + */ + enum TALER_ErrorCode ec; + +}; + + +#endif diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h @@ -25,17 +25,14 @@ #ifndef _TALER_EXCHANGE_SERVICE_H #define _TALER_EXCHANGE_SERVICE_H -#include <jansson.h> -#include "taler_util.h" -#include "taler_error_codes.h" -#include <gnunet/gnunet_curl_lib.h> - +#include <taler-exchange/common.h> +#include <taler-exchange/aml_legitimizations_get.h> /** * Version of the Taler Exchange API, in hex. * Thus 0.8.4-1 = 0x00080401. */ -#define TALER_EXCHANGE_API_VERSION 0x00100006 +#define TALER_EXCHANGE_API_VERSION 0x00100007 /** * Information returned when a client needs to pass @@ -772,46 +769,6 @@ enum TALER_EXCHANGE_VersionCompatibility /** - * General information about the HTTP response we obtained - * from the exchange for a request. - */ -struct TALER_EXCHANGE_HttpResponse -{ - - /** - * The complete JSON reply. NULL if we failed to parse the - * reply (too big, invalid JSON). - */ - const json_t *reply; - - /** - * Set to the human-readable 'hint' that is optionally - * provided by the exchange together with errors. NULL - * if no hint was provided or if there was no error. - */ - const char *hint; - - /** - * HTTP status code for the response. 0 if the - * HTTP request failed and we did not get any answer, or - * if the answer was invalid and we set @a ec to a - * client-side error code. - */ - unsigned int http_status; - - /** - * Taler error code. #TALER_EC_NONE if everything was - * OK. Usually set to the "code" field of an error - * response, but may be set to values created at the - * client side, for example when the response was - * not in JSON format or was otherwise ill-formed. - */ - enum TALER_ErrorCode ec; - -}; - - -/** * Response from /keys. */ struct TALER_EXCHANGE_KeysResponse diff --git a/src/include/taler_util.h b/src/include/taler_util.h @@ -790,8 +790,8 @@ enum TALER_EXCHANGE_YesNoAll TALER_EXCHANGE_YNA_YES = 1, /** - * If condition is no. - */ + * If condition is no. + */ TALER_EXCHANGE_YNA_NO = 2, /**