From aa5e7d2ad5e712434f32ab41b63d53bb897c6105 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 17 Feb 2023 18:24:20 +0100 Subject: more towards actually allowing AML decisions to trigger KYC --- src/exchange/taler-exchange-httpd_aml-decision.c | 85 ++++++++++++++++++++-- src/exchange/taler-exchange-httpd_batch-withdraw.c | 15 +++- src/exchange/taler-exchange-httpd_kyc-check.c | 13 +++- src/exchange/taler-exchange-httpd_kyc-wallet.c | 2 +- src/exchangedb/0003-aml_history.sql | 7 ++ src/exchangedb/exchange_do_insert_aml_decision.sql | 5 +- src/exchangedb/pg_insert_aml_decision.c | 4 +- src/exchangedb/pg_insert_aml_decision.h | 2 + src/include/taler_exchangedb_plugin.h | 3 + src/include/taler_kyclogic_lib.h | 5 +- src/kyclogic/kyclogic_api.c | 31 +++++++- 11 files changed, 157 insertions(+), 15 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c index 0fd58b9ec..2830e54ed 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -103,6 +103,85 @@ make_aml_decision (void *cls, enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Timestamp last_date; bool invalid_officer; + uint64_t requirement_row = 0; + + if ( (NULL != dc->kyc_requirements) && + (0 != json_array_size (dc->kyc_requirements)) ) + { + char *res = NULL; + size_t idx; + json_t *req; + bool satisfied; + + json_array_foreach (dc->kyc_requirements, idx, req) + { + const char *r = json_string_value (req); + + if (NULL == res) + { + res = GNUNET_strdup (r); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s %s", + res, + r); + GNUNET_free (res); + res = tmp; + } + } + + { + json_t *kyc_details = NULL; + + qs = TALER_KYCLOGIC_check_satisfied ( + &res, + &dc->h_payto, + &kyc_details, + TEH_plugin->select_satisfied_kyc_processes, + TEH_plugin->cls, + &satisfied); + json_decref (kyc_details); + } + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + { + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_satisfied_kyc_processes"); + return GNUNET_DB_STATUS_HARD_ERROR; + } + return qs; + } + if (! satisfied) + { + qs = TEH_plugin->insert_kyc_requirement_for_account ( + TEH_plugin->cls, + res, + &dc->h_payto, + &requirement_row); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + { + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_requirement_for_account"); + return GNUNET_DB_STATUS_HARD_ERROR; + } + return qs; + } + } + GNUNET_free (res); + } qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls, &dc->h_payto, @@ -111,6 +190,7 @@ make_aml_decision (void *cls, dc->decision_time, dc->justification, dc->kyc_requirements, + requirement_row, dc->officer_pub, &dc->officer_sig, &invalid_officer, @@ -151,11 +231,6 @@ make_aml_decision (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } - if (NULL != dc->kyc_requirements) - { - // FIXME: act on these! - } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c index e6be54a58..493a2bc70 100644 --- a/src/exchange/taler-exchange-httpd_batch-withdraw.c +++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c @@ -210,7 +210,7 @@ batch_withdraw_transaction (void *cls, enum GNUNET_DB_QueryStatus qs; bool balance_ok = false; bool found = false; - const char *kyc_required; + char *kyc_required; struct TALER_PaytoHashP reserve_h_payto; wc->now = GNUNET_TIME_timestamp_get (); @@ -349,11 +349,22 @@ batch_withdraw_transaction (void *cls, { /* insert KYC requirement into DB! */ wc->kyc.ok = false; - return TEH_plugin->insert_kyc_requirement_for_account ( + qs = TEH_plugin->insert_kyc_requirement_for_account ( TEH_plugin->cls, kyc_required, &wc->h_payto, &wc->kyc.requirement_row); + GNUNET_free (kyc_required); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_requirement_for_account"); + } + return qs; } wc->kyc.ok = true; qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls, diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c index 07b8dc661..57ac8389d 100644 --- a/src/exchange/taler-exchange-httpd_kyc-check.c +++ b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -332,7 +332,7 @@ kyc_check (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } qs = TALER_KYCLOGIC_check_satisfied ( - requirements, + &requirements, &h_payto, &kyp->kyc_details, TEH_plugin->select_satisfied_kyc_processes, @@ -389,6 +389,17 @@ kyc_check (void *cls, NULL, NULL, &kyp->process_row); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_requirement_process"); + return GNUNET_DB_STATUS_HARD_ERROR; + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Initiating KYC check with logic %s\n", kyp->ih_logic->name); diff --git a/src/exchange/taler-exchange-httpd_kyc-wallet.c b/src/exchange/taler-exchange-httpd_kyc-wallet.c index e56204ef5..77f2dea78 100644 --- a/src/exchange/taler-exchange-httpd_kyc-wallet.c +++ b/src/exchange/taler-exchange-httpd_kyc-wallet.c @@ -237,7 +237,7 @@ TEH_handler_kyc_wallet ( NULL, 0); } - GNUNET_free (kyc.required); + GNUNET_free (krc.required); return TEH_RESPONSE_reply_kyc_required (rc->connection, &krc.h_payto, &krc.kyc); diff --git a/src/exchangedb/0003-aml_history.sql b/src/exchangedb/0003-aml_history.sql index b411a6fb1..e57a2313f 100644 --- a/src/exchangedb/0003-aml_history.sql +++ b/src/exchangedb/0003-aml_history.sql @@ -33,6 +33,7 @@ BEGIN ',decision_time INT8 NOT NULL DEFAULT(0)' ',justification VARCHAR NOT NULL' ',kyc_requirements VARCHAR' + ',kyc_req_row INT8 NOT NULL DEFAULT(0)' ',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)' ',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)' ') %s ;' @@ -87,6 +88,12 @@ BEGIN ,table_name ,partition_suffix ); + PERFORM comment_partitioned_column( + 'Row in the KYC table for this KYC requirement, 0 for none.' + ,'kyc_req_row' + ,table_name + ,partition_suffix + ); PERFORM comment_partitioned_column( 'Signature key of the staff member affirming the AML decision; of type AML_DECISION' ,'decider_sig' diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql index 8e22c57f8..00f803752 100644 --- a/src/exchangedb/exchange_do_insert_aml_decision.sql +++ b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -25,6 +25,7 @@ CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision( IN in_decider_sig BYTEA, IN in_notify_s VARCHAR, IN in_kyc_requirements VARCHAR, + IN in_requirement_row INT8, OUT out_invalid_officer BOOLEAN, OUT out_last_date INT8) LANGUAGE plpgsql @@ -86,6 +87,7 @@ INSERT INTO exchange.aml_history ,decision_time ,justification ,kyc_requirements + ,kyc_req_row ,decider_pub ,decider_sig ) VALUES @@ -96,6 +98,7 @@ INSERT INTO exchange.aml_history ,in_decision_time ,in_justification ,in_kyc_requirements + ,in_requirement_row ,in_decider_pub ,in_decider_sig); @@ -119,5 +122,5 @@ END IF; END $$; -COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, INT8, INT4, INT4, INT8, VARCHAR, BYTEA, BYTEA, VARCHAR, VARCHAR) +COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, INT8, INT4, INT4, INT8, VARCHAR, BYTEA, BYTEA, VARCHAR, VARCHAR, INT8) IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table'; diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c index 5a1b71235..62645c2a2 100644 --- a/src/exchangedb/pg_insert_aml_decision.c +++ b/src/exchangedb/pg_insert_aml_decision.c @@ -36,6 +36,7 @@ TEH_PG_insert_aml_decision ( struct GNUNET_TIME_Timestamp decision_time, const char *justification, const json_t *kyc_requirements, + uint64_t requirements_row, const struct TALER_AmlOfficerPublicKeyP *decider_pub, const struct TALER_AmlOfficerSignatureP *decider_sig, bool *invalid_officer, @@ -64,6 +65,7 @@ TEH_PG_insert_aml_decision ( NULL != kyc_requirements ? GNUNET_PQ_query_param_string (kyc_s) : GNUNET_PQ_query_param_null (), + GNUNET_PQ_query_param_uint64 (&requirements_row), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -81,7 +83,7 @@ TEH_PG_insert_aml_decision ( " out_invalid_officer" ",out_last_date" " FROM exchange_do_insert_aml_decision" - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);"); + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "do_insert_aml_decision", params, diff --git a/src/exchangedb/pg_insert_aml_decision.h b/src/exchangedb/pg_insert_aml_decision.h index 4d4ca6e54..073a2f50a 100644 --- a/src/exchangedb/pg_insert_aml_decision.h +++ b/src/exchangedb/pg_insert_aml_decision.h @@ -37,6 +37,7 @@ * @param decision_time when was the decision made * @param justification human-readable text justifying the decision * @param kyc_requirements JSON array with KYC requirements + * @param requirements_row row in the KYC table for this process, 0 for none * @param decider_pub public key of the staff member * @param decider_sig signature of the staff member * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now @@ -53,6 +54,7 @@ TEH_PG_insert_aml_decision ( struct GNUNET_TIME_Timestamp decision_time, const char *justification, const json_t *kyc_requirements, + uint64_t requirements_row, const struct TALER_AmlOfficerPublicKeyP *decider_pub, const struct TALER_AmlOfficerSignatureP *decider_sig, bool *invalid_officer, diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index f4397e296..44dd912f8 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -6689,6 +6689,8 @@ struct TALER_EXCHANGEDB_Plugin * @param new_status AML decision status * @param decision_time when was the decision made * @param justification human-readable text justifying the decision + * @param kyc_requirements specific KYC requiremnts being imposed + * @param requirements_row row in the KYC table for this process, 0 for none * @param decider_pub public key of the staff member * @param decider_sig signature of the staff member * @param[out] invalid_officer set to TRUE if @a decider_pub is not allowed to make decisions right now @@ -6705,6 +6707,7 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Timestamp decision_time, const char *justification, const json_t *kyc_requirements, + uint64_t requirements_row, const struct TALER_AmlOfficerPublicKeyP *decider_pub, const struct TALER_AmlOfficerSignatureP *decider_sig, bool *invalid_officer, diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h index 98233b359..34a429121 100644 --- a/src/include/taler_kyclogic_lib.h +++ b/src/include/taler_kyclogic_lib.h @@ -232,7 +232,8 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event, * Check if the @a requirements are now satsified for * @a h_payto account. * - * @param requirements space-spearated list of requirements + * @param[in,out] requirements space-spearated list of requirements, + * already satisfied requirements are removed from the list * @param h_payto hash over the account * @param[out] kyc_details if satisfied, set to what kind of * KYC information was collected @@ -242,7 +243,7 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event, * @return transaction status (from @a ki) */ enum GNUNET_DB_QueryStatus -TALER_KYCLOGIC_check_satisfied (const char *requirements, +TALER_KYCLOGIC_check_satisfied (char **requirements, const struct TALER_PaytoHashP *h_payto, json_t **kyc_details, TALER_KYCLOGIC_KycSatisfiedIterator ki, diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index e1aff0204..9f35743cd 100644 --- a/src/kyclogic/kyclogic_api.c +++ b/src/kyclogic/kyclogic_api.c @@ -1228,7 +1228,7 @@ TALER_KYCLOGIC_kyc_get_details ( enum GNUNET_DB_QueryStatus -TALER_KYCLOGIC_check_satisfied (const char *requirements, +TALER_KYCLOGIC_check_satisfied (char **requirements, const struct TALER_PaytoHashP *h_payto, json_t **kyc_details, TALER_KYCLOGIC_KycSatisfiedIterator ki, @@ -1244,13 +1244,14 @@ TALER_KYCLOGIC_check_satisfied (const char *requirements, return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } { - char *req = GNUNET_strdup (requirements); + char *req = *requirements; for (const char *tok = strtok (req, " "); NULL != tok; tok = strtok (NULL, " ")) needed[needed_cnt++] = add_check (tok); GNUNET_free (req); + *requirements = NULL; } { @@ -1286,6 +1287,32 @@ TALER_KYCLOGIC_check_satisfied (const char *requirements, } } *satisfied = (0 == needed_cnt); + + { + char *res = NULL; + + for (unsigned int i = 0; iname); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s %s", + res, + need->name); + GNUNET_free (res); + res = tmp; + } + } + *requirements = res; + } return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } -- cgit v1.2.3