begin API change to allow AML officers to trigger KYC process

This commit is contained in:
Christian Grothoff 2023-02-14 14:26:00 +01:00
parent 437e6ec86a
commit afe3f70d33
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
13 changed files with 90 additions and 4 deletions

View File

@ -50,6 +50,7 @@ TEH_handler_post_aml_decision (
uint32_t new_state32;
enum TALER_AmlDecisionState new_state;
struct TALER_AmlOfficerSignatureP officer_sig;
json_t *kyc_requirements = NULL;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("officer_sig",
&officer_sig),
@ -64,6 +65,10 @@ TEH_handler_post_aml_decision (
&decision_time),
GNUNET_JSON_spec_uint32 ("new_state",
&new_state32),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("kyc_requirements",
&kyc_requirements),
NULL),
GNUNET_JSON_spec_end ()
};
@ -89,6 +94,7 @@ TEH_handler_post_aml_decision (
&new_threshold,
&h_payto,
new_state,
kyc_requirements,
officer_pub,
&officer_sig))
{
@ -99,6 +105,8 @@ TEH_handler_post_aml_decision (
TALER_EC_EXCHANGE_AML_DECISION_ADD_SIGNATURE_INVALID,
NULL);
}
// FIXME: check kyc_requirements is well-formed!
{
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Timestamp last_date;
@ -112,6 +120,7 @@ TEH_handler_post_aml_decision (
new_state,
decision_time,
justification,
kyc_requirements,
officer_pub,
&officer_sig,
&invalid_officer,
@ -127,6 +136,11 @@ TEH_handler_post_aml_decision (
TALER_EC_GENERIC_DB_STORE_FAILED,
"add aml_decision");
}
if (NULL != kyc_requirements)
{
// FIXME: act on these!
}
if (invalid_officer)
{
GNUNET_break_op (0);

View File

@ -32,6 +32,7 @@ BEGIN
',new_status INT4 NOT NULL DEFAULT(0)'
',decision_time INT8 NOT NULL DEFAULT(0)'
',justification VARCHAR NOT NULL'
',kyc_requirements VARCHAR'
',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)'
',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)'
') %s ;'
@ -80,6 +81,12 @@ BEGIN
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Additional KYC requirements imposed by the AML staff member. Serialized JSON array of strings.'
,'kyc_requirements'
,table_name
,partition_suffix
);
PERFORM comment_partitioned_column(
'Signature key of the staff member affirming the AML decision; of type AML_DECISION'
,'decider_sig'
@ -114,7 +121,6 @@ BEGIN
);
END $$;
-- FIXME: also have INSERT on AML decisions to update AML status!
INSERT INTO exchange_tables
(name

View File

@ -24,6 +24,7 @@ CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision(
IN in_decider_pub BYTEA,
IN in_decider_sig BYTEA,
IN in_notify_s VARCHAR,
IN in_kyc_requirements VARCHAR,
OUT out_invalid_officer BOOLEAN,
OUT out_last_date INT8)
LANGUAGE plpgsql
@ -84,6 +85,7 @@ INSERT INTO exchange.aml_history
,new_status
,decision_time
,justification
,kyc_requirements
,decider_pub
,decider_sig
) VALUES
@ -93,6 +95,7 @@ INSERT INTO exchange.aml_history
,in_new_status
,in_decision_time
,in_justification
,in_kyc_requirements
,in_decider_pub
,in_decider_sig);
@ -116,5 +119,5 @@ END IF;
END $$;
COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, INT8, INT4, INT4, INT8, VARCHAR, BYTEA, BYTEA, VARCHAR)
COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, INT8, INT4, INT4, INT8, VARCHAR, BYTEA, BYTEA, VARCHAR, VARCHAR)
IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table';

View File

@ -35,6 +35,7 @@ TEH_PG_insert_aml_decision (
enum TALER_AmlDecisionState new_status,
struct GNUNET_TIME_Timestamp decision_time,
const char *justification,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPublicKeyP *decider_pub,
const struct TALER_AmlOfficerSignatureP *decider_sig,
bool *invalid_officer,
@ -48,6 +49,9 @@ TEH_PG_insert_aml_decision (
.h_payto = *h_payto
};
char *notify_s = GNUNET_PG_get_event_notify_channel (&rep.header);
char *kyc_s = (NULL != kyc_requirements)
? json_dumps (kyc_requirements, JSON_COMPACT)
: NULL;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
TALER_PQ_query_param_amount (new_threshold),
@ -57,6 +61,9 @@ TEH_PG_insert_aml_decision (
GNUNET_PQ_query_param_auto_from_type (decider_pub),
GNUNET_PQ_query_param_auto_from_type (decider_sig),
GNUNET_PQ_query_param_string (notify_s),
NULL != kyc_requirements
? GNUNET_PQ_query_param_string (kyc_s)
: GNUNET_PQ_query_param_null (),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@ -74,11 +81,13 @@ 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);");
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);");
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"do_insert_aml_decision",
params,
rs);
GNUNET_free (notify_s);
if (NULL != kyc_s)
free (kyc_s);
return qs;
}

View File

@ -36,6 +36,7 @@
* @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 JSON array with KYC requirements
* @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
@ -51,6 +52,7 @@ TEH_PG_insert_aml_decision (
enum TALER_AmlDecisionState new_status,
struct GNUNET_TIME_Timestamp decision_time,
const char *justification,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPublicKeyP *decider_pub,
const struct TALER_AmlOfficerSignatureP *decider_sig,
bool *invalid_officer,

View File

@ -2406,6 +2406,8 @@ TALER_officer_aml_query_verify (
* @param h_payto payto URI hash of the account the
* decision is about
* @param new_state updated AML state
* @param kyc_requirements additional KYC requirements to
* impose, can be NULL
* @param officer_priv private key of AML officer
* @param[out] officer_sig where to write the signature
*/
@ -2416,6 +2418,7 @@ TALER_officer_aml_decision_sign (
const struct TALER_Amount *new_threshold,
const struct TALER_PaytoHashP *h_payto,
enum TALER_AmlDecisionState new_state,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
struct TALER_AmlOfficerSignatureP *officer_sig);
@ -2430,6 +2433,8 @@ TALER_officer_aml_decision_sign (
* @param h_payto payto URI hash of the account the
* decision is about
* @param new_state updated AML state
* @param kyc_requirements additional KYC requirements to
* impose, can be NULL
* @param officer_pub public key of AML officer
* @param officer_sig signature to verify
* @return #GNUNET_OK if the signature is valid
@ -2441,6 +2446,7 @@ TALER_officer_aml_decision_verify (
const struct TALER_Amount *new_threshold,
const struct TALER_PaytoHashP *h_payto,
enum TALER_AmlDecisionState new_state,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
const struct TALER_AmlOfficerSignatureP *officer_sig);

View File

@ -4608,6 +4608,7 @@ typedef void
* @param h_payto payto URI hash of the account the
* decision is about
* @param new_state updated AML state
* @param kyc_requirements JSON array of KYC requirements being imposed, NULL for none
* @param officer_priv private key of the deciding AML officer
* @param cb function to call with the exchange's result
* @param cb_cls closure for @a cb
@ -4622,6 +4623,7 @@ TALER_EXCHANGE_add_aml_decision (
const struct TALER_Amount *new_threshold,
const struct TALER_PaytoHashP *h_payto,
enum TALER_AmlDecisionState new_state,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
TALER_EXCHANGE_AddAmlDecisionCallback cb,
void *cb_cls);

View File

@ -6704,6 +6704,7 @@ struct TALER_EXCHANGEDB_Plugin
enum TALER_AmlDecisionState new_status,
struct GNUNET_TIME_Timestamp decision_time,
const char *justification,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPublicKeyP *decider_pub,
const struct TALER_AmlOfficerSignatureP *decider_sig,
bool *invalid_officer,

View File

@ -2741,6 +2741,7 @@ TALER_TESTING_cmd_set_officer (
* @param new_threshold new threshold to set
* @param justification justification given for the decision
* @param new_state new AML state for the account
* @param kyc_requirement KYC requirement to impose
* @param expected_response expected HTTP return status
* @return the command
*/
@ -2752,6 +2753,7 @@ TALER_TESTING_cmd_take_aml_decision (
const char *new_threshold,
const char *justification,
enum TALER_AmlDecisionState new_state,
const char *kyc_requirement,
unsigned int expected_response);

View File

@ -132,6 +132,7 @@ TALER_EXCHANGE_add_aml_decision (
const struct TALER_Amount *new_threshold,
const struct TALER_PaytoHashP *h_payto,
enum TALER_AmlDecisionState new_state,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
TALER_EXCHANGE_AddAmlDecisionCallback cb,
void *cb_cls)
@ -149,6 +150,7 @@ TALER_EXCHANGE_add_aml_decision (
new_threshold,
h_payto,
new_state,
kyc_requirements,
officer_priv,
&officer_sig);
wh = GNUNET_new (struct TALER_EXCHANGE_AddAmlDecision);
@ -190,6 +192,9 @@ TALER_EXCHANGE_add_aml_decision (
h_payto),
GNUNET_JSON_pack_uint64 ("new_state",
(uint32_t) new_state),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_array_incref ("kyc_requirements",
(json_t *) kyc_requirements)),
TALER_JSON_pack_amount ("new_threshold",
new_threshold),
GNUNET_JSON_pack_timestamp ("decision_time",

View File

@ -448,6 +448,7 @@ run (void *cls,
"EUR:10000",
"party time",
TALER_AML_NORMAL,
NULL,
MHD_HTTP_FORBIDDEN),
/* Check that no decision was taken, but that we are allowed
to read this information */
@ -468,6 +469,7 @@ run (void *cls,
"EUR:10000",
"party time",
TALER_AML_NORMAL,
NULL,
MHD_HTTP_NO_CONTENT),
TALER_TESTING_cmd_check_aml_decisions ("check-decisions-one-normal",
"create-aml-officer-1",
@ -489,6 +491,7 @@ run (void *cls,
"EUR:1000",
"party over",
TALER_AML_FROZEN,
NULL,
MHD_HTTP_NO_CONTENT),
TALER_TESTING_cmd_check_aml_decisions ("check-decisions-one-frozen",
"create-aml-officer-1",

View File

@ -72,6 +72,11 @@ struct AmlDecisionState
*/
const char *justification;
/**
* KYC requirement to add.
*/
const char *kyc_requirement;
/**
* Threshold transaction amount.
*/
@ -133,6 +138,7 @@ take_aml_decision_run (void *cls,
const struct TALER_PaytoHashP *h_payto;
const struct TALER_AmlOfficerPrivateKeyP *officer_priv;
const struct TALER_TESTING_Command *ref;
json_t *kyc_requirements = NULL;
(void) cmd;
now = GNUNET_TIME_timestamp_get ();
@ -160,6 +166,15 @@ take_aml_decision_run (void *cls,
TALER_TESTING_get_trait_officer_priv (ref,
&officer_priv));
ds->h_payto = *h_payto;
if (NULL != ds->kyc_requirement)
{
kyc_requirements = json_array ();
GNUNET_assert (NULL != kyc_requirements);
GNUNET_assert (0 ==
json_array_append (kyc_requirements,
json_string (ds->kyc_requirement)));
}
ds->dh = TALER_EXCHANGE_add_aml_decision (
is->ctx,
is->exchange_url,
@ -168,9 +183,11 @@ take_aml_decision_run (void *cls,
&ds->new_threshold,
h_payto,
ds->new_state,
kyc_requirements,
officer_priv,
&take_aml_decision_cb,
ds);
json_decref (kyc_requirements);
if (NULL == ds->dh)
{
GNUNET_break (0);
@ -246,6 +263,7 @@ TALER_TESTING_cmd_take_aml_decision (
const char *new_threshold,
const char *justification,
enum TALER_AmlDecisionState new_state,
const char *kyc_requirement,
unsigned int expected_response)
{
struct AmlDecisionState *ds;
@ -253,6 +271,7 @@ TALER_TESTING_cmd_take_aml_decision (
ds = GNUNET_new (struct AmlDecisionState);
ds->officer_ref_cmd = ref_officer;
ds->account_ref_cmd = ref_operation;
ds->kyc_requirement = kyc_requirement;
if (GNUNET_OK !=
TALER_string_to_amount (new_threshold,
&ds->new_threshold))

View File

@ -56,6 +56,12 @@ struct TALER_AmlDecisionPS
*/
struct TALER_PaytoHashP h_payto GNUNET_PACKED;
/**
* Hash over JSON array with KYC requirements that were imposed. All zeros
* for none.
*/
struct GNUNET_HashCode h_kyc_requirements;
/**
* What is the new AML status?
*/
@ -72,6 +78,7 @@ TALER_officer_aml_decision_sign (
const struct TALER_Amount *new_threshold,
const struct TALER_PaytoHashP *h_payto,
enum TALER_AmlDecisionState new_state,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
struct TALER_AmlOfficerSignatureP *officer_sig)
{
@ -87,6 +94,9 @@ TALER_officer_aml_decision_sign (
&ad.h_justification);
TALER_amount_hton (&ad.new_threshold,
new_threshold);
if (NULL != kyc_requirements)
TALER_json_hash (kyc_requirements,
&ad.h_kyc_requirements);
GNUNET_CRYPTO_eddsa_sign (&officer_priv->eddsa_priv,
&ad,
&officer_sig->eddsa_signature);
@ -100,6 +110,7 @@ TALER_officer_aml_decision_verify (
const struct TALER_Amount *new_threshold,
const struct TALER_PaytoHashP *h_payto,
enum TALER_AmlDecisionState new_state,
const json_t *kyc_requirements,
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
const struct TALER_AmlOfficerSignatureP *officer_sig)
{
@ -115,6 +126,9 @@ TALER_officer_aml_decision_verify (
&ad.h_justification);
TALER_amount_hton (&ad.new_threshold,
new_threshold);
if (NULL != kyc_requirements)
TALER_json_hash (kyc_requirements,
&ad.h_kyc_requirements);
return GNUNET_CRYPTO_eddsa_verify (
TALER_SIGNATURE_AML_DECISION,
&ad,