exchange

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

commit 97df3b30de69df738c8904d5c9917de77241d590
parent 012973ca977179e2d4f801aa06c77ca9052c5990
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 26 Jul 2024 15:31:18 +0200

fixes to error handling, logging, and aml/kyc history returning feature

Diffstat:
Msrc/exchange/taler-exchange-httpd_common_kyc.c | 614+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/exchange/taler-exchange-httpd_common_kyc.h | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/exchange/taler-exchange-httpd_kyc-proof.c | 190+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/exchange/taler-exchange-httpd_kyc-upload.c | 1+
Msrc/exchange/taler-exchange-httpd_kyc-webhook.c | 103++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/exchangedb/0005-legitimization_processes.sql | 18++++++++++++++++--
Msrc/exchangedb/Makefile.am | 2++
Msrc/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql | 3++-
Msrc/exchangedb/pg_get_coin_transactions.c | 2+-
Msrc/exchangedb/pg_insert_kyc_failure.c | 9++++++++-
Msrc/exchangedb/pg_insert_kyc_failure.h | 6+++++-
Asrc/exchangedb/pg_lookup_aml_history.c | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchangedb/pg_lookup_aml_history.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchangedb/pg_lookup_kyc_history.c | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchangedb/pg_lookup_kyc_history.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/exchangedb/pg_trigger_kyc_rule_for_account.c | 4+++-
Msrc/exchangedb/pg_trigger_kyc_rule_for_account.h | 4+++-
Msrc/exchangedb/plugin_exchangedb_postgres.c | 6++++++
Msrc/include/taler_exchangedb_plugin.h | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/include/taler_kyclogic_lib.h | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/kyclogic/kyclogic_api.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
21 files changed, 1675 insertions(+), 221 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -76,6 +76,16 @@ struct TEH_KycAmlTrigger void *cb_cls; /** + * Handle to fallback processing. + */ + struct TEH_KycAmlFallback *fb; + + /** + * Name of the fallback @e fb is running (or NULL). + */ + char *fallback_name; + + /** * user attributes returned by the provider */ json_t *attributes; @@ -120,14 +130,59 @@ struct TEH_KycAmlTrigger /** + * Function called with the result of activating a + * fallback measure. + * + * @param cls a `struct TEH_KycAmlTrigger *` + * @param result true if the fallback was activated + * successfully + * @param requirement_row row of + * new KYC requirement that was created, 0 for none + */ +static void +fallback_result_cb (void *cls, + bool result, + uint64_t requirement_row) +{ + struct TEH_KycAmlTrigger *kat = cls; + struct GNUNET_AsyncScopeSave old_scope; + + kat->fb = NULL; + (void) requirement_row; + // FIXME: return new requirement_row!? + GNUNET_async_scope_enter (&kat->scope, + &old_scope); + if (result) + { + kat->cb (kat->cb_cls, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_MHD_make_error ( + TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, + NULL)); + } + else + { + kat->cb (kat->cb_cls, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_MHD_make_error ( + TALER_EC_EXCHANGE_GENERIC_KYC_FALLBACK_FAILED, + kat->fallback_name)); + } + TEH_kyc_finished_cancel (kat); + GNUNET_async_scope_restore (&old_scope); +} + + +/** * Type of a callback that receives a JSON @a result. * * @param cls closure of type `struct TEH_KycAmlTrigger *` * @param apr AML program result */ static void -kyc_aml_finished (void *cls, - const struct TALER_KYCLOGIC_AmlProgramResult *apr) +kyc_aml_finished ( + void *cls, + const struct TALER_KYCLOGIC_AmlProgramResult *apr) { struct TEH_KycAmlTrigger *kat = cls; enum GNUNET_DB_QueryStatus qs; @@ -141,8 +196,67 @@ kyc_aml_finished (void *cls, &old_scope); if (TALER_KYCLOGIC_AMLR_SUCCESS != apr->status) { - // FIXME ... - GNUNET_break (0); // not implemented! + if (! TEH_kyc_failed ( + kat->process_row, + &kat->account_id, + kat->provider_name, + kat->provider_user_id, + kat->provider_legitimization_id, + apr->details.failure.error_message, + apr->details.failure.ec)) + { + /* double-bad: error during error handling */ + GNUNET_break (0); + kat->cb (kat->cb_cls, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_MHD_make_error ( + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_failure")); + TEH_kyc_finished_cancel (kat); + GNUNET_async_scope_restore (&old_scope); + return; + } + if (NULL == apr->details.failure.fallback_measure) + { + /* Not sure this can happen (fallback required?), + but report AML program failure to client */ + kat->cb (kat->cb_cls, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_MHD_make_error ( + TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, + NULL)); + TEH_kyc_finished_cancel (kat); + GNUNET_async_scope_restore (&old_scope); + return; + } + kat->fallback_name + = GNUNET_strdup ( + apr->details.failure.fallback_measure); + kat->fb + = TEH_kyc_fallback ( + &kat->scope, + &kat->account_id, + kat->process_row, + kat->attributes, + kat->aml_history, + kat->kyc_history, + kat->fallback_name, + &fallback_result_cb, + kat); + if (NULL == kat->fb) + { + GNUNET_break (0); + kat->cb (kat->cb_cls, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_MHD_make_error ( + TALER_EC_EXCHANGE_GENERIC_KYC_FALLBACK_UNKNOWN, + kat->fallback_name)); + TEH_kyc_finished_cancel (kat); + GNUNET_async_scope_restore (&old_scope); + return; + } + GNUNET_async_scope_restore (&old_scope); + return; } { const char *birthdate; @@ -222,19 +336,151 @@ RETURN_RESULT: } +/** + * Function called to expand AML history for the account. + * + * @param cls a `json_t *` array to build + * @param decision_time when was the decision taken + * @param justification what was the given justification + * @param decider_pub which key signed the decision + * @param jproperties what are the new account properties + * @param jnew_rules what are the new account rules + * @param to_investigate should AML staff investigate + * after the decision + * @param is_active is this the active decision + */ +static void +add_aml_history_entry ( + void *cls, + struct GNUNET_TIME_Timestamp decision_time, + const char *justification, + const struct TALER_AmlOfficerPublicKeyP *decider_pub, + const json_t *jproperties, + const json_t *jnew_rules, + bool to_investigate, + bool is_active) +{ + json_t *aml_history = cls; + json_t *e; + + e = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_timestamp ("decision_time", + decision_time), + GNUNET_JSON_pack_string ("justification", + justification), + GNUNET_JSON_pack_data_auto ("decider_pub", + decider_pub), + GNUNET_JSON_pack_object_incref ("properties", + (json_t *) jproperties), + GNUNET_JSON_pack_object_incref ("new_rules", + (json_t *) jnew_rules), + GNUNET_JSON_pack_bool ("to_investigate", + to_investigate), + GNUNET_JSON_pack_bool ("is_active", + is_active) + ); + GNUNET_assert (0 == + json_array_append_new (aml_history, + e)); +} + + +/** + * Function called to expand KYC history for the account. + * + * @param cls a `json_t *` array to build + * @param provider_name name of the KYC provider + * or NULL for none + * @param finished did the KYC process finish + * @param error_code error code from the KYC process + * @param error_message error message from the KYC process, + * or NULL for none + * @param provider_user_id user ID at the provider + * or NULL for none + * @param provider_legitimization_id legitimization process ID at the provider + * or NULL for none + * @param collection_time when was the data collected + * @param expiration_time when does the collected data expire + * @param encrypted_attributes_len number of bytes in @a encrypted_attributes + * @param encrypted_attributes encrypted KYC attributes + */ +static void +add_kyc_history_entry ( + void *cls, + const char *provider_name, + bool finished, + enum TALER_ErrorCode error_code, + const char *error_message, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Timestamp collection_time, + struct GNUNET_TIME_Absolute expiration_time, + size_t encrypted_attributes_len, + const void *encrypted_attributes) +{ + json_t *kyc_history = cls; + json_t *attributes; + json_t *e; + + attributes = TALER_CRYPTO_kyc_attributes_decrypt ( + &TEH_attribute_key, + encrypted_attributes, + encrypted_attributes_len); + e = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ( + "provider_name", + provider_name), + GNUNET_JSON_pack_bool ( + "finished", + finished), + TALER_JSON_pack_ec (error_code), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ( + "error_message", + error_message)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ( + "provider_user_id", + provider_user_id)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ( + "provider_legitimization_id", + provider_legitimization_id)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ( + "collection_time", + collection_time)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ( + "expiration_time", + GNUNET_TIME_absolute_to_timestamp ( + expiration_time))), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_steal ( + "attributes", + attributes)) + ); + + GNUNET_assert (0 == + json_array_append_new (kyc_history, + e)); +} + + struct TEH_KycAmlTrigger * -TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, - uint64_t process_row, - const struct TALER_PaytoHashP *account_id, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id, - struct GNUNET_TIME_Absolute expiration, - const json_t *attributes, - unsigned int http_status, - struct MHD_Response *response, - TEH_KycAmlTriggerCallback cb, - void *cb_cls) +TEH_kyc_finished ( + const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_name, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, + unsigned int http_status, + struct MHD_Response *response, + TEH_KycAmlTriggerCallback cb, + void *cb_cls) { struct TEH_KycAmlTrigger *kat; enum GNUNET_DB_QueryStatus qs; @@ -276,16 +522,44 @@ TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } -#if FIXME + kat->aml_history = json_array (); + kat->kyc_history = json_array (); qs = TEH_plugin->lookup_aml_history ( TEH_plugin->cls, account_id, - &kat->aml_history, - &kat->kyc_history); -#else - kat->aml_history = json_array (); - kat->kyc_history = json_array (); -#endif + &add_aml_history_entry, + kat->aml_history); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + TEH_kyc_finished_cancel (kat); + return NULL; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* empty history is fine! */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + qs = TEH_plugin->lookup_kyc_history ( + TEH_plugin->cls, + account_id, + &add_kyc_history_entry, + kat->kyc_history); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + TEH_kyc_finished_cancel (kat); + return NULL; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* empty history is fine! */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } kat->kyc_aml = TALER_KYCLOGIC_run_aml_program (kat->attributes, kat->aml_history, @@ -312,9 +586,15 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat) TALER_KYCLOGIC_run_aml_program_cancel (kat->kyc_aml); kat->kyc_aml = NULL; } + if (NULL != kat->fb) + { + TEH_kyc_fallback_cancel (kat->fb); + kat->fb = NULL; + } GNUNET_free (kat->provider_name); GNUNET_free (kat->provider_user_id); GNUNET_free (kat->provider_legitimization_id); + GNUNET_free (kat->fallback_name); json_decref (kat->jmeasures); json_decref (kat->attributes); json_decref (kat->aml_history); @@ -328,12 +608,278 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat) } +struct TEH_KycAmlFallback +{ + + /** + * Our logging scope. + */ + struct GNUNET_AsyncScopeId scope; + + /** + * Account this is for. + */ + struct TALER_PaytoHashP account_id; + + /** + * Function to call when done. + */ + TEH_KycAmlFallbackCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Handle for asynchronously running AML program. + */ + struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh; + + /** + * Task for asynchrnous returning of the result. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * New requirement row we created, 0 if none. + */ + uint64_t requirement_row; + + /** + * Original requirement row the fallback is for. + */ + uint64_t orig_requirement_row; + + /** + * True if we failed. + */ + bool failure; + +}; + + +/** + * Handle result from AML fallback program. + * + * @param cls a `struct TEH_KycAmlFallback` + * @param apr AML program result to handle + */ +static void +handle_aml_fallback_result ( + void *cls, + const struct TALER_KYCLOGIC_AmlProgramResult *apr) +{ + struct TEH_KycAmlFallback *fb = cls; + size_t eas; + void *ea; + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_AsyncScopeSave old_scope; + + fb->aprh = NULL; + GNUNET_async_scope_enter (&fb->scope, + &old_scope); + if (TALER_KYCLOGIC_AMLR_SUCCESS != apr->status) + { + if (! TEH_kyc_failed ( + fb->orig_requirement_row, + &fb->account_id, + NULL, + NULL, + NULL, + apr->details.failure.error_message, + apr->details.failure.ec)) + { + /* tripple-bad: error during error handling of fallback */ + GNUNET_break (0); + fb->cb (fb->cb_cls, + false, + 0); + TEH_kyc_fallback_cancel (fb); + GNUNET_async_scope_restore (&old_scope); + return; + } + /* Fallback not allowed on fallback */ + GNUNET_break (0); + fb->cb (fb->cb_cls, + false, + 0); + TEH_kyc_fallback_cancel (fb); + GNUNET_async_scope_restore (&old_scope); + return; + } + + { + json_t *attributes; + + /* empty attributes for fallback */ + attributes = json_object (); + GNUNET_assert (NULL != attributes); + TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, + attributes, + &ea, + &eas); + json_decref (attributes); + } + qs = TEH_plugin->insert_kyc_attributes ( + TEH_plugin->cls, + fb->orig_requirement_row, + &fb->account_id, + 0, + GNUNET_TIME_timestamp_get (), + NULL, + NULL, + NULL, + GNUNET_TIME_UNIT_FOREVER_ABS, + apr->details.success.account_properties, + apr->details.success.new_rules, + apr->details.success.to_investigate, + apr->details.success.num_events, + apr->details.success.events, + eas, + ea); + GNUNET_free (ea); + if (qs <= 0) + { + GNUNET_break (0); + fb->cb (fb->cb_cls, + false, + 0); + GNUNET_async_scope_restore (&old_scope); + TEH_kyc_fallback_cancel (fb); + return; + } + /* Finally, return result to main handler */ + fb->cb (fb->cb_cls, + true, + 0); + TEH_kyc_fallback_cancel (fb); + GNUNET_async_scope_restore (&old_scope); +} + + +/** + * Helper task function to asynchronously return + * the result of the operation. + * + * @param cls a `struct TEH_KycAmlFallback`. + */ +static void +return_fallback_result (void *cls) +{ + struct TEH_KycAmlFallback *fb = cls; + struct GNUNET_AsyncScopeSave old_scope; + + fb->task = NULL; + GNUNET_async_scope_enter (&fb->scope, + &old_scope); + fb->cb (fb->cb_cls, + ! fb->failure, + fb->requirement_row); + TEH_kyc_fallback_cancel (fb); + GNUNET_async_scope_restore (&old_scope); +} + + +struct TEH_KycAmlFallback* +TEH_kyc_fallback ( + const struct GNUNET_AsyncScopeId *scope, + const struct TALER_PaytoHashP *account_id, + uint64_t orig_requirement_row, + const json_t *attributes, + const json_t *aml_history, + const json_t *kyc_history, + const char *fallback_measure, + TEH_KycAmlFallbackCallback cb, + void *cb_cls) +{ + struct TEH_KycAmlFallback *fb; + struct TALER_KYCLOGIC_KycCheckContext kcc; + + if (GNUNET_OK != + TALER_KYCLOGIC_get_default_measure ( + fallback_measure, + &kcc)) + { + /* very bad, could not find fallback measure!? */ + GNUNET_break (0); + return NULL; + } + fb = GNUNET_new (struct TEH_KycAmlFallback); + fb->scope = *scope; + fb->account_id = *account_id; + fb->orig_requirement_row = orig_requirement_row; + fb->cb = cb; + fb->cb_cls = cb_cls; + if (NULL == kcc.check) + { + fb->aprh + = TALER_KYCLOGIC_run_aml_program2 (kcc.prog_name, + attributes, + aml_history, + kyc_history, + kcc.context, + &handle_aml_fallback_result, + fb); + if (NULL == fb->aprh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fallback AML program `%s' unknown\n", + kcc.prog_name); + TEH_kyc_fallback_cancel (fb); + return NULL; + } + return fb; + } + /* activate given check */ + { + json_t *jmeasures; + enum GNUNET_DB_QueryStatus qs; + + jmeasures = TALER_KYCLOGIC_check_to_measures (&kcc); + qs = TEH_plugin->trigger_kyc_rule_for_account ( + TEH_plugin->cls, + NULL, /* account_id is already in wire targets */ + account_id, + jmeasures, + 65536, /* high priority (does it matter?) */ + &fb->requirement_row); + json_decref (jmeasures); + fb->failure = (qs <= 0); + fb->task = GNUNET_SCHEDULER_add_now (&return_fallback_result, + fb); + } + return fb; +} + + +void +TEH_kyc_fallback_cancel ( + struct TEH_KycAmlFallback *fb) +{ + if (NULL != fb->task) + { + GNUNET_SCHEDULER_cancel (fb->task); + fb->task = NULL; + } + if (NULL != fb->aprh) + { + TALER_KYCLOGIC_run_aml_program_cancel (fb->aprh); + fb->aprh = NULL; + } + GNUNET_free (fb); +} + + bool -TEH_kyc_failed (uint64_t process_row, - const struct TALER_PaytoHashP *account_id, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id) +TEH_kyc_failed ( + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_name, + const char *provider_user_id, + const char *provider_legitimization_id, + const char *error_message, + enum TALER_ErrorCode ec) { enum GNUNET_DB_QueryStatus qs; @@ -343,7 +889,13 @@ TEH_kyc_failed (uint64_t process_row, account_id, provider_name, provider_user_id, - provider_legitimization_id); - GNUNET_break (qs >= 0); - return qs >= 0; + provider_legitimization_id, + error_message, + ec); + if (qs <= 0) + { + GNUNET_break (0); + return false; + } + return true; } diff --git a/src/exchange/taler-exchange-httpd_common_kyc.h b/src/exchange/taler-exchange-httpd_common_kyc.h @@ -50,7 +50,7 @@ typedef void */ struct TEH_KycAmlTrigger; -// FIXME: also pass async log context and set it! + /** * We have finished a KYC process and obtained new * @a attributes for a given @a account_id. @@ -73,18 +73,19 @@ struct TEH_KycAmlTrigger; * @return handle to cancel the operation */ struct TEH_KycAmlTrigger * -TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, - uint64_t process_row, - const struct TALER_PaytoHashP *account_id, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id, - struct GNUNET_TIME_Absolute expiration, - const json_t *attributes, - unsigned int http_status, - struct MHD_Response *response, - TEH_KycAmlTriggerCallback cb, - void *cb_cls); +TEH_kyc_finished ( + const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_name, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, + unsigned int http_status, + struct MHD_Response *response, + TEH_KycAmlTriggerCallback cb, + void *cb_cls); /** @@ -97,6 +98,69 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat); /** + * Handle for an asynchronous operation to run some + * fallback measure. + */ +struct TEH_KycAmlFallback; + + +/** + * Function called after the KYC-AML fallback + * processing is done. + * + * @param cls closure + * @param result true if fallback handling was OK + * @param requirement_row row of + * new KYC requirement that was created, 0 for none + */ +typedef void +(*TEH_KycAmlFallbackCallback) ( + void *cls, + bool result, + uint64_t requirement_row); + + +/** + * Activate fallback measure for the given account. + * + * @param scope the HTTP request logging scope + * @param account_id account to activate fallback for + * @param orig_requirement_row original requirement + * row that now triggered the fallback + * @param attributes attributes to run with + * @param aml_history AML history of the account + * @param kyc_history KYC history of the account + * @param fallback_measure fallback to activate + * @param cb callback to call with result + * @param cb_cls closure for @a cb + * @return handle for fallback operation, NULL + * if @a fallback_measure is unknown + */ +struct TEH_KycAmlFallback * +TEH_kyc_fallback ( + const struct GNUNET_AsyncScopeId *scope, + const struct TALER_PaytoHashP * + account_id, + uint64_t orig_requirement_row, + const json_t *attributes, + const json_t *aml_history, + const json_t *kyc_history, + const char *fallback_measure, + TEH_KycAmlFallbackCallback cb, + void *cb_cls); + + +/** + * Cancel fallback operation. + * + * @param[in] fb operation to cancel + */ +void +TEH_kyc_fallback_cancel ( + struct TEH_KycAmlFallback *fb); + + +/** * Update state of a legitmization process to 'finished' * (and failed, no attributes were obtained). * @@ -105,13 +169,18 @@ TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat); * @param provider_name name KYC provider with the logic that was run * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown - * @return true on success, false if updating the database failed + * @param error_message error message to log + * @param ec error code to log + * @return true if the error was handled successfully */ bool -TEH_kyc_failed (uint64_t process_row, - const struct TALER_PaytoHashP *account_id, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id); +TEH_kyc_failed ( + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_name, + const char *provider_user_id, + const char *provider_legitimization_id, + const char *error_message, + enum TALER_ErrorCode ec); #endif diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -299,28 +299,29 @@ proof_cb ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC process #%llu succeeded with KYC provider\n", (unsigned long long) kpc->process_row); - kpc->kat = TEH_kyc_finished (&rc->async_scope_id, - kpc->process_row, - &kpc->h_payto, - kpc->provider_name, - provider_user_id, - provider_legitimization_id, - expiration, - attributes, - http_status, - response, - &proof_finish, - kpc); + kpc->kat = TEH_kyc_finished ( + &rc->async_scope_id, + kpc->process_row, + &kpc->h_payto, + kpc->provider_name, + provider_user_id, + provider_legitimization_id, + expiration, + attributes, + http_status, + response, + &proof_finish, + kpc); + response = NULL; /* taken over by TEH_kyc_finished */ if (NULL == kpc->kat) { http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; - if (NULL != response) - MHD_destroy_response (response); - response = make_html_error (kpc->rc->connection, - "kyc-proof-internal-error", - &http_status, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - "[exchange] AML_KYC_TRIGGER"); + response = make_html_error ( + kpc->rc->connection, + "kyc-proof-internal-error", + &http_status, + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + "[exchange] AML_KYC_TRIGGER"); } break; case TALER_KYCLOGIC_STATUS_FAILED: @@ -345,30 +346,35 @@ proof_cb ( "Failure by KYC provider (HTTP status %u)\n", http_status); http_status = MHD_HTTP_BAD_GATEWAY; - response = make_html_error (kpc->rc->connection, - "kyc-proof-internal-error", - &http_status, - TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, - msg); + response = make_html_error ( + kpc->rc->connection, + "kyc-proof-internal-error", + &http_status, + TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY, + msg); GNUNET_free (msg); } else { - if (! TEH_kyc_failed (kpc->process_row, - &kpc->h_payto, - kpc->provider_name, - provider_user_id, - provider_legitimization_id)) + if (! TEH_kyc_failed ( + kpc->process_row, + &kpc->h_payto, + kpc->provider_name, + provider_user_id, + provider_legitimization_id, + TALER_KYCLOGIC_status2s (status), + TALER_EC_EXCHANGE_GENERIC_KYC_FAILED)) { GNUNET_break (0); if (NULL != response) MHD_destroy_response (response); http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; - response = make_html_error (kpc->rc->connection, - "kyc-proof-internal-error", - &http_status, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_failure"); + response = make_html_error ( + kpc->rc->connection, + "kyc-proof-internal-error", + &http_status, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_failure"); } } break; @@ -440,11 +446,12 @@ TEH_handler_kyc_proof ( if (NULL == provider_name_or_logic) { GNUNET_break_op (0); - return respond_html_ec (rc, - MHD_HTTP_NOT_FOUND, - "kyc-proof-endpoint-unknown", - TALER_EC_GENERIC_ENDPOINT_UNKNOWN, - "'/kyc-proof/$PROVIDER_NAME?state=$H_PAYTO' required"); + return respond_html_ec ( + rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-endpoint-unknown", + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + "'/kyc-proof/$PROVIDER_NAME?state=$H_PAYTO' required"); } kpc = GNUNET_new (struct KycProofContext); kpc->rc = rc; @@ -454,17 +461,19 @@ TEH_handler_kyc_proof ( "state", &kpc->h_payto); if (GNUNET_OK != - TALER_KYCLOGIC_lookup_logic (provider_name_or_logic, - &kpc->logic, - &kpc->pd, - &kpc->provider_name)) + TALER_KYCLOGIC_lookup_logic ( + provider_name_or_logic, + &kpc->logic, + &kpc->pd, + &kpc->provider_name)) { GNUNET_break_op (0); - return respond_html_ec (rc, - MHD_HTTP_NOT_FOUND, - "kyc-proof-target-unknown", - TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, - provider_name_or_logic); + return respond_html_ec ( + rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-target-unknown", + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, + provider_name_or_logic); } if (NULL != kpc->provider_name) { @@ -475,11 +484,12 @@ TEH_handler_kyc_proof ( kpc->provider_name)) { GNUNET_break_op (0); - return respond_html_ec (rc, - MHD_HTTP_BAD_REQUEST, - "kyc-proof-bad-request", - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "PROVIDER_NAME"); + return respond_html_ec ( + rc, + MHD_HTTP_BAD_REQUEST, + "kyc-proof-bad-request", + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "PROVIDER_NAME"); } qs = TEH_plugin->lookup_kyc_process_by_account ( @@ -494,47 +504,52 @@ TEH_handler_kyc_proof ( { case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_SOFT_ERROR: - return respond_html_ec (rc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - "kyc-proof-internal-error", - TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_kyc_process_by_account"); + return respond_html_ec ( + rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_kyc_process_by_account"); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return respond_html_ec (rc, - MHD_HTTP_NOT_FOUND, - "kyc-proof-target-unknown", - TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, - kpc->provider_name); + return respond_html_ec ( + rc, + MHD_HTTP_NOT_FOUND, + "kyc-proof-target-unknown", + TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN, + kpc->provider_name); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } if (GNUNET_TIME_absolute_is_future (expiration)) { /* KYC not required */ - return respond_html_ec (rc, - MHD_HTTP_OK, - "kyc-proof-already-done", - TALER_EC_NONE, - NULL); + return respond_html_ec ( + rc, + MHD_HTTP_OK, + "kyc-proof-already-done", + TALER_EC_NONE, + NULL); } } - kpc->ph = kpc->logic->proof (kpc->logic->cls, - kpc->pd, - rc->connection, - &kpc->h_payto, - kpc->process_row, - kpc->provider_user_id, - kpc->provider_legitimization_id, - &proof_cb, - kpc); + kpc->ph = kpc->logic->proof ( + kpc->logic->cls, + kpc->pd, + rc->connection, + &kpc->h_payto, + kpc->process_row, + kpc->provider_user_id, + kpc->provider_legitimization_id, + &proof_cb, + kpc); if (NULL == kpc->ph) { GNUNET_break (0); - return respond_html_ec (rc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - "kyc-proof-internal-error", - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "could not start proof with KYC logic"); + return respond_html_ec ( + rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "could not start proof with KYC logic"); } @@ -549,11 +564,12 @@ TEH_handler_kyc_proof ( if (NULL == kpc->response) { GNUNET_break (0); - return respond_html_ec (rc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - "kyc-proof-internal-error", - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "handler resumed without response"); + return respond_html_ec ( + rc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + "kyc-proof-internal-error", + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "handler resumed without response"); } /* return response from KYC logic */ diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c @@ -523,6 +523,7 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, empty_response, &aml_trigger_callback, uc); + empty_response = NULL; /* taken over by TEH_kyc_finished */ if (NULL == uc->kat) { GNUNET_break (0); diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022-2023 Taler Systems SA + Copyright (C) 2022-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 @@ -240,11 +240,14 @@ webhook_finished_cb ( provider_legitimization_id, (unsigned long long) process_row, status); - if (! TEH_kyc_failed (process_row, - account_id, - provider_name, - provider_user_id, - provider_legitimization_id)) + if (! TEH_kyc_failed ( + process_row, + account_id, + provider_name, + provider_user_id, + provider_legitimization_id, + TALER_KYCLOGIC_status2s (status), + TALER_EC_EXCHANGE_GENERIC_KYC_FAILED)) { GNUNET_break (0); if (NULL != response) @@ -256,12 +259,13 @@ webhook_finished_cb ( } break; default: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "KYC status of %s/%s (Row #%llu) is %d\n", - provider_user_id, - provider_legitimization_id, - (unsigned long long) process_row, - (int) status); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "KYC status of %s/%s (Row #%llu) is %d\n", + provider_user_id, + provider_legitimization_id, + (unsigned long long) process_row, + (int) status); break; } GNUNET_break (NULL == kwh->kat); @@ -327,40 +331,44 @@ handler_kyc_webhook_generic ( if ( (NULL == args[0]) || (GNUNET_OK != - TALER_KYCLOGIC_lookup_logic (args[0], - &kwh->plugin, - &kwh->pd, - &kwh->provider_name)) ) + TALER_KYCLOGIC_lookup_logic ( + args[0], + &kwh->plugin, + &kwh->pd, + &kwh->provider_name)) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "KYC logic `%s' unknown (check KYC provider configuration)\n", args[0]); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, - args[0]); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, + args[0]); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC logic `%s' mapped to section %s\n", args[0], kwh->provider_name); - kwh->wh = kwh->plugin->webhook (kwh->plugin->cls, - kwh->pd, - TEH_plugin->kyc_provider_account_lookup, - TEH_plugin->cls, - method, - &args[1], - rc->connection, - root, - &webhook_finished_cb, - kwh); + kwh->wh = kwh->plugin->webhook ( + kwh->plugin->cls, + kwh->pd, + TEH_plugin->kyc_provider_account_lookup, + TEH_plugin->cls, + method, + &args[1], + rc->connection, + root, + &webhook_finished_cb, + kwh); if (NULL == kwh->wh) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "failed to run webhook logic"); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "failed to run webhook logic"); } kwh->suspended = GNUNET_YES; GNUNET_CONTAINER_DLL_insert (kwh_head, @@ -385,10 +393,11 @@ handler_kyc_webhook_generic ( /* We resumed, but got no response? This should not happen. */ GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "resumed without response"); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "resumed without response"); } @@ -397,10 +406,11 @@ TEH_handler_kyc_webhook_get ( struct TEH_RequestContext *rc, const char *const args[]) { - return handler_kyc_webhook_generic (rc, - MHD_HTTP_METHOD_GET, - NULL, - args); + return handler_kyc_webhook_generic ( + rc, + MHD_HTTP_METHOD_GET, + NULL, + args); } @@ -410,10 +420,11 @@ TEH_handler_kyc_webhook_post ( const json_t *root, const char *const args[]) { - return handler_kyc_webhook_generic (rc, - MHD_HTTP_METHOD_POST, - root, - args); + return handler_kyc_webhook_generic ( + rc, + MHD_HTTP_METHOD_POST, + root, + args); } diff --git a/src/exchangedb/0005-legitimization_processes.sql b/src/exchangedb/0005-legitimization_processes.sql @@ -24,7 +24,9 @@ BEGIN PERFORM create_partitioned_table( 'ALTER TABLE legitimization_processes' ' ADD COLUMN legitimization_measure_serial_id BIGINT' - ',ADD COLUMN measure_index INT4' + ',ADD COLUMN measure_index INT4 DEFAULT(0)' + ',ADD COLUMN error_code INT4 DEFAULT (0)' + ',ADD COLUMN error_message TEXT DEFAULT NULL' ';' ,'legitimization_processes' ,'' @@ -45,11 +47,23 @@ BEGIN ,shard_suffix ); PERFORM comment_partitioned_column( - 'index of the measure in legitimization_measures that was selected for this KYC setup; NULL if legitimization_measure_serial_id is NULL; enables determination of the context data provided to the external proces' + 'index of the measure in legitimization_measures that was selected for this KYC setup; NULL if legitimization_measure_serial_id is NULL; enables determination of the context data provided to the external process' ,'measure_index' ,'legitimization_processes' ,shard_suffix ); + PERFORM comment_partitioned_column( + 'TALER_ErrorCode set if the process failed, otherwise NULL' + ,'error_code' + ,'legitimization_processes' + ,shard_suffix + ); + PERFORM comment_partitioned_column( + 'human-readable error details set if the process failed, otherwise NULL' + ,'error_message' + ,'legitimization_processes' + ,shard_suffix + ); END $$; diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am @@ -115,6 +115,8 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_select_withdraw_amounts_for_kyc_check.h pg_select_withdraw_amounts_for_kyc_check.c \ pg_select_merge_amounts_for_kyc_check.h pg_select_merge_amounts_for_kyc_check.c \ pg_profit_drains_set_finished.h pg_profit_drains_set_finished.c \ + pg_lookup_aml_history.h pg_lookup_aml_history.c \ + pg_lookup_kyc_history.h pg_lookup_kyc_history.c \ pg_profit_drains_get_pending.h pg_profit_drains_get_pending.c \ pg_get_drain_profit.h pg_get_drain_profit.c \ pg_get_purse_deposit.h pg_get_purse_deposit.c \ diff --git a/src/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql b/src/exchangedb/exchange_do_trigger_kyc_rule_for_account.sql @@ -26,7 +26,8 @@ AS $$ DECLARE my_access_token BYTEA; BEGIN - +-- Note: in_payto_uri is allowed to be NULL *if* +-- in_h_payto is already in wire_targets SELECT access_token INTO diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c @@ -38,7 +38,7 @@ #define RETRIES 3 /** - * Closure for callbacks called from #postgres_get_coin_transactions() + * Closure for callbacks called from #TEH_PG_get_coin_transactions() */ struct CoinHistoryContext { diff --git a/src/exchangedb/pg_insert_kyc_failure.c b/src/exchangedb/pg_insert_kyc_failure.c @@ -34,9 +34,12 @@ TEH_PG_insert_kyc_failure ( const struct TALER_PaytoHashP *h_payto, const char *provider_name, const char *provider_account_id, - const char *provider_legitimization_id) + const char *provider_legitimization_id, + const char *error_message, + enum TALER_ErrorCode ec) { struct PostgresClosure *pg = cls; + uint32_t ec32 = (uint32_t) ec; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&process_row), GNUNET_PQ_query_param_auto_from_type (h_payto), @@ -47,6 +50,8 @@ TEH_PG_insert_kyc_failure ( NULL != provider_legitimization_id ? GNUNET_PQ_query_param_string (provider_legitimization_id) : GNUNET_PQ_query_param_null (), + GNUNET_PQ_query_param_uint32 (&ec32), + GNUNET_PQ_query_param_string (error_message), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; @@ -58,6 +63,8 @@ TEH_PG_insert_kyc_failure ( " finished=TRUE" " ,provider_user_id=$4" " ,provider_legitimization_id=$5" + " ,error_code=$6" + " ,error_message=$7" " WHERE h_payto=$2" " AND legitimization_process_serial_id=$1" " AND provider_name=$3;"); diff --git a/src/exchangedb/pg_insert_kyc_failure.h b/src/exchangedb/pg_insert_kyc_failure.h @@ -35,6 +35,8 @@ * @param provider_name provider that must be checked * @param provider_account_id provider account ID * @param provider_legitimization_id provider legitimization ID + * @param error_message details about what went wrong + * @param ec error code about the failure * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -44,7 +46,9 @@ TEH_PG_insert_kyc_failure ( const struct TALER_PaytoHashP *h_payto, const char *provider_name, const char *provider_account_id, - const char *provider_legitimization_id); + const char *provider_legitimization_id, + const char *error_message, + enum TALER_ErrorCode ec); #endif diff --git a/src/exchangedb/pg_lookup_aml_history.c b/src/exchangedb/pg_lookup_aml_history.c @@ -0,0 +1,170 @@ +/* + 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/> + */ +/** + * @file exchangedb/pg_lookup_aml_history.c + * @brief Implementation of the lookup_aml_history function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_lookup_aml_history.h" +#include "pg_helper.h" + + +/** + * Closure for callbacks called from #TEH_PG_lookup_aml_history() + */ +struct AmlHistoryContext +{ + + /** + * Function to call on each result. + */ + TALER_EXCHANGEDB_AmlHistoryCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Plugin context. + */ + struct PostgresClosure *pg; + + /** + * Set to 'true' if the transaction failed. + */ + bool failed; + +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct AmlHistoryContext` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +handle_aml_entry (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct AmlHistoryContext *ahc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + struct GNUNET_TIME_Timestamp decision_time; + char *justification; + struct TALER_AmlOfficerPublicKeyP decider_pub; + json_t *jproperties; + json_t *jnew_rules; + bool to_investigate; + bool is_active; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("decision_time", + &decision_time), + GNUNET_PQ_result_spec_string ("justification", + &justification), + GNUNET_PQ_result_spec_auto_from_type ("decider_pub", + &decider_pub), + TALER_PQ_result_spec_json ("properties", + &jproperties), + TALER_PQ_result_spec_json ("new_rules", + &jnew_rules), + GNUNET_PQ_result_spec_bool ("to_investigate", + &to_investigate), + GNUNET_PQ_result_spec_bool ("is_active", + &is_active), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ahc->failed = true; + return; + } + ahc->cb (ahc->cb_cls, + decision_time, + justification, + &decider_pub, + jproperties, + jnew_rules, + to_investigate, + is_active); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_lookup_aml_history ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + TALER_EXCHANGEDB_AmlHistoryCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct AmlHistoryContext ahc = { + .pg = pg, + .cb = cb, + .cb_cls = cb_cls + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "lookup_aml_history", + "SELECT" + " ah.decision_time" + ",ah.justification" + ",ah.decider_pub" + ",lo.jproperties" + ",lo.jnew_rules" + ",lo.to_investigate" + ".lo.is_active" + " FROM aml_history ah" + " JOIN legitimization_outcomes lo" + " USING (outcome_serial_id)" + " WHERE h_payto=$1" + " ORDER BY decision_time DESC;"); + qs = GNUNET_PQ_eval_prepared_multi_select ( + pg->conn, + "lookup_aml_history", + params, + &handle_aml_entry, + &ahc); + if (qs <= 0) + return qs; + if (ahc.failed) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + return qs; +} diff --git a/src/exchangedb/pg_lookup_aml_history.h b/src/exchangedb/pg_lookup_aml_history.h @@ -0,0 +1,47 @@ +/* + 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/> + */ +/** + * @file exchangedb/pg_lookup_aml_history.h + * @brief implementation of the lookup_aml_history function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_LOOKUP_AML_HISTORY_H +#define PG_LOOKUP_AML_HISTORY_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Lookup AML history for an account identified via + * @a h_payto. + * + * @param cls closure + * @param h_payto hash of account to lookup history for + * @param cb function to call on results + * @param cb_cls closure for @a cb + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_lookup_aml_history ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + TALER_EXCHANGEDB_AmlHistoryCallback cb, + void *cb_cls); + + +#endif diff --git a/src/exchangedb/pg_lookup_kyc_history.c b/src/exchangedb/pg_lookup_kyc_history.c @@ -0,0 +1,195 @@ +/* + 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/> + */ +/** + * @file exchangedb/pg_lookup_kyc_history.c + * @brief Implementation of the lookup_kyc_history function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_lookup_kyc_history.h" +#include "pg_helper.h" + +/** + * Closure for callbacks called from #TEH_PG_lookup_kyc_history() + */ +struct KycHistoryContext +{ + + /** + * Function to call on each result. + */ + TALER_EXCHANGEDB_KycHistoryCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Plugin context. + */ + struct PostgresClosure *pg; + + /** + * Set to 'true' if the transaction failed. + */ + bool failed; + +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct KycHistoryContext` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +handle_kyc_entry (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct KycHistoryContext *khc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *provider_name = NULL; + bool finished; + uint32_t error_code; + char *error_message = NULL; + char *provider_user_id = NULL; + char *provider_legitimization_id = NULL; + struct GNUNET_TIME_Timestamp collection_time; + struct GNUNET_TIME_Absolute expiration_time + = GNUNET_TIME_UNIT_ZERO_ABS; + void *encrypted_attributes; + size_t encrypted_attributes_len; + + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("provider_name", + &provider_name), + NULL), + GNUNET_PQ_result_spec_bool ("finished", + &finished), + GNUNET_PQ_result_spec_uint32 ("error_code", + &error_code), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("error_message", + &error_message), + NULL), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("provider_user_id", + &provider_user_id), + NULL), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("provider_legitimization_id", + &provider_legitimization_id), + NULL), + GNUNET_PQ_result_spec_timestamp ("collection_time", + &collection_time), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_absolute_time ("expiration_time", + &expiration_time), + NULL), + GNUNET_PQ_result_spec_variable_size ("encrypted_attributes", + &encrypted_attributes, + &encrypted_attributes_len), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + khc->failed = true; + return; + } + khc->cb (khc->cb_cls, + provider_name, + finished, + (enum TALER_ErrorCode) error_code, + error_message, + provider_user_id, + provider_legitimization_id, + collection_time, + expiration_time, + encrypted_attributes_len, + encrypted_attributes); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_lookup_kyc_history ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + TALER_EXCHANGEDB_KycHistoryCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct KycHistoryContext khc = { + .pg = pg, + .cb = cb, + .cb_cls = cb_cls + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "lookup_kyc_history", + "SELECT" + " lp.provider_name" + ",lp.finished" + ",lp.error_code" + ",lp.error_message" + ",lp.provider_user_id" + ",lp.provider_legitimization_id" + ",ka.collection_time" + ",ka.expiration_time" + ",ka.encrypted_attributes" + " FROM kyc_attributes ka" + " JOIN legitimization_processes lp" + " ON (ka.legitimization_serial = lp.legitimization_process_serial)" + " WHERE ka.h_payto=$1" + " ORDER BY collection_time DESC;"); + + qs = GNUNET_PQ_eval_prepared_multi_select ( + pg->conn, + "lookup_kyc_history", + params, + &handle_kyc_entry, + &khc); + if (qs <= 0) + return qs; + if (khc.failed) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + return qs; +} diff --git a/src/exchangedb/pg_lookup_kyc_history.h b/src/exchangedb/pg_lookup_kyc_history.h @@ -0,0 +1,46 @@ +/* + 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/> + */ +/** + * @file exchangedb/pg_lookup_kyc_history.h + * @brief implementation of the lookup_kyc_history function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_LOOKUP_KYC_HISTORY_H +#define PG_LOOKUP_KYC_HISTORY_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Lookup KYC history for an account identified via + * @a h_payto. + * + * @param cls closure + * @param h_payto hash of account to lookup history for + * @param cb function to call on results + * @param cb_cls closure for @a cb + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_lookup_kyc_history ( + void *cls, + const struct TALER_PaytoHashP *h_payto, + TALER_EXCHANGEDB_KycHistoryCallback cb, + void *cb_cls); + +#endif diff --git a/src/exchangedb/pg_trigger_kyc_rule_for_account.c b/src/exchangedb/pg_trigger_kyc_rule_for_account.c @@ -40,7 +40,9 @@ TEH_PG_trigger_kyc_rule_for_account ( = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_payto), - GNUNET_PQ_query_param_string (payto_uri), + NULL == payto_uri + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (payto_uri), GNUNET_PQ_query_param_absolute_time (&now), TALER_PQ_query_param_json (jmeasures), GNUNET_PQ_query_param_uint32 (&display_priority), diff --git a/src/exchangedb/pg_trigger_kyc_rule_for_account.h b/src/exchangedb/pg_trigger_kyc_rule_for_account.h @@ -30,7 +30,9 @@ * Insert KYC requirement for @a h_payto account into table. * * @param cls closure - * @param payto_uri account that must be KYC'ed + * @param payto_uri account that must be KYC'ed, + * can be NULL if @a h_payto is already + * guaranteed to be in wire_targets * @param h_payto hash of @a payto_uri * @param jmeasures serialized MeasureSet to put in place * @param display_priority priority of the rule diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c @@ -94,6 +94,8 @@ #include "pg_select_contract_by_purse.h" #include "pg_insert_drain_profit.h" #include "pg_do_reserve_purse.h" +#include "pg_lookup_aml_history.h" +#include "pg_lookup_kyc_history.h" #include "pg_lookup_global_fee_by_time.h" #include "pg_do_purse_deposit.h" #include "pg_activate_signing_key.h" @@ -686,6 +688,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_gc; plugin->select_coin_deposits_above_serial_id = &TEH_PG_select_coin_deposits_above_serial_id; + plugin->lookup_aml_history + = &TEH_PG_lookup_aml_history; + plugin->lookup_kyc_history + = &TEH_PG_lookup_kyc_history; plugin->select_purse_decisions_above_serial_id = &TEH_PG_select_purse_decisions_above_serial_id; plugin->select_purse_deposits_by_purse diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -3082,6 +3082,66 @@ typedef void /** + * Function called with historic AML events of an + * account. + * + * @param cls closure + * @param decision_time when was the decision taken + * @param justification what was the given justification + * @param decider_pub which key signed the decision + * @param jproperties what are the new account properties + * @param jnew_rules what are the new account rules + * @param to_investigate should AML staff investigate + * after the decision + * @param is_active is this the active decision + */ +typedef void +(*TALER_EXCHANGEDB_AmlHistoryCallback) ( + void *cls, + struct GNUNET_TIME_Timestamp decision_time, + const char *justification, + const struct TALER_AmlOfficerPublicKeyP *decider_pub, + const json_t *jproperties, + const json_t *jnew_rules, + bool to_investigate, + bool is_active); + + +/** + * Function called with historic KYC events of an + * account. + * + * @param cls closure + * @param provider_name name of the KYC provider + * @param finished did the KYC process finish + * @param error_code error code from the KYC process + * @param error_message error message from the KYC process, + * or NULL for none + * @param provider_user_id user ID at the provider + * or NULL for none + * @param provider_legitimization_id legitimization process ID at the provider + * or NULL for none + * @param collection_time when was the data collected + * @param expiration_time when does the collected data expire + * @param encrypted_attributes_len number of bytes in @a encrypted_attributes + * @param encrypted_attributes encrypted KYC attributes + */ +typedef void +(*TALER_EXCHANGEDB_KycHistoryCallback) ( + void *cls, + const char *provider_name, + bool finished, + enum TALER_ErrorCode error_code, + const char *error_message, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Timestamp collection_time, + struct GNUNET_TIME_Absolute expiration_time, + size_t encrypted_attributes_len, + const void *encrypted_attributes); + + +/** * Provide information about wire fees. * * @param cls closure @@ -6801,7 +6861,9 @@ struct TALER_EXCHANGEDB_Plugin * Insert KYC requirement for @a h_payto account into table. * * @param cls closure - * @param payto_uri account that must be KYC'ed + * @param payto_uri account that must be KYC'ed, + * can be NULL if @a h_payto is already + * guaranteed to be in wire_targets * @param h_payto hash of @a payto_uri * @param jmeasures serialized MeasureSet to put in place * @param display_priority priority of the rule @@ -7337,6 +7399,42 @@ struct TALER_EXCHANGEDB_Plugin /** + * Lookup AML history for an account identified via + * @a h_payto. + * + * @param cls closure + * @param h_payto hash of account to lookup history for + * @param cb function to call on results + * @param cb_cls closure for @a cb + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*lookup_aml_history)( + void *cls, + const struct TALER_PaytoHashP *h_payto, + TALER_EXCHANGEDB_AmlHistoryCallback cb, + void *cb_cls); + + + /** + * Lookup AML history for an account identified via + * @a h_payto. + * + * @param cls closure + * @param h_payto hash of account to lookup history for + * @param cb function to call on results + * @param cb_cls closure for @a cb + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*lookup_kyc_history)( + void *cls, + const struct TALER_PaytoHashP *h_payto, + TALER_EXCHANGEDB_KycHistoryCallback cb, + void *cb_cls); + + + /** * Lookup measure data for an active legitimization process. * * @param cls closure @@ -7399,6 +7497,8 @@ struct TALER_EXCHANGEDB_Plugin * @param provider_name provider that must be checked * @param provider_account_id provider account ID * @param provider_legitimization_id provider legitimization ID + * @param error_message details about what went wrong + * @param ec error code about the failure * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -7408,7 +7508,9 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_PaytoHashP *h_payto, const char *provider_name, const char *provider_account_id, - const char *provider_legitimization_id); + const char *provider_legitimization_id, + const char *error_message, + enum TALER_ErrorCode ec); /** * Function called to inject auditor triggers into the diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -342,10 +342,32 @@ TALER_KYCLOGIC_kyc_test_required ( const struct TALER_KYCLOGIC_KycRule **triggered_rule); +/** + * Get human-readable name of KYC rule. + * + * @param r rule to convert + * @return name of the rule + */ const char * TALER_KYCLOGIC_rule2s (const struct TALER_KYCLOGIC_KycRule *r); +/** + * Convert KYC status to human-readable string. + * + * @param status status to convert + * @return human-readable string + */ +const char * +TALER_KYCLOGIC_status2s (enum TALER_KYCLOGIC_KycStatus status); + + +/** + * Get priority of KYC rule. + * + * @param r rule to convert + * @return priority of the rule + */ uint32_t TALER_KYCLOGIC_rule2priority (const struct TALER_KYCLOGIC_KycRule *r); @@ -388,16 +410,56 @@ TALER_KYCLOGIC_is_enabled (void); /** - * A KYC rule @a r has been triggered. Convert the resulting requirements in - * to JSON of type ``LegitimizationMeasures`` for the legitimization measures table. + * A KYC rule @a r has been triggered. Convert the resulting requirements into + * JSON of type ``LegitimizationMeasures`` for the legitimization measures table. * - * FIXME: not implemented! * @param r a rule that was triggered * @return JSON serialization of the corresponding * ``LegitimizationMeasures``, NULL on error */ json_t * -TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r); +TALER_KYCLOGIC_rule_to_measures ( + const struct TALER_KYCLOGIC_KycRule *r); + + +/** + * Tuple with information about a KYC check to perform. Note that it will + * have references into the legitimization rule set provided to + * #TALER_KYCLOGIC_requirements_to_check() and thus has a lifetime that + * matches the legitimization rule set. + */ +struct TALER_KYCLOGIC_KycCheckContext +{ + /** + * KYC check to perform. + */ + const struct TALER_KYCLOGIC_KycCheck *check; + + /** + * Context for the check. Can be NULL. + */ + const json_t *context; + + /** + * Name of the AML program. + */ + char *prog_name; +}; + + +/** + * A KYC check @a kcc has been triggered. Convert the resulting singular + * requirement (only a single check is possible, not multiple alternatives) + * into JSON of type ``LegitimizationMeasures`` for the legitimization + * measures table. + * + * @param kcc check that was triggered + * @return JSON serialization of the corresponding + * ``LegitimizationMeasures`` + */ +json_t * +TALER_KYCLOGIC_check_to_measures ( + const struct TALER_KYCLOGIC_KycCheckContext *kcc); /** @@ -482,28 +544,17 @@ TALER_KYCLOGIC_provider_to_logic ( /** - * Tuple with information about a KYC check to perform. Note that it will - * have references into the legitimization rule set provided to - * #TALER_KYCLOGIC_requirements_to_check() and thus has a lifetime that - * matches the legitimization rule set. + * Find default measure @a measure_name. + * + * @param measure_name name of measure to find + * @param[out] kcc initialized with KYC check data + * for the default measure + * @return #GNUNET_OK on success */ -struct TALER_KYCLOGIC_KycCheckContext -{ - /** - * KYC check to perform. - */ - const struct TALER_KYCLOGIC_KycCheck *check; - - /** - * Context for the check. Can be NULL. - */ - const json_t *context; - - /** - * Name of the AML program. - */ - char *prog_name; -}; +enum GNUNET_GenericReturnValue +TALER_KYCLOGIC_get_default_measure ( + const char *measure_name, + struct TALER_KYCLOGIC_KycCheckContext *kcc); /** @@ -741,6 +792,30 @@ TALER_KYCLOGIC_run_aml_program ( /** + * Run AML program @a prog_name with the given @a context. + * + * @param prog_name name of AML program to run + * @param attributes attributes to run with + * @param aml_history AML history of the account + * @param kyc_history KYC history of the account + * @param context context to run with + * @param aprc function to call with the result + * @param aprc_cls closure for @a aprc + * @return NULL if @a jmeasures is invalid for the + * selected @a measure_index or @a attributes + */ +struct TALER_KYCLOGIC_AmlProgramRunnerHandle * +TALER_KYCLOGIC_run_aml_program2 ( + const char *prog_name, + const json_t *attributes, + const json_t *aml_history, + const json_t *kyc_history, + const json_t *context, + TALER_KYCLOGIC_AmlProgramResultCallback aprc, + void *aprc_cls); + + +/** * Cancel running AML program. * * @param[in] aprh handle of program to cancel diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -749,6 +749,40 @@ TALER_KYCLOGIC_rule2s ( } +const char * +TALER_KYCLOGIC_status2s (enum TALER_KYCLOGIC_KycStatus status) +{ + switch (status) + { + case TALER_KYCLOGIC_STATUS_SUCCESS: + return "success"; + case TALER_KYCLOGIC_STATUS_USER: + return "user"; + case TALER_KYCLOGIC_STATUS_PROVIDER: + return "provider"; + case TALER_KYCLOGIC_STATUS_FAILED: + return "failed"; + case TALER_KYCLOGIC_STATUS_PENDING: + return "pending"; + case TALER_KYCLOGIC_STATUS_ABORTED: + return "aborted"; + case TALER_KYCLOGIC_STATUS_USER_PENDING: + return "pending with user"; + case TALER_KYCLOGIC_STATUS_PROVIDER_PENDING: + return "pending at provider"; + case TALER_KYCLOGIC_STATUS_USER_ABORTED: + return "aborted by user"; + case TALER_KYCLOGIC_STATUS_PROVIDER_FAILED: + return "failed by provider"; + case TALER_KYCLOGIC_STATUS_KEEP: + return "keep"; + case TALER_KYCLOGIC_STATUS_INTERNAL_ERROR: + return "internal error"; + } + return "unknown status"; +} + + json_t * TALER_KYCLOGIC_rules_to_limits (const json_t *jrules) { @@ -920,6 +954,38 @@ TALER_KYCLOGIC_rule_to_measures (const struct TALER_KYCLOGIC_KycRule *r) } +json_t * +TALER_KYCLOGIC_check_to_measures ( + const struct TALER_KYCLOGIC_KycCheckContext *kcc) +{ + const struct TALER_KYCLOGIC_KycCheck *check + = kcc->check; + json_t *jmeasures; + json_t *mi; + + jmeasures = json_array (); + GNUNET_assert (NULL != jmeasures); + mi = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("check_name", + check->check_name), + GNUNET_JSON_pack_string ("prog_name", + kcc->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("context", + (json_t *) kcc->context))); + GNUNET_assert (0 == + json_array_append_new (jmeasures, + mi)); + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("measures", + jmeasures), + GNUNET_JSON_pack_bool ("is_and_combinator", + true), + GNUNET_JSON_pack_bool ("verboten", + false)); +} + + uint32_t TALER_KYCLOGIC_rule2priority (const struct TALER_KYCLOGIC_KycRule *r) { @@ -2322,6 +2388,49 @@ TALER_KYCLOGIC_provider_to_logic ( enum GNUNET_GenericReturnValue +TALER_KYCLOGIC_get_default_measure ( + const char *measure_name, + struct TALER_KYCLOGIC_KycCheckContext *kcc) +{ + const struct TALER_KYCLOGIC_Measure *measure; + + measure = find_measure (&default_rules, + measure_name); + if (NULL == measure) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Default measure `%s' unknown\n", + measure_name); + return GNUNET_SYSERR; + } + if (0 == strcasecmp (measure->check_name, + "SKIP")) + { + kcc->check = NULL; + kcc->prog_name = measure->prog_name; + kcc->context = measure->context; + return GNUNET_OK; + } + + for (unsigned int i = 0; i<num_kyc_checks; i++) + if (0 == strcmp (measure->check_name, + kyc_checks[i]->check_name)) + { + kcc->check = kyc_checks[i]; + kcc->prog_name = measure->prog_name; + kcc->context = measure->context; + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Check `%s' unknown (but required by measure %s)\n", + measure->check_name, + measure_name); + return GNUNET_SYSERR; +} + + +// FIXME: not used in 'main' exchange logic! +enum GNUNET_GenericReturnValue TALER_KYCLOGIC_requirements_to_check ( const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs, const struct TALER_KYCLOGIC_KycRule *kyc_rule, @@ -2331,6 +2440,8 @@ TALER_KYCLOGIC_requirements_to_check ( bool found = false; const struct TALER_KYCLOGIC_Measure *measure = NULL; + if (NULL == lrs) + lrs = &default_rules; if (NULL == measure_name) { /* No measure selected, return the "new_check" */ @@ -2342,7 +2453,7 @@ TALER_KYCLOGIC_requirements_to_check ( } else { - kcc->check =NULL; + kcc->check = NULL; } kcc->prog_name = measure->prog_name; kcc->context = measure->context; @@ -3135,9 +3246,7 @@ TALER_KYCLOGIC_run_aml_program ( TALER_KYCLOGIC_AmlProgramResultCallback aprc, void *aprc_cls) { - struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh; const json_t *context; - struct TALER_KYCLOGIC_AmlProgram *prog; const char *check_name; const char *prog_name; @@ -3155,6 +3264,29 @@ TALER_KYCLOGIC_run_aml_program ( return NULL; } } + return TALER_KYCLOGIC_run_aml_program2 (prog_name, + attributes, + aml_history, + kyc_history, + context, + aprc, + aprc_cls); +} + + +struct TALER_KYCLOGIC_AmlProgramRunnerHandle * +TALER_KYCLOGIC_run_aml_program2 ( + const char *prog_name, + const json_t *attributes, + const json_t *aml_history, + const json_t *kyc_history, + const json_t *context, + TALER_KYCLOGIC_AmlProgramResultCallback aprc, + void *aprc_cls) +{ + struct TALER_KYCLOGIC_AmlProgramRunnerHandle *aprh; + struct TALER_KYCLOGIC_AmlProgram *prog; + prog = find_program (prog_name); if (NULL == prog) {