exchange

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

commit 640f0a6186e4581d89b4616d4620ba7344d09ac1
parent ceb2b95a60570d7cb4d97a683773a8c95530b3e6
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  6 May 2024 21:41:52 +0200

work on DD23

Diffstat:
Msrc/exchange/taler-exchange-aggregator.c | 175+++++++++++++++++++++++++------------------------------------------------------
Msrc/exchangedb/0005-legitimization_measures.sql | 4+++-
Msrc/include/taler_exchangedb_plugin.h | 14++++++++------
Msrc/include/taler_kyclogic_lib.h | 25+++++++++++++++++--------
Msrc/kyclogic/kyclogic_api.c | 19+++++++++++++++++++
5 files changed, 101 insertions(+), 136 deletions(-)

diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016-2022 Taler Systems SA + Copyright (C) 2016-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 @@ -490,49 +490,90 @@ return_relevant_amounts (void *cls, /** - * Test if KYC is required for a transfer to @a h_payto. + * Test if legitimization rules are satisfied for a transfer to @a h_payto. * * @param[in,out] au_active aggregation unit to check for * @return true if KYC checks are satisfied */ static bool -kyc_satisfied (struct AggregationUnit *au_active) +legitimization_satisfied (struct AggregationUnit *au_active) { - char *requirement; + struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL; + struct TALER_KYCLOGIC_KycRule *requirement; + struct GNUNET_TIME_Timestamp expiration_time; enum GNUNET_DB_QueryStatus qs; + json_t *jrule; if (kyc_off) return true; + + { + json_t *jrules; + + qs = db_plugin->get_kyc_rules (db_plugin->cls, + &au_active->h_payto, + &expiration_time, + &jrules); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return false; + } + if ( (qs > 0) && + (GNUNET_TIME_absolute_is_past (expiration_time.abs_time)) ) + { + json_decref (jrules); + jrules = NULL; + qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } + if (qs > 0) + { + lrs = TALER_KYCLOGIC_rules_parse (jrules); + if (NULL == lrs) + { + GNUNET_break (0); + json_decref (jrules); + return false; + } + json_decref (jrules); + } + } qs = TALER_KYCLOGIC_kyc_test_required ( TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, &au_active->h_payto, - db_plugin->select_satisfied_kyc_processes, - db_plugin->cls, + lrs, &return_relevant_amounts, (void *) au_active, &requirement); if (qs < 0) { + TALER_KYCLOGIC_rules_free (lrs); GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return false; } if (NULL == requirement) + { + TALER_KYCLOGIC_rules_free (lrs); return true; + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC requirement for %s is %s\n", TALER_amount2s (&au_active->total_amount), - requirement); - qs = db_plugin->insert_kyc_requirement_for_account ( + TALER_KYCLOGIC_rule2s (requirement)); + jrule = TALER_KYCLOGIC_rule2j (requirement); + qs = db_plugin->trigger_kyc_rule_for_account ( db_plugin->cls, - requirement, &au_active->h_payto, - NULL, /* not a reserve */ + NULL, + jrule, + TALER_KYCLOGIC_rule2priority (requirement), &au_active->requirement_row); + json_decref (jrule); if (qs < 0) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to persist KYC requirement `%s' in DB!\n", - requirement); + TALER_KYCLOGIC_rule2s (requirement)); } else { @@ -540,114 +581,7 @@ kyc_satisfied (struct AggregationUnit *au_active) "Legitimization process %llu started\n", (unsigned long long) au_active->requirement_row); } - GNUNET_free (requirement); - return false; -} - - -/** - * Function called on each @a amount that was found to - * be relevant for an AML check. - * - * @param cls closure with the `struct TALER_Amount *` where we store the sum - * @param amount encountered transaction amount - * @param date when was the amount encountered - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to abort iteration - * #GNUNET_SYSERR on internal error (also abort itaration) - */ -static enum GNUNET_GenericReturnValue -sum_for_aml ( - void *cls, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute date) -{ - struct TALER_Amount *sum = cls; - - (void) date; - if (0 > - TALER_amount_add (sum, - sum, - amount)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Test if AML is required for a transfer to @a h_payto. - * - * @param[in,out] au_active aggregation unit to check for - * @return true if AML checks are satisfied - */ -static bool -aml_satisfied (struct AggregationUnit *au_active) -{ - enum GNUNET_DB_QueryStatus qs; - struct TALER_Amount total; - struct TALER_Amount threshold; - enum TALER_AmlDecisionState decision; - struct TALER_EXCHANGEDB_KycStatus kyc; - - total = au_active->final_amount; - qs = db_plugin->select_aggregation_amounts_for_kyc_check ( - db_plugin->cls, - &au_active->h_payto, - GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (), - GNUNET_TIME_UNIT_MONTHS), - &sum_for_aml, - &total); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - return false; - } - qs = db_plugin->select_aml_threshold (db_plugin->cls, - &au_active->h_payto, - &decision, - &kyc, - &threshold); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - return false; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - threshold = aml_threshold; /* use default */ - decision = TALER_AML_NORMAL; - } - switch (decision) - { - case TALER_AML_NORMAL: - if (0 >= TALER_amount_cmp (&total, - &threshold)) - { - /* total <= threshold, do nothing */ - return true; - } - qs = db_plugin->trigger_aml_process (db_plugin->cls, - &au_active->h_payto, - &total); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - return false; - } - return false; - case TALER_AML_PENDING: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "AML already pending, doing nothing\n"); - return false; - case TALER_AML_FROZEN: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Account frozen, doing nothing\n"); - return false; - } - GNUNET_assert (0); + TALER_KYCLOGIC_rules_free (lrs); return false; } @@ -783,8 +717,7 @@ do_aggregate (struct AggregationUnit *au) TALER_amount_round_down (&au->final_amount, &currency_round_unit)) || (TALER_amount_is_zero (&au->final_amount)) || - (! kyc_satisfied (au)) || - (! aml_satisfied (au)) ) + (! legitimization_satisfied (au)) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not ready for wire transfer (%d/%s)\n", diff --git a/src/exchangedb/0005-legitimization_measures.sql b/src/exchangedb/0005-legitimization_measures.sql @@ -26,7 +26,7 @@ BEGIN '(legitimization_measure_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY' ',target_token BYTEA NOT NULL CHECK (LENGTH(target_token)=32)' ',start_time INT8 NOT NULL' - ',jmeasures TEXT NOT NULL' + ',jmeasures TEXT NOT NULL' -- FIXME: rename to jrule? ',display_priority INT4 NOT NULL' ',is_finished BOOL NOT NULL DEFAULT(FALSE)' ') %s ;' @@ -57,6 +57,8 @@ BEGIN ,'legitimization_measures' ,partition_suffix ); + -- FIXME: LegitimizationMeasures is *bad* here, as we only have the KycRule; the specific measure may + -- not yet have been selected at the time of the trigger! PERFORM comment_partitioned_column( 'JSON object of type LegitimizationMeasures with KYC/AML measures for the account encoded' ,'jmeasures' diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -998,7 +998,7 @@ struct TALER_EXCHANGEDB_DenominationKeyMetaData { /** * Serial of the denomination key as in the DB. - * Can be used in calls to stored procedures in order to spare + * Can be used calls to stored procedures in order to spare * additional lookups. */ uint64_t serial; @@ -6717,18 +6717,20 @@ struct TALER_EXCHANGEDB_Plugin * Insert KYC requirement for @a h_payto account into table. * * @param cls closure - * @param requirements requirements that must be checked * @param h_payto account that must be KYC'ed - * @param reserve_pub if account is a reserve, its public key, NULL otherwise + * @param account_pub public key authorizing access, NULL if not known + * @param jrule serialized KYC rule that was triggered + * @param display_priority priority of the rule * @param[out] requirement_row set to legitimization requirement row for this check * @return database transaction status */ enum GNUNET_DB_QueryStatus - (*insert_kyc_requirement_for_account)( + (*trigger_kyc_rule_for_account)( void *cls, - const char *requirements, const struct TALER_PaytoHashP *h_payto, - const struct TALER_ReservePublicKeyP *reserve_pub, + const union TALER_AccountPublicKeyP *account_pub, + const json_t *jrule, + uint32_t display_priority, uint64_t *requirement_row); diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -158,20 +158,17 @@ struct TALER_KYCLOGIC_KycRule; /** * Set of rules that applies to an account. */ -struct TALER_KYCLOGIC_KycRuleSet; +struct TALER_KYCLOGIC_LegitimizationRuleSet; /** * Parse set of rules that applies to an account. * * @param jrules JSON representation to parse - * @param[out] lrs set to rule set - * @return #GNUNET_SYSERR JSON is invalid + * @return rule set, NULL if JSON is invalid */ -enum GNUNET_GenericReturnValue -TALER_KYCLOGIC_rules_parse ( - const json_t *jrules, - struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs); +struct TALER_KYCLOGIC_LegitimizationRuleSet * +TALER_KYCLOGIC_rules_parse (const json_t *jrules); /** @@ -192,7 +189,8 @@ TALER_KYCLOGIC_rules_free (struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs); * @param event what type of operation is triggering the * test if KYC is required * @param h_payto account the event is about - * @param lrs legitimization rules for @a h_payto + * @param lrs legitimization rules for @a h_payto, + * NULL to use default rules * @param ai callback offered to inquire about historic * amounts involved in this type of operation * at the given account @@ -213,6 +211,17 @@ TALER_KYCLOGIC_kyc_test_required ( struct TALER_KYCLOGIC_KycRule **triggered_rule); +const char * +TALER_KYCLOGIC_rule2s (struct TALER_KYCLOGIC_KycRule *r); + + +json_t * +TALER_KYCLOGIC_rule2j (struct TALER_KYCLOGIC_KycRule *r); + + +uint32_t +TALER_KYCLOGIC_rule2priority (struct TALER_KYCLOGIC_KycRule *r); + /** * Iterate over all thresholds that are applicable to a particular type of @a * event under exposed global rules. diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -278,6 +278,11 @@ struct TALER_KYCLOGIC_LegitimizationRuleSet * Length of the @e custom_measures array. */ unsigned int num_custom_measures; + + /** + * Display priority for this rule. + */ + uint32_t display_priority; }; @@ -298,6 +303,20 @@ TALER_KYCLOGIC_rules_free (struct TALER_KYCLOGIC_KycRuleSet *krs) } +const char * +TALER_KYCLOGIC_rule2s (struct TALER_KYCLOGIC_KycRule *r) +{ + return r->rule_name; +} + + +uint32_t +TALER_KYCLOGIC_rule2priority (struct TALER_KYCLOGIC_KycRule *r) +{ + return r->display_priority; +} + + /** * AML programs. */