more towards actually allowing AML decisions to trigger KYC

This commit is contained in:
Christian Grothoff 2023-02-17 18:24:20 +01:00
parent 86e0f2c70d
commit aa5e7d2ad5
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
11 changed files with 157 additions and 15 deletions

View File

@ -103,6 +103,85 @@ make_aml_decision (void *cls,
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Timestamp last_date; struct GNUNET_TIME_Timestamp last_date;
bool invalid_officer; 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, qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls,
&dc->h_payto, &dc->h_payto,
@ -111,6 +190,7 @@ make_aml_decision (void *cls,
dc->decision_time, dc->decision_time,
dc->justification, dc->justification,
dc->kyc_requirements, dc->kyc_requirements,
requirement_row,
dc->officer_pub, dc->officer_pub,
&dc->officer_sig, &dc->officer_sig,
&invalid_officer, &invalid_officer,
@ -151,11 +231,6 @@ make_aml_decision (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if (NULL != dc->kyc_requirements)
{
// FIXME: act on these!
}
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
} }

View File

@ -210,7 +210,7 @@ batch_withdraw_transaction (void *cls,
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
bool balance_ok = false; bool balance_ok = false;
bool found = false; bool found = false;
const char *kyc_required; char *kyc_required;
struct TALER_PaytoHashP reserve_h_payto; struct TALER_PaytoHashP reserve_h_payto;
wc->now = GNUNET_TIME_timestamp_get (); wc->now = GNUNET_TIME_timestamp_get ();
@ -349,11 +349,22 @@ batch_withdraw_transaction (void *cls,
{ {
/* insert KYC requirement into DB! */ /* insert KYC requirement into DB! */
wc->kyc.ok = false; wc->kyc.ok = false;
return TEH_plugin->insert_kyc_requirement_for_account ( qs = TEH_plugin->insert_kyc_requirement_for_account (
TEH_plugin->cls, TEH_plugin->cls,
kyc_required, kyc_required,
&wc->h_payto, &wc->h_payto,
&wc->kyc.requirement_row); &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; wc->kyc.ok = true;
qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls, qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,

View File

@ -332,7 +332,7 @@ kyc_check (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
qs = TALER_KYCLOGIC_check_satisfied ( qs = TALER_KYCLOGIC_check_satisfied (
requirements, &requirements,
&h_payto, &h_payto,
&kyp->kyc_details, &kyp->kyc_details,
TEH_plugin->select_satisfied_kyc_processes, TEH_plugin->select_satisfied_kyc_processes,
@ -389,6 +389,17 @@ kyc_check (void *cls,
NULL, NULL,
NULL, NULL,
&kyp->process_row); &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, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Initiating KYC check with logic %s\n", "Initiating KYC check with logic %s\n",
kyp->ih_logic->name); kyp->ih_logic->name);

View File

@ -237,7 +237,7 @@ TEH_handler_kyc_wallet (
NULL, NULL,
0); 0);
} }
GNUNET_free (kyc.required); GNUNET_free (krc.required);
return TEH_RESPONSE_reply_kyc_required (rc->connection, return TEH_RESPONSE_reply_kyc_required (rc->connection,
&krc.h_payto, &krc.h_payto,
&krc.kyc); &krc.kyc);

View File

@ -33,6 +33,7 @@ BEGIN
',decision_time INT8 NOT NULL DEFAULT(0)' ',decision_time INT8 NOT NULL DEFAULT(0)'
',justification VARCHAR NOT NULL' ',justification VARCHAR NOT NULL'
',kyc_requirements VARCHAR' ',kyc_requirements VARCHAR'
',kyc_req_row INT8 NOT NULL DEFAULT(0)'
',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)' ',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)'
',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)' ',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)'
') %s ;' ') %s ;'
@ -87,6 +88,12 @@ BEGIN
,table_name ,table_name
,partition_suffix ,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( PERFORM comment_partitioned_column(
'Signature key of the staff member affirming the AML decision; of type AML_DECISION' 'Signature key of the staff member affirming the AML decision; of type AML_DECISION'
,'decider_sig' ,'decider_sig'

View File

@ -25,6 +25,7 @@ CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision(
IN in_decider_sig BYTEA, IN in_decider_sig BYTEA,
IN in_notify_s VARCHAR, IN in_notify_s VARCHAR,
IN in_kyc_requirements VARCHAR, IN in_kyc_requirements VARCHAR,
IN in_requirement_row INT8,
OUT out_invalid_officer BOOLEAN, OUT out_invalid_officer BOOLEAN,
OUT out_last_date INT8) OUT out_last_date INT8)
LANGUAGE plpgsql LANGUAGE plpgsql
@ -86,6 +87,7 @@ INSERT INTO exchange.aml_history
,decision_time ,decision_time
,justification ,justification
,kyc_requirements ,kyc_requirements
,kyc_req_row
,decider_pub ,decider_pub
,decider_sig ,decider_sig
) VALUES ) VALUES
@ -96,6 +98,7 @@ INSERT INTO exchange.aml_history
,in_decision_time ,in_decision_time
,in_justification ,in_justification
,in_kyc_requirements ,in_kyc_requirements
,in_requirement_row
,in_decider_pub ,in_decider_pub
,in_decider_sig); ,in_decider_sig);
@ -119,5 +122,5 @@ END IF;
END $$; 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'; IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table';

View File

@ -36,6 +36,7 @@ TEH_PG_insert_aml_decision (
struct GNUNET_TIME_Timestamp decision_time, struct GNUNET_TIME_Timestamp decision_time,
const char *justification, const char *justification,
const json_t *kyc_requirements, const json_t *kyc_requirements,
uint64_t requirements_row,
const struct TALER_AmlOfficerPublicKeyP *decider_pub, const struct TALER_AmlOfficerPublicKeyP *decider_pub,
const struct TALER_AmlOfficerSignatureP *decider_sig, const struct TALER_AmlOfficerSignatureP *decider_sig,
bool *invalid_officer, bool *invalid_officer,
@ -64,6 +65,7 @@ TEH_PG_insert_aml_decision (
NULL != kyc_requirements NULL != kyc_requirements
? GNUNET_PQ_query_param_string (kyc_s) ? GNUNET_PQ_query_param_string (kyc_s)
: GNUNET_PQ_query_param_null (), : GNUNET_PQ_query_param_null (),
GNUNET_PQ_query_param_uint64 (&requirements_row),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
@ -81,7 +83,7 @@ TEH_PG_insert_aml_decision (
" out_invalid_officer" " out_invalid_officer"
",out_last_date" ",out_last_date"
" FROM exchange_do_insert_aml_decision" " 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, qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"do_insert_aml_decision", "do_insert_aml_decision",
params, params,

View File

@ -37,6 +37,7 @@
* @param decision_time when was the decision made * @param decision_time when was the decision made
* @param justification human-readable text justifying the decision * @param justification human-readable text justifying the decision
* @param kyc_requirements JSON array with KYC requirements * @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_pub public key of the staff member
* @param decider_sig signature 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 * @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, struct GNUNET_TIME_Timestamp decision_time,
const char *justification, const char *justification,
const json_t *kyc_requirements, const json_t *kyc_requirements,
uint64_t requirements_row,
const struct TALER_AmlOfficerPublicKeyP *decider_pub, const struct TALER_AmlOfficerPublicKeyP *decider_pub,
const struct TALER_AmlOfficerSignatureP *decider_sig, const struct TALER_AmlOfficerSignatureP *decider_sig,
bool *invalid_officer, bool *invalid_officer,

View File

@ -6689,6 +6689,8 @@ struct TALER_EXCHANGEDB_Plugin
* @param new_status AML decision status * @param new_status AML decision status
* @param decision_time when was the decision made * @param decision_time when was the decision made
* @param justification human-readable text justifying the decision * @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_pub public key of the staff member
* @param decider_sig signature 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 * @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, struct GNUNET_TIME_Timestamp decision_time,
const char *justification, const char *justification,
const json_t *kyc_requirements, const json_t *kyc_requirements,
uint64_t requirements_row,
const struct TALER_AmlOfficerPublicKeyP *decider_pub, const struct TALER_AmlOfficerPublicKeyP *decider_pub,
const struct TALER_AmlOfficerSignatureP *decider_sig, const struct TALER_AmlOfficerSignatureP *decider_sig,
bool *invalid_officer, bool *invalid_officer,

View File

@ -232,7 +232,8 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
* Check if the @a requirements are now satsified for * Check if the @a requirements are now satsified for
* @a h_payto account. * @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 h_payto hash over the account
* @param[out] kyc_details if satisfied, set to what kind of * @param[out] kyc_details if satisfied, set to what kind of
* KYC information was collected * KYC information was collected
@ -242,7 +243,7 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
* @return transaction status (from @a ki) * @return transaction status (from @a ki)
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
TALER_KYCLOGIC_check_satisfied (const char *requirements, TALER_KYCLOGIC_check_satisfied (char **requirements,
const struct TALER_PaytoHashP *h_payto, const struct TALER_PaytoHashP *h_payto,
json_t **kyc_details, json_t **kyc_details,
TALER_KYCLOGIC_KycSatisfiedIterator ki, TALER_KYCLOGIC_KycSatisfiedIterator ki,

View File

@ -1228,7 +1228,7 @@ TALER_KYCLOGIC_kyc_get_details (
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
TALER_KYCLOGIC_check_satisfied (const char *requirements, TALER_KYCLOGIC_check_satisfied (char **requirements,
const struct TALER_PaytoHashP *h_payto, const struct TALER_PaytoHashP *h_payto,
json_t **kyc_details, json_t **kyc_details,
TALER_KYCLOGIC_KycSatisfiedIterator ki, TALER_KYCLOGIC_KycSatisfiedIterator ki,
@ -1244,13 +1244,14 @@ TALER_KYCLOGIC_check_satisfied (const char *requirements,
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
} }
{ {
char *req = GNUNET_strdup (requirements); char *req = *requirements;
for (const char *tok = strtok (req, " "); for (const char *tok = strtok (req, " ");
NULL != tok; NULL != tok;
tok = strtok (NULL, " ")) tok = strtok (NULL, " "))
needed[needed_cnt++] = add_check (tok); needed[needed_cnt++] = add_check (tok);
GNUNET_free (req); GNUNET_free (req);
*requirements = NULL;
} }
{ {
@ -1286,6 +1287,32 @@ TALER_KYCLOGIC_check_satisfied (const char *requirements,
} }
} }
*satisfied = (0 == needed_cnt); *satisfied = (0 == needed_cnt);
{
char *res = NULL;
for (unsigned int i = 0; i<needed_cnt; i++)
{
const struct TALER_KYCLOGIC_KycCheck *need = needed[i];
if (NULL == res)
{
res = GNUNET_strdup (need->name);
}
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; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
} }