exchange

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

commit bde075f640cda8c5df6fcb60df15be0cb4f9121d
parent 41b10090a3443f77f6f6a76c6a04132229d398c8
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 12 Apr 2025 16:05:45 +0200

enable long-polling kyc-status by decision row ID

Diffstat:
Msrc/auditor/taler-helper-auditor-reserves.c | 6+++---
Msrc/exchange-tools/taler-exchange-kyc-trigger.c | 1+
Msrc/exchange/taler-exchange-httpd_kyc-check.c | 16+++++++++++++++-
Msrc/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql | 3+++
Msrc/exchangedb/pg_lookup_kyc_requirement_by_row.c | 7+++++++
Msrc/exchangedb/pg_lookup_kyc_requirement_by_row.h | 3+++
Msrc/include/taler_exchange_service.h | 8++++++++
Msrc/include/taler_exchangedb_plugin.h | 3+++
Msrc/lib/exchange_api_kyc_check.c | 12++++++++++++
Msrc/testing/testing_api_cmd_kyc_check_get.c | 1+
10 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/src/auditor/taler-helper-auditor-reserves.c b/src/auditor/taler-helper-auditor-reserves.c @@ -630,14 +630,14 @@ handle_withdrawals ( &auditor_fee, &issue->fees.withdraw); { - struct TALER_Amount amount_with_fee; + struct TALER_Amount issue_amount_with_fee; - TALER_ARL_amount_add (&amount_with_fee, + TALER_ARL_amount_add (&issue_amount_with_fee, &issue->value, &issue->fees.withdraw); TALER_ARL_amount_add (&auditor_amount_with_fee, &auditor_amount_with_fee, - &amount_with_fee); + &issue_amount_with_fee); } } diff --git a/src/exchange-tools/taler-exchange-kyc-trigger.c b/src/exchange-tools/taler-exchange-kyc-trigger.c @@ -188,6 +188,7 @@ kyc_wallet_cb ( CFG_exchange_url, &h_payto, &pk, + 0, TALER_EXCHANGE_KLPT_NONE, GNUNET_TIME_UNIT_ZERO, &kyc_status_cb, diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -77,6 +77,12 @@ struct KycPoller union TALER_AccountSignatureP account_sig; /** + * Generation of KYC rules already known to the client + * (when long-polling). Do not send these rules again. + */ + uint64_t min_rule; + + /** * What are we long-polling for (if anything)? */ enum TALER_EXCHANGE_KycLongPollTarget lpt; @@ -197,6 +203,7 @@ TEH_handler_kyc_check ( bool aml_review; bool kyc_required; bool access_ok = false; + uint64_t rule_gen = 0; if (NULL == kyp) { @@ -255,6 +262,9 @@ TEH_handler_kyc_check ( kyp->timeout), true)); } + TALER_MHD_parse_request_number (rc->connection, + "min_rule", + &kyp->min_rule); /* long polling needed? */ if (GNUNET_TIME_absolute_is_future (kyp->timeout)) { @@ -300,6 +310,7 @@ TEH_handler_kyc_check ( &account_pub, &reserve_pub.reserve_pub, &access_token, + &rule_gen, &jrules, &aml_review, &kyc_required); @@ -336,7 +347,8 @@ TEH_handler_kyc_check ( (GNUNET_OK == TALER_account_kyc_auth_verify (&reserve_pub, &kyp->account_sig)) ) ); - if (GNUNET_TIME_absolute_is_future (kyp->timeout)) + if (GNUNET_TIME_absolute_is_future (kyp->timeout) && + (rule_gen <= kyp->min_rule) ) { switch (kyp->lpt) { @@ -437,6 +449,8 @@ TEH_handler_kyc_check ( : MHD_HTTP_OK, GNUNET_JSON_pack_bool ("aml_review", aml_review), + GNUNET_JSON_pack_uint64 ("rule_gen", + rule_gen), GNUNET_JSON_pack_data_auto ("access_token", &access_token), GNUNET_JSON_pack_allow_null ( diff --git a/src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql b/src/exchangedb/exchange_do_lookup_kyc_requirement_by_row.sql @@ -24,6 +24,7 @@ CREATE FUNCTION exchange_do_lookup_kyc_requirement_by_row( OUT out_access_token BYTEA, -- NULL if 'out_not_found' OUT out_jrules TEXT, -- NULL allowed OUT out_not_found BOOLEAN, + OUT out_rule_gen INT8, -- NULL allowed OUT out_aml_review BOOLEAN, -- NULL allowed OUT out_kyc_required BOOLEAN) LANGUAGE plpgsql @@ -64,6 +65,7 @@ out_kyc_required = FOUND; -- Only one should ever be active per account. SELECT jnew_rules ,to_investigate + ,outcome_serial_id INTO my_lorec FROM legitimization_outcomes WHERE h_payto=in_h_normalized_payto @@ -73,6 +75,7 @@ IF FOUND THEN out_jrules=my_lorec.jnew_rules; out_aml_review=my_lorec.to_investigate; + out_rule_gen=my_lorec.outcome_serial_id; END IF; -- Check most recent reserve_in wire transfer, we also diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.c b/src/exchangedb/pg_lookup_kyc_requirement_by_row.c @@ -33,6 +33,7 @@ TEH_PG_lookup_kyc_requirement_by_row ( union TALER_AccountPublicKeyP *account_pub, struct TALER_ReservePublicKeyP *reserve_pub, struct TALER_AccountAccessTokenP *access_token, + uint64_t *rule_gen, json_t **jrules, bool *aml_review, bool *kyc_required) @@ -66,6 +67,10 @@ TEH_PG_lookup_kyc_requirement_by_row ( GNUNET_PQ_result_spec_bool ("aml_review", aml_review), NULL), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint64 ("rule_gen", + rule_gen), + NULL), GNUNET_PQ_result_spec_bool ("kyc_required", kyc_required), GNUNET_PQ_result_spec_bool ("not_found", @@ -76,6 +81,7 @@ TEH_PG_lookup_kyc_requirement_by_row ( *jrules = NULL; *aml_review = false; + *rule_gen = 0; memset (account_pub, 0, sizeof (*account_pub)); @@ -95,6 +101,7 @@ TEH_PG_lookup_kyc_requirement_by_row ( ",out_not_found AS not_found" ",out_aml_review AS aml_review" ",out_kyc_required AS kyc_required" + ",out_rule_gen AS rule_gen" " FROM exchange_do_lookup_kyc_requirement_by_row" " ($1);"); qs = GNUNET_PQ_eval_prepared_singleton_select ( diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.h b/src/exchangedb/pg_lookup_kyc_requirement_by_row.h @@ -39,6 +39,8 @@ * all zeros if not known * @param[out] access_token set to the access token to begin * work on KYC processes for this account + * @param[out] rule_gen row ID of the last decision this + * response is based on (for long-polling by clients) * @param[out] jrules set to active ``LegitimizationRuleSet`` * of the account impacted by the requirement * @param[out] aml_review set to true if the account is under @@ -54,6 +56,7 @@ TEH_PG_lookup_kyc_requirement_by_row ( union TALER_AccountPublicKeyP *account_pub, struct TALER_ReservePublicKeyP *reserve_pub, struct TALER_AccountAccessTokenP *access_token, + uint64_t *rule_gen, json_t **jrules, bool *aml_review, bool *kyc_required); diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h @@ -4313,6 +4313,12 @@ struct TALER_EXCHANGE_AccountKycStatus unsigned int limits_length; /** + * Generation of this rule, matches a monotonically increasing + * table row in the exchange with KYC rules for this account. + */ + uint64_t rule_gen; + + /** * Array of length @e limits_array with (exposed) limits that apply to the * account. */ @@ -4389,6 +4395,7 @@ typedef void * @param url exchange base URL * @param h_payto hash of the account the KYC check is about * @param pk private key to authorize the request with + * @param known_rule_gen latest known AML decision, for long polling * @param lpt target for long polling * @param timeout how long to wait for an answer, including possibly long polling for the desired @a lpt status * @param cb function to call with the result @@ -4401,6 +4408,7 @@ TALER_EXCHANGE_kyc_check ( const char *url, const struct TALER_NormalizedPaytoHashP *h_payto, const union TALER_AccountPrivateKeyP *pk, + uint64_t known_rule_gen, enum TALER_EXCHANGE_KycLongPollTarget lpt, struct GNUNET_TIME_Relative timeout, TALER_EXCHANGE_KycStatusCallback cb, diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -7356,6 +7356,8 @@ struct TALER_EXCHANGEDB_Plugin * all zeros if not known * @param[out] access_token set to the access token to begin * work on KYC processes for this account + * @param[out] rule_gen row ID of the last decision this + * response is based on (for long-polling by clients) * @param[out] jrules set to active ``LegitimizationRuleSet`` * of the account impacted by the requirement * @param[out] aml_review set to true if the account is under @@ -7371,6 +7373,7 @@ struct TALER_EXCHANGEDB_Plugin union TALER_AccountPublicKeyP *account_pub, struct TALER_ReservePublicKeyP *reserve_pub, struct TALER_AccountAccessTokenP *access_token, + uint64_t *rule_gen, json_t **jrules, bool *aml_review, bool *kyc_required); diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c @@ -70,6 +70,8 @@ parse_account_status ( struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_bool ("aml_review", &status->aml_review), + GNUNET_JSON_spec_uint64 ("rule_gen", + &status->rule_gen), GNUNET_JSON_spec_fixed_auto ("access_token", &status->access_token), GNUNET_JSON_spec_mark_optional ( @@ -258,6 +260,7 @@ TALER_EXCHANGE_kyc_check ( const char *url, const struct TALER_NormalizedPaytoHashP *h_payto, const union TALER_AccountPrivateKeyP *account_priv, + uint64_t known_rule_gen, enum TALER_EXCHANGE_KycLongPollTarget lpt, struct GNUNET_TIME_Relative timeout, TALER_EXCHANGE_KycStatusCallback cb, @@ -268,6 +271,7 @@ TALER_EXCHANGE_kyc_check ( char arg_str[128]; char timeout_ms[32]; char lpt_str[32]; + char krg_str[32]; struct curl_slist *job_headers = NULL; unsigned long long tms; @@ -289,6 +293,10 @@ TALER_EXCHANGE_kyc_check ( sizeof (timeout_ms), "%llu", tms); + GNUNET_snprintf (krg_str, + sizeof (krg_str), + "%llu", + (unsigned long long) known_rule_gen); GNUNET_snprintf (lpt_str, sizeof (lpt_str), "%d", @@ -304,6 +312,10 @@ TALER_EXCHANGE_kyc_check ( GNUNET_TIME_relative_is_zero (timeout) ? NULL : timeout_ms, + "min_rule", + 0 == known_rule_gen + ? NULL + : krg_str, "lpt", TALER_EXCHANGE_KLPT_NONE == lpt ? NULL diff --git a/src/testing/testing_api_cmd_kyc_check_get.c b/src/testing/testing_api_cmd_kyc_check_get.c @@ -178,6 +178,7 @@ check_kyc_run (void *cls, h_payto, account_priv, kcg->lpt, + 0, TALER_EXCHANGE_KLPT_NONE == kcg->lpt ? GNUNET_TIME_UNIT_ZERO : GNUNET_TIME_UNIT_MINUTES,