exchange

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

commit 6bae1938e96164364c177b13d4f6e7d39d9e378a
parent 2c81efb092330a1ef80b7a7f6742f7b011a10691
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 14 Jun 2026 16:08:11 +0200

implement protocol v37

Diffstat:
Msrc/exchange/taler-exchange-httpd.c | 4++++
Msrc/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.c | 28+++++++++++++++++++++++++++-
Msrc/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.h | 17+++++++++++++++++
Msrc/exchange/taler-exchange-httpd_get-config.h | 2+-
Msrc/exchangedb/meson.build | 1+
Asrc/exchangedb/select_wallet_merges.c | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/exchange-database/select_exchange_debit_transfers.h | 25+++++++++----------------
Asrc/include/exchange-database/select_wallet_merges.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/exchange_api_handle.c | 5++---
9 files changed, 327 insertions(+), 21 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -604,6 +604,10 @@ handle_get_aml (struct TEH_RequestContext *rc, .handler = &TEH_handler_aml_transfer_debit_get }, { + .op = "wallet-credit", + .handler = &TEH_handler_aml_wallet_credit_get + }, + { .op = NULL, .handler = NULL }, diff --git a/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.c b/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.c @@ -30,13 +30,15 @@ #include "taler-exchange-httpd_get-metrics.h" #include "exchange-database/select_exchange_credit_transfers.h" #include "exchange-database/select_exchange_debit_transfers.h" +#include "exchange-database/select_wallet_merges.h" enum TransferType { TT_CREDIT, TT_DEBIT, - TT_KYCAUTH + TT_KYCAUTH, + TT_WALLET }; @@ -184,6 +186,17 @@ aml_transfer_get ( transfers); query = "select_exchange_kycauth_transfers"; break; + case TT_WALLET: + qs = TALER_EXCHANGEDB_select_wallet_merges ( + TEH_pg, + &threshold, + offset, + limit, + have_payto ? &h_payto : NULL, + &record_cb, + transfers); + query = "select_wallet_merges"; + break; } switch (qs) { @@ -255,4 +268,17 @@ TEH_handler_aml_transfer_debit_get ( } +enum MHD_Result +TEH_handler_aml_wallet_credit_get ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]) +{ + return aml_transfer_get (rc, + officer_pub, + TT_WALLET, + args); +} + + /* end of taler-exchange-httpd_aml-decisions_get.c */ diff --git a/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.h b/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.h @@ -76,4 +76,21 @@ TEH_handler_aml_transfer_debit_get ( const char *const args[]); +/** + * Handle a GET "/aml/$OFFICER_PUB/wallet-credit" request. Parses the + * request details, checks the signatures and if appropriately authorized + * returns the matching wallet-credits. + * + * @param rc request context + * @param officer_pub public key of the AML officer who made the request + * @param args GET arguments (should be empty) + * @return MHD result code + */ +enum MHD_Result +TEH_handler_aml_wallet_credit_get ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const char *const args[]); + + #endif diff --git a/src/exchange/taler-exchange-httpd_get-config.h b/src/exchange/taler-exchange-httpd_get-config.h @@ -41,7 +41,7 @@ * * Returned via both /config and /keys endpoints. */ -#define EXCHANGE_PROTOCOL_VERSION "35:0:1" +#define EXCHANGE_PROTOCOL_VERSION "37:0:1" /** diff --git a/src/exchangedb/meson.build b/src/exchangedb/meson.build @@ -229,6 +229,7 @@ libtalerexchangedb = library( 'select_reserve_open_above_serial_id.c', 'select_reserves_in_above_serial_id_by_account.c', 'select_reserves_in_above_serial_id.c', + 'select_wallet_merges.c', 'select_wire_out_above_serial_id_by_account.c', 'select_wire_out_above_serial_id.c', 'select_withdrawals_above_serial_id.c', diff --git a/src/exchangedb/select_wallet_merges.c b/src/exchangedb/select_wallet_merges.c @@ -0,0 +1,196 @@ +/* + This file is part of TALER + Copyright (C) 2026 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 exchangedb/select_wallet_merges.c + * @brief Implementation of the select_wallet_merges function + * @author Christian Grothoff + */ +#include "taler/taler_pq_lib.h" +#include "exchange-database/select_wallet_merges.h" +#include "helper.h" + +/** + * Closure for #handle_aml_result. + */ +struct SelectTransferContext +{ + /** + * Function to call on each result. + */ + TALER_EXCHANGEDB_AmlTransferCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Plugin context. + */ + struct TALER_EXCHANGEDB_PostgresContext *pg; + + /** + * Set to #GNUNET_SYSERR on serious errors. + */ + enum GNUNET_GenericReturnValue status; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. Helper function + * for #TALER_EXCHANGEDB_select_wallet_merges(). + * + * @param cls closure of type `struct SelectTransferContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +handle_transfer_result (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct SelectTransferContext *stc = cls; + struct TALER_EXCHANGEDB_PostgresContext *pg = stc->pg; + + for (unsigned int i = 0; i<num_results; i++) + { + char *payto_uri; + uint64_t rowid; + struct GNUNET_TIME_Absolute execution_time; + struct TALER_Amount amount; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("serial_id", + &rowid), + GNUNET_PQ_result_spec_string ("payto_uri", + &payto_uri), + GNUNET_PQ_result_spec_absolute_time ("execution_time", + &execution_time), + TALER_PQ_RESULT_SPEC_AMOUNT ("amount", + &amount), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + stc->status = GNUNET_SYSERR; + return; + } + stc->cb (stc->cb_cls, + rowid, + payto_uri, + execution_time, + &amount); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TALER_EXCHANGEDB_select_wallet_merges ( + struct TALER_EXCHANGEDB_PostgresContext *pg, + const struct TALER_Amount *threshold, + uint64_t offset, + int64_t limit, + const struct TALER_NormalizedPaytoHashP *h_payto, + TALER_EXCHANGEDB_AmlTransferCallback cb, + void *cb_cls) +{ + struct SelectTransferContext stc = { + .pg = pg, + .cb = cb, + .cb_cls = cb_cls, + .status = GNUNET_OK + }; + uint64_t ulimit = (limit > 0) ? limit : -limit; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&offset), + GNUNET_PQ_query_param_uint64 (&ulimit), + TALER_PQ_query_param_amount (pg->conn, + threshold), + NULL != h_payto + ? GNUNET_PQ_query_param_auto_from_type (h_payto) + : GNUNET_PQ_query_param_null (), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "select_wallet_merges_inc", + "SELECT" + " pd.purse_decision_serial_id AS serial_id" + ",wt.payto_uri" + ",pd.action_timestamp AS execution_time" + ",pr.amount_with_fee AS amount" + " FROM purse_decision pd" + " JOIN purse_requests pr" + " ON (pr.purse_pub = pd.purse_pub)" + " JOIN purse_merges pm" + " ON (pm.purse_pub = pd.purse_pub)" + " JOIN kyc_targets kt" + " ON (kt.target_pub = pm.reserve_pub)" + " JOIN wire_targets wt" + " ON (wt.h_normalized_payto = kt.h_normalized_payto)" + " WHERE kt.is_wallet" + " AND NOT pd.refunded" + " AND (pd.purse_decision_serial_id > $1)" + " AND ( ($4::BYTEA IS NULL) OR (wt.h_normalized_payto=$4) )" + " AND ( ( (pr.amount_with_fee).val > ($3::taler_amount).val)" + " OR ( ( (pr.amount_with_fee).val >= ($3::taler_amount).val)" + " AND ( (pr.amount_with_fee).frac >= ($3::taler_amount).frac) ) )" + " ORDER BY pd.purse_decision_serial_id ASC" + " LIMIT $2"); + PREPARE (pg, + "select_wallet_merges_dec", + "SELECT" + " pd.purse_decision_serial_id AS serial_id" + ",wt.payto_uri" + ",pd.action_timestamp AS execution_time" + ",pr.amount_with_fee AS amount" + " FROM purse_decision pd" + " JOIN purse_requests pr" + " ON (pr.purse_pub = pd.purse_pub)" + " JOIN purse_merges pm" + " ON (pm.purse_pub = pd.purse_pub)" + " JOIN kyc_targets kt" + " ON (kt.target_pub = pm.reserve_pub)" + " JOIN wire_targets wt" + " ON (wt.h_normalized_payto = kt.h_normalized_payto)" + " WHERE kt.is_wallet" + " AND NOT pd.refunded" + " AND (pd.purse_decision_serial_id < $1)" + " AND ( ($4::BYTEA IS NULL) OR (wt.h_normalized_payto=$4) )" + " AND ( ( (pr.amount_with_fee).val > ($3::taler_amount).val)" + " OR ( ( (pr.amount_with_fee).val >= ($3::taler_amount).val)" + " AND ( (pr.amount_with_fee).frac >= ($3::taler_amount).frac) ) )" + " ORDER BY pd.purse_decision_serial_id DESC" + " LIMIT $2"); + qs = GNUNET_PQ_eval_prepared_multi_select ( + pg->conn, + (limit > 0) + ? "select_wallet_merges_inc" + : "select_wallet_merges_dec", + params, + &handle_transfer_result, + &stc); + if (GNUNET_OK != stc.status) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} diff --git a/src/include/exchange-database/select_exchange_debit_transfers.h b/src/include/exchange-database/select_exchange_debit_transfers.h @@ -26,7 +26,6 @@ #include "exchangedb_lib.h" -/* Callback typedefs */ /** * Callback that is given AML-relevant transfer data. * @@ -44,6 +43,7 @@ typedef void struct GNUNET_TIME_Absolute execution_time, const struct TALER_Amount *amount); + /** * Return AML-relevant wire transfer debit data. * @@ -57,21 +57,14 @@ typedef void * @return transaction status */ enum GNUNET_DB_QueryStatus -TALER_EXCHANGEDB_select_exchange_debit_transfers (struct - TALER_EXCHANGEDB_PostgresContext - * - pg, - const struct TALER_Amount * - threshold - , - uint64_t offset, - int64_t limit, - const struct - TALER_NormalizedPaytoHashP * - h_payto, - TALER_EXCHANGEDB_AmlTransferCallback - cb, - void *cb_cls); +TALER_EXCHANGEDB_select_exchange_debit_transfers ( + struct TALER_EXCHANGEDB_PostgresContext *pg, + const struct TALER_Amount *threshold, + uint64_t offset, + int64_t limit, + const struct TALER_NormalizedPaytoHashP *h_payto, + TALER_EXCHANGEDB_AmlTransferCallback cb, + void *cb_cls); #endif diff --git a/src/include/exchange-database/select_wallet_merges.h b/src/include/exchange-database/select_wallet_merges.h @@ -0,0 +1,70 @@ +/* + This file is part of TALER + Copyright (C) 2026 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 src/include/exchange-database/select_wallet_merges.h + * @brief implementation of the select_wallet_merges function + * @author Christian Grothoff + */ +#ifndef EXCHANGE_DATABASE_SELECT_WALLET_MERGES_H +#define EXCHANGE_DATABASE_SELECT_WALLET_MERGES_H + +#include "taler/taler_util.h" +#include "taler/taler_json_lib.h" +#include "exchangedb_lib.h" + + +/** + * Callback that is given AML-relevant transfer data. + * + * @param cls closure + * @param row_id current row in AML status table + * @param payto_uri account involved with the wire transfer + * @param execution_time when was the transfer made + * @param amount wire amount of the transfer + */ +typedef void +(*TALER_EXCHANGEDB_AmlTransferCallback)( + void *cls, + uint64_t row_id, + const char *payto_uri, + struct GNUNET_TIME_Absolute execution_time, + const struct TALER_Amount *amount); + + +/** + * Return AML-relevant merges (credits) into a wallet. + * + * @param pg the database context + * @param threshold minimum wire amount to return data for + * @param offset offset in table to filter by + * @param limit maximum number of entries to return, negative for descending + * @param h_payto account to filter transfer data by + * @param cb function to call on each result + * @param cb_cls closure to pass to @a cb + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TALER_EXCHANGEDB_select_wallet_merges ( + struct TALER_EXCHANGEDB_PostgresContext *pg, + const struct TALER_Amount *threshold, + uint64_t offset, + int64_t limit, + const struct TALER_NormalizedPaytoHashP *h_payto, + TALER_EXCHANGEDB_AmlTransferCallback cb, + void *cb_cls); + + +#endif diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c @@ -35,12 +35,12 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define EXCHANGE_PROTOCOL_CURRENT 35 +#define EXCHANGE_PROTOCOL_CURRENT 37 /** * How many versions are we backwards compatible with? */ -#define EXCHANGE_PROTOCOL_AGE 1 +#define EXCHANGE_PROTOCOL_AGE 3 /** * Set to 1 for extra debug logging. @@ -1244,7 +1244,6 @@ TALER_EXCHANGE_decode_keys_json_ ( key_data)); - /* * Parse the denomination keys, merging with the * possibly EXISTING array as required (/keys cherry picking).