exchange

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

commit 4915fe568e8b7e7bf5e5cd4eecacf3cdefa10d04
parent 961211bf896c08ba9ff444b4de541b2dedae4395
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Tue, 17 Dec 2024 15:14:21 +0100

work on #9386

Diffstat:
Msrc/auditor/taler-helper-auditor-wire-credit.c | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/auditordb/Makefile.am | 1+
Msrc/auditordb/pg_get_reserve_in_inconsistency.c | 5+----
Asrc/auditordb/pg_lookup_reserve_in_inconsistency.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/auditordb/pg_lookup_reserve_in_inconsistency.h | 37+++++++++++++++++++++++++++++++++++++
Msrc/auditordb/plugin_auditordb_postgres.c | 6+++++-
Msrc/bank-lib/Makefile.am | 2+-
Msrc/bank-lib/bank_api_credit.c | 21+++++++++++++++++++++
Msrc/bank-lib/fakebank_twg.c | 2+-
Msrc/exchangedb/test-exchange-db-postgres.conf | 2++
Msrc/include/taler_auditordb_plugin.h | 24+++++++++++++++++++++++-
Msrc/include/taler_bank_service.h | 5+++++
12 files changed, 308 insertions(+), 11 deletions(-)

diff --git a/src/auditor/taler-helper-auditor-wire-credit.c b/src/auditor/taler-helper-auditor-wire-credit.c @@ -169,6 +169,11 @@ static TALER_ARL_DEF_AB (total_misattribution_in); static TALER_ARL_DEF_AB (total_wire_in); /** + * Total wire credit fees charged to the exchange account. + */ +static TALER_ARL_DEF_AB (total_wire_credit_fees); + +/** * Amount of zero in our currency. */ static struct TALER_Amount zero; @@ -338,6 +343,7 @@ commit (enum GNUNET_DB_QueryStatus qs) qs = TALER_ARL_adb->update_balance ( TALER_ARL_adb->cls, TALER_ARL_SET_AB (total_wire_in), + TALER_ARL_SET_AB (total_wire_credit_fees), TALER_ARL_SET_AB (total_bad_amount_in_plus), TALER_ARL_SET_AB (total_bad_amount_in_minus), TALER_ARL_SET_AB (total_misattribution_in), @@ -347,6 +353,7 @@ commit (enum GNUNET_DB_QueryStatus qs) qs = TALER_ARL_adb->insert_balance ( TALER_ARL_adb->cls, TALER_ARL_SET_AB (total_wire_in), + TALER_ARL_SET_AB (total_wire_credit_fees), TALER_ARL_SET_AB (total_bad_amount_in_plus), TALER_ARL_SET_AB (total_bad_amount_in_minus), TALER_ARL_SET_AB (total_misattribution_in), @@ -428,6 +435,59 @@ conclude_credit_history (void) /** + * Check if the given wire transfers are equivalent. + * + * @param credit amount that was received + * @param credit2 2nd amount that was received + * @param reserve_pub public key of the reserve (also the WTID) + * @param reserve_pub2 2nd public key of the reserve (also the WTID) + * @param sender_account_details payto://-URL of the sender's bank account + * @param sender_account_details2 2nd payto://-URL of the sender's bank account + * @param execution_date when did we receive the funds + * @param execution_date2 2nd when did we receive the funds + * @return #GNUNET_YES if so, + * #GNUNET_NO if not + * #GNUNET_SYSERR on internal error + */ +static enum GNUNET_GenericReturnValue +check_equality (const struct TALER_Amount *credit, + const struct TALER_Amount *credit2, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReservePublicKeyP *reserve_pub2, + const struct TALER_FullPayto sender_account_details, + const struct TALER_FullPayto sender_account_details2, + struct GNUNET_TIME_Timestamp execution_date, + struct GNUNET_TIME_Timestamp execution_date2) +{ + if (0 != TALER_amount_cmp (credit, + credit2)) + return GNUNET_NO; + if (0 != GNUNET_memcmp (reserve_pub, + reserve_pub2)) + return GNUNET_NO; + { + struct TALER_NormalizedPayto np; + struct TALER_NormalizedPayto np2; + bool fail; + + np = TALER_payto_normalize (sender_account_details); + np2 = TALER_payto_normalize (sender_account_details2); + fail = (0 != TALER_normalized_payto_cmp (np, + np2)); + GNUNET_free (np.normalized_payto); + GNUNET_free (np2.normalized_payto); + if (fail) + return GNUNET_NO; + } + if (GNUNET_TIME_timestamp_cmp (execution_date, + !=, + execution_date2)) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** * Function called with details about incoming wire transfers * as claimed by the exchange DB. * @@ -462,6 +522,56 @@ reserve_in_cb (void *cls, TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in), &TALER_ARL_USE_AB (total_wire_in), credit); + { + enum GNUNET_DB_QueryStatus qs; + struct TALER_AUDITORDB_ReserveInInconsistency dc; + + qs = TALER_ARL_adb->select_reserve_in_inconsistency ( + TALER_ARL_adb->cls, + wire_reference, + &dc); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + global_qs = qs; + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return GNUNET_SYSERR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + if (TALER_amount_is_zero (&dc.amount_exchange_expected)) + { + /* database entry indicates unmatched transaction */ + enum GNUNET_GenericReturnValue ret; + + ret = check_equality (&dc.amount_wired, + credit, + &dc.reserve_pub, + reserve_pub, + dc.account, + sender_account_details, + GNUNET_TIME_absolute_to_timestamp (dc.timestamp), + execution_date); + if (GNUNET_SYSERR == ret) + return GNUNET_SYSERR; + if (GNUNET_YES == ret) + { + qs = TALER_ARL_adb->delete_reserve_in_inconsistency ( + TALER_ARL_adb->cls, + dc.serial_id); + if (qs < 0) + { + global_qs = qs; + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return GNUNET_SYSERR; + } + return GNUNET_OK; + } + } + break; + } + } slen = strlen (sender_account_details.full_payto) + 1; rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen); rii->rowid = rowid; @@ -621,6 +731,9 @@ analyze_credit ( GNUNET_TIME_timestamp2s (credit_details->execution_date), TALER_amount2s (&credit_details->amount), TALER_B2S (&credit_details->details.reserve.reserve_pub)); + TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees), + &TALER_ARL_USE_AB (total_wire_credit_fees), + &credit_details->credit_fee); GNUNET_CRYPTO_hash (&credit_details->serial_id, sizeof (credit_details->serial_id), &key); @@ -628,11 +741,31 @@ analyze_credit ( &key); if (NULL == rii) { - // FIXME: add to auditor DB and report missing! - // (and modify balances!) + struct TALER_AUDITORDB_ReserveInInconsistency dc = { + .bank_row_id = credit_details->serial_id, + .amount_exchange_expected = zero, + .amount_wired = credit_details->amount, + .reserve_pub = credit_details->details.reserve.reserve_pub, + .timestamp = credit_details->execution_date.abs_time, + .account = credit_details->debit_account_uri, + .diagnostic = (char *) "unknown to exchange" + }; + enum GNUNET_DB_QueryStatus qs; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to find wire transfer at `%s' in exchange database.\n", GNUNET_TIME_timestamp2s (credit_details->execution_date)); + qs = TALER_ARL_adb->insert_reserve_in_inconsistency (TALER_ARL_adb->cls, + &dc); + if (qs <= 0) + { + global_qs = qs; + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return false; + } + TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), + &TALER_ARL_USE_AB (total_bad_amount_in_plus), + &credit_details->amount); return true; } @@ -783,7 +916,6 @@ analyze_credit ( if (qs < 0) { - /* FIXME: this error handling sucks... */ global_qs = qs; GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return false; @@ -980,6 +1112,7 @@ begin_transaction (void) qs = TALER_ARL_adb->get_balance ( TALER_ARL_adb->cls, TALER_ARL_GET_AB (total_wire_in), + TALER_ARL_GET_AB (total_wire_credit_fees), TALER_ARL_GET_AB (total_bad_amount_in_plus), TALER_ARL_GET_AB (total_bad_amount_in_minus), TALER_ARL_GET_AB (total_misattribution_in), diff --git a/src/auditordb/Makefile.am b/src/auditordb/Makefile.am @@ -115,6 +115,7 @@ libtaler_plugin_auditordb_postgres_la_SOURCES = \ pg_insert_refreshes_hanging.c pg_insert_refreshes_hanging.h \ pg_get_refreshes_hanging.c pg_get_refreshes_hanging.h \ pg_get_reserve_in_inconsistency.c pg_get_reserve_in_inconsistency.h \ + pg_lookup_reserve_in_inconsistency.c pg_lookup_reserve_in_inconsistency.h \ pg_insert_reserve_in_inconsistency.c pg_insert_reserve_in_inconsistency.h \ pg_get_reserve_not_closed_inconsistency.c pg_get_reserve_not_closed_inconsistency.h \ pg_insert_reserve_not_closed_inconsistency.c pg_insert_reserve_not_closed_inconsistency.h \ diff --git a/src/auditordb/pg_get_reserve_in_inconsistency.c b/src/auditordb/pg_get_reserve_in_inconsistency.c @@ -65,11 +65,10 @@ reserve_in_inconsistency_cb (void *cls, for (unsigned int i = 0; i < num_results; i++) { - uint64_t serial_id; struct TALER_AUDITORDB_ReserveInInconsistency dc; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("row_id", - &serial_id), + &dc.serial_id), GNUNET_PQ_result_spec_uint64 ("bank_row_id", &dc.bank_row_id), TALER_PQ_RESULT_SPEC_AMOUNT ("amount_exchange_expected", @@ -101,9 +100,7 @@ reserve_in_inconsistency_cb (void *cls, } dcc->qs = i + 1; - rval = dcc->cb (dcc->cb_cls, - serial_id, &dc); GNUNET_PQ_cleanup_result (rs); if (GNUNET_OK != rval) diff --git a/src/auditordb/pg_lookup_reserve_in_inconsistency.c b/src/auditordb/pg_lookup_reserve_in_inconsistency.c @@ -0,0 +1,75 @@ +/* + 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 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/> + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_helper.h" +#include "pg_lookup_reserve_in_inconsistency.h" + + +enum GNUNET_DB_QueryStatus +TAH_PG_lookup_reserve_in_inconsistency ( + void *cls, + uint64_t bank_row_id, + struct TALER_AUDITORDB_ReserveInInconsistency *dc) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&bank_row_id), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("row_id", + &dc->serial_id), + TALER_PQ_RESULT_SPEC_AMOUNT ("amount_exchange_expected", + &dc->amount_exchange_expected), + TALER_PQ_RESULT_SPEC_AMOUNT ("amount_wired", + &dc->amount_wired), + GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", + &dc->reserve_pub), + GNUNET_PQ_result_spec_absolute_time ("timestamp", + &dc->timestamp), + GNUNET_PQ_result_spec_string ("account", + &dc->account.full_payto), + GNUNET_PQ_result_spec_string ("diagnostic", + &dc->diagnostic), + GNUNET_PQ_result_spec_bool ("suppressed", + &dc->suppressed), + GNUNET_PQ_result_spec_end + }; + + PREPARE (pg, + "auditor_lookup_reserve_in_inconsistency", + "SELECT" + " row_id" + ",amount_exchange_expected" + ",amount_wired" + ",reserve_pub" + ",timestamp" + ",account" + ",diagnostic" + ",suppressed" + " FROM auditor_reserve_in_inconsistency" + " WHERE (bank_row_id = $1)" + ); + dc->bank_row_id = bank_row_id; + return GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "auditor_lookup_reserve_in_inconsistency", + params, + rs); +} diff --git a/src/auditordb/pg_lookup_reserve_in_inconsistency.h b/src/auditordb/pg_lookup_reserve_in_inconsistency.h @@ -0,0 +1,37 @@ +/* + 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 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/> + */ +#ifndef PG_LOOKUP_RESERVE_IN_INCONSISTENCY_H +#define PG_LOOKUP_RESERVE_IN_INCONSISTENCY_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_auditordb_plugin.h" + +/** + * Lookup information about reserve-in-inconsistency from the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param bank_row_id row of the transaction at the bank + * @param[out] dc set to the transaction details + * @return query result status + */ +enum GNUNET_DB_QueryStatus +TAH_PG_lookup_reserve_in_inconsistency ( + void *cls, + uint64_t bank_row_id, + struct TALER_AUDITORDB_ReserveInInconsistency *dc); + +#endif diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c @@ -95,6 +95,7 @@ #include "pg_insert_reserve_balance_insufficient_inconsistency.h" #include "pg_get_reserve_in_inconsistency.h" +#include "pg_lookup_reserve_in_inconsistency.h" #include "pg_insert_reserve_in_inconsistency.h" #include "pg_get_reserve_not_closed_inconsistency.h" @@ -656,7 +657,10 @@ libtaler_plugin_auditordb_postgres_init (void *cls) plugin->insert_reserve_in_inconsistency = &TAH_PG_insert_reserve_in_inconsistency; - plugin->get_reserve_in_inconsistency = &TAH_PG_get_reserve_in_inconsistency; + plugin->get_reserve_in_inconsistency + = &TAH_PG_get_reserve_in_inconsistency; + plugin->lookup_reserve_in_inconsistency + = &TAH_PG_lookup_reserve_in_inconsistency; plugin->insert_reserve_not_closed_inconsistency = &TAH_PG_insert_reserve_not_closed_inconsistency; diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am @@ -34,7 +34,7 @@ lib_LTLIBRARIES = \ libtalerfakebank.la libtalerbank_la_LDFLAGS = \ - -version-info 2:0:0 \ + -version-info 3:0:0 \ -no-undefined libtalerbank_la_SOURCES = \ bank_api_account_token.c \ diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c @@ -109,11 +109,16 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh, { struct TALER_BANK_CreditDetails *td = &cd[i]; const char *type; + bool no_credit_fee; struct GNUNET_JSON_Specification hist_spec[] = { GNUNET_JSON_spec_string ("type", &type), TALER_JSON_spec_amount_any ("amount", &td->amount), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_amount_any ("credit_fee", + &td->credit_fee), + &no_credit_fee), GNUNET_JSON_spec_timestamp ("date", &td->execution_date), GNUNET_JSON_spec_uint64 ("row_id", @@ -134,6 +139,22 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh, GNUNET_break_op (0); return GNUNET_SYSERR; } + if (no_credit_fee) + { + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (td->amount.currency, + &td->credit_fee)); + } + else + { + if (GNUNET_YES != + TALER_amount_cmp_currency (&td->amount, + &td->credit_fee)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } if (0 == strcasecmp ("RESERVE", type)) { diff --git a/src/bank-lib/fakebank_twg.c b/src/bank-lib/fakebank_twg.c @@ -61,7 +61,7 @@ TALER_FAKEBANK_twg_main_ ( connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("version", - "0:0:0"), + "3:0:3"), GNUNET_JSON_pack_string ("currency", h->currency), GNUNET_JSON_pack_string ("implementation", diff --git a/src/exchangedb/test-exchange-db-postgres.conf b/src/exchangedb/test-exchange-db-postgres.conf @@ -17,6 +17,8 @@ CURRENCY = EUR [exchangedb] +MAX_AML_PROGRAM_RUNTIME = 1 minute + # After how long do we close idle reserves? The exchange # and the auditor must agree on this value. We currently # expect it to be globally defined for the whole system, diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h @@ -387,6 +387,7 @@ struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency */ struct TALER_AUDITORDB_ReserveInInconsistency { + uint64_t serial_id; uint64_t bank_row_id; struct TALER_Amount amount_exchange_expected; struct TALER_Amount amount_wired; @@ -861,7 +862,6 @@ typedef enum GNUNET_GenericReturnValue typedef enum GNUNET_GenericReturnValue (*TALER_AUDITORDB_ReserveInInconsistencyCallback)( void *cls, - uint64_t serial_id, const struct TALER_AUDITORDB_ReserveInInconsistency *dc); typedef enum GNUNET_GenericReturnValue @@ -1521,6 +1521,21 @@ struct TALER_AUDITORDB_Plugin enum TALER_AUDITORDB_DeletableSuppressableTables table, uint64_t row_id); + /** + * Lookup information about reserve-in-inconsistency from the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param bank_row_id row of the transaction at the bank + * @param[out] dc set to the transaction details + * @return query result status + */ + enum GNUNET_DB_QueryStatus + (*lookup_reserve_in_inconsistency) ( + void *cls, + uint64_t bank_row_id, + struct TALER_AUDITORDB_ReserveInInconsistency *dc); + + enum GNUNET_DB_QueryStatus (*get_reserve_in_inconsistency)( void *cls, @@ -1542,6 +1557,13 @@ struct TALER_AUDITORDB_Plugin void *cls, const struct TALER_AUDITORDB_ReserveInInconsistency *dc); + + enum GNUNET_DB_QueryStatus + (*select_reserve_in_inconsistency)( + void *cls, + uint64_t bank_row_id, + struct TALER_AUDITORDB_ReserveInInconsistency *dc); + enum GNUNET_DB_QueryStatus (*get_reserve_not_closed_inconsistency)( void *cls, diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h @@ -658,6 +658,11 @@ struct TALER_BANK_CreditDetails struct TALER_Amount amount; /** + * Fee paid by the creditor. + */ + struct TALER_Amount credit_fee; + + /** * Time of the the transfer */ struct GNUNET_TIME_Timestamp execution_date;