exchange

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

commit 2441001c9925987f4f0f953ef0be3fa987b9970a
parent 1d4025d708b7ebffaf1bfa6f271b8d75c71e1160
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 22 Jun 2025 00:02:14 +0200

allow prog_name to be NULL/missing for AML programs with checks of type INFO (#9874)

Diffstat:
Msrc/exchange/taler-exchange-httpd_config.h | 2+-
Msrc/exchange/taler-exchange-httpd_kyc-info.c | 13++++---------
Msrc/exchangedb/exchangedb_aml.c | 15+++++++++++++--
Msrc/include/taler/taler_exchange_service.h | 3++-
Msrc/include/taler/taler_kyclogic_lib.h | 5++---
Msrc/kyclogic/Makefile.am | 2+-
Msrc/kyclogic/kyclogic_api.c | 326++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/lib/exchange_api_add_aml_decision.c | 5+++--
Msrc/lib/exchange_api_handle.c | 4++--
Msrc/lib/exchange_api_lookup_aml_decisions.c | 6++++--
Msrc/testing/testing_api_cmd_take_aml_decision.c | 6++++--
11 files changed, 252 insertions(+), 135 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_config.h b/src/exchange/taler-exchange-httpd_config.h @@ -41,7 +41,7 @@ * * Returned via both /config and /keys endpoints. */ -#define EXCHANGE_PROTOCOL_VERSION "29:0:7" +#define EXCHANGE_PROTOCOL_VERSION "30:0:8" /** diff --git a/src/exchange/taler-exchange-httpd_kyc-info.c b/src/exchange/taler-exchange-httpd_kyc-info.c @@ -365,13 +365,10 @@ resume_with_reply (struct KycPoller *kyp, json_array_foreach ((json_t *) measures, i, mi) { const char *check_name; - const char *prog_name; const json_t *context = NULL; struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_string ("check_name", &check_name), - GNUNET_JSON_spec_string ("prog_name", - &prog_name), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("context", &context), @@ -395,9 +392,8 @@ resume_with_reply (struct KycPoller *kyp, return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Found required check `%s' with program `%s'\n", - check_name, - prog_name); + "Found required check `%s'\n", + check_name); if (NULL != context) { json_dumpf (context, @@ -443,7 +439,6 @@ resume_with_reply (struct KycPoller *kyp, } kri = TALER_KYCLOGIC_measure_to_requirement ( check_name, - prog_name, context, &kyp->access_token, i, @@ -576,8 +571,8 @@ current_rules_cb ( GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_array_steal ( "voluntary_measures", - TALER_KYCLOGIC_voluntary_measures - (kyp->lrs)) + TALER_KYCLOGIC_voluntary_measures ( + kyp->lrs)) ))); return; } diff --git a/src/exchangedb/exchangedb_aml.c b/src/exchangedb/exchangedb_aml.c @@ -477,7 +477,18 @@ run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru, /* Free previous one, in case we are iterating... */ GNUNET_free (ru->aml_program_name); - ru->aml_program_name = GNUNET_strdup (m->prog_name); + if (NULL != m->prog_name) + { + ru->aml_program_name = GNUNET_strdup (m->prog_name); + } + else + { + /* How do we get to run a measure if the check type + is INFO (which is the only case where prog_name + is allowed to be NULL?) */ + GNUNET_break (0); + ru->aml_program_name = NULL; + } qs = ru->plugin->set_aml_lock ( ru->plugin->cls, &ru->account, @@ -516,7 +527,7 @@ run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru, return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Check is of type 'skip', running AML program %s.\n", + "Check is of type 'SKIP', running AML program %s.\n", m->prog_name); GNUNET_assert (NULL == ru->t); ru->amlh = TALER_KYCLOGIC_run_aml_program3 ( diff --git a/src/include/taler/taler_exchange_service.h b/src/include/taler/taler_exchange_service.h @@ -5310,7 +5310,8 @@ struct TALER_EXCHANGE_MeasureInformation const char *check_name; /** - * Name of the AML program. + * Name of the AML program, can be NULL if @e check_name + * is for a check of type "INFO". */ const char *prog_name; diff --git a/src/include/taler/taler_kyclogic_lib.h b/src/include/taler/taler_kyclogic_lib.h @@ -502,7 +502,8 @@ TALER_KYCLOGIC_rules_to_limits (const json_t *jrules, * @param jmeasures a LegitimizationMeasures object * @param measure_index an index into the measures * @param[out] check_name set to the name of the check - * @param[out] prog_name set to the name of the program + * @param[out] prog_name set to the name of the program, + * possibly NULL for "SKIP" checks * @param[out] context set to the measure context * (or NULL if there is no context) * @return #TALER_EC_NONE on success @@ -541,7 +542,6 @@ TALER_KYCLOGIC_check_form ( * KycRequirementInformation used by the client. * * @param check_name the prescribed check - * @param prog_name the program to run * @param context context to return, can be NULL * @param access_token access token for the measure * @param offset offset of the measure @@ -551,7 +551,6 @@ TALER_KYCLOGIC_check_form ( json_t * TALER_KYCLOGIC_measure_to_requirement ( const char *check_name, - const char *prog_name, const json_t *context, const struct TALER_AccountAccessTokenP *access_token, size_t offset, diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am @@ -62,7 +62,7 @@ libtalerkyclogic_la_LIBADD = \ -ljansson \ $(XLIB) libtalerkyclogic_la_LDFLAGS = \ - -version-info 2:0:0 \ + -version-info 3:0:0 \ -no-undefined diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -452,6 +452,11 @@ find_check (const char *check_name) static struct TALER_KYCLOGIC_AmlProgram * find_program (const char *program_name) { + if (NULL == program_name) + { + GNUNET_break (0); + return NULL; + } for (unsigned int i = 0; i<num_aml_programs; i++) { struct TALER_KYCLOGIC_AmlProgram *program @@ -504,96 +509,126 @@ static bool check_measure (const struct TALER_KYCLOGIC_Measure *measure) { const struct TALER_KYCLOGIC_KycCheck *check; - const struct TALER_KYCLOGIC_AmlProgram *program; - program = find_program (measure->prog_name); - if (NULL == program) + check = find_check (measure->check_name); + if ( (NULL == check) && + (0 != strcasecmp (measure->check_name, + "SKIP")) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown program `%s' used in measure `%s'\n", - measure->prog_name, + "Unknown check `%s' used in measure `%s'\n", + measure->check_name, measure->measure_name); return false; } - for (unsigned int j = 0; j<program->num_required_contexts; j++) + if ( (NULL == check) || + (TALER_KYCLOGIC_CT_INFO != check->type) ) { - const char *required_context = program->required_contexts[j]; + const struct TALER_KYCLOGIC_AmlProgram *program; - if (NULL == - json_object_get (measure->context, - required_context)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Measure `%s' lacks required context `%s' for AML program `%s'\n", - measure->measure_name, - required_context, - program->program_name); - return false; - } - } - if (0 == strcasecmp (measure->check_name, - "SKIP")) - { - if (0 != program->num_required_attributes) + program = find_program (measure->prog_name); + if (NULL == program) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "AML program `%s' of measure `%s' has required attributes, but check is of type `SKIP' and thus cannot provide any!\n", - program->program_name, + "Unknown program `%s' used in measure `%s'\n", + measure->prog_name, measure->measure_name); return false; } - return true; - } - check = find_check (measure->check_name); - if (NULL == check) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unknown check `%s' used in measure `%s'\n", - measure->check_name, - measure->measure_name); - return false; - } - for (unsigned int j = 0; j<program->num_required_attributes; j++) - { - const char *required_attribute = program->required_attributes[j]; - bool found = false; + for (unsigned int j = 0; j<program->num_required_contexts; j++) + { + const char *required_context = program->required_contexts[j]; - for (unsigned int i = 0; i<check->num_outputs; i++) + if (NULL == + json_object_get (measure->context, + required_context)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Measure `%s' lacks required context `%s' for AML program `%s'\n", + measure->measure_name, + required_context, + program->program_name); + return false; + } + } + if (0 == strcasecmp (measure->check_name, + "SKIP")) { - if (0 == strcasecmp (required_attribute, - check->outputs[i])) + if (0 != program->num_required_attributes) { - found = true; - break; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "AML program `%s' of measure `%s' has required attributes, but check is of type `SKIP' and thus cannot provide any!\n", + program->program_name, + measure->measure_name); + return false; } + return true; } - if (! found) + for (unsigned int j = 0; j<program->num_required_attributes; j++) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Check `%s' of measure `%s' does not provide required output `%s' for AML program `%s'\n", - check->check_name, - measure->measure_name, - required_attribute, - program->program_name); - return false; + const char *required_attribute = program->required_attributes[j]; + bool found = false; + + if (NULL != check) + { + for (unsigned int i = 0; i<check->num_outputs; i++) + { + if (0 == strcasecmp (required_attribute, + check->outputs[i])) + { + found = true; + break; + } + } + } + if (! found) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Check `%s' of measure `%s' does not provide required output `%s' for AML program `%s'\n", + measure->check_name, + measure->measure_name, + required_attribute, + program->program_name); + return false; + } } } - for (unsigned int j = 0; j<check->num_requires; j++) + else { - const char *required_input = check->requires[j]; - - if (NULL == - json_object_get (measure->context, - required_input)) + /* Check is of type "INFO" */ + if (NULL != measure->prog_name) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Program `%s' used in INFO measure `%s' will never be used.\n", + measure->prog_name, + measure->measure_name); + if (0 == strcasecmp (measure->check_name, + "SKIP")) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Measure `%s' lacks required context `%s' for check `%s'\n", - measure->measure_name, - required_input, - check->check_name); + "INFO check of measure `%s' should not be called 'SKIP'.\n", + measure->measure_name); return false; } } + if (NULL != check) + { + for (unsigned int j = 0; j<check->num_requires; j++) + { + const char *required_input = check->requires[j]; + + if (NULL == + json_object_get (measure->context, + required_input)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Measure `%s' lacks required context `%s' for check `%s'\n", + measure->measure_name, + required_input, + measure->check_name); + return false; + } + } + } return true; } @@ -721,7 +756,7 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) jmeasure) { const char *check_name; - const char *prog_name; + const char *prog_name = NULL; const json_t *context = NULL; bool voluntary = false; struct TALER_KYCLOGIC_Measure *measure @@ -729,8 +764,10 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_string ("check_name", &check_name), - GNUNET_JSON_spec_string ("prog_name", - &prog_name), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("prog_name", + &prog_name), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("context", &context), @@ -754,8 +791,9 @@ TALER_KYCLOGIC_rules_parse (const json_t *jlrs) = GNUNET_strdup (measure_name); measure->check_name = GNUNET_strdup (check_name); - measure->prog_name - = GNUNET_strdup (prog_name); + if (NULL != prog_name) + measure->prog_name + = GNUNET_strdup (prog_name); measure->voluntary = voluntary; if (NULL != context) @@ -1192,8 +1230,9 @@ TALER_KYCLOGIC_rule_to_measures ( mi = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", ms->check_name), - GNUNET_JSON_pack_string ("prog_name", - ms->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + ms->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", ms->context))); @@ -1265,8 +1304,9 @@ TALER_KYCLOGIC_zero_measures ( rule->trigger), GNUNET_JSON_pack_string ("check_name", ms->check_name), - GNUNET_JSON_pack_string ("prog_name", - ms->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + ms->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", ms->context))); @@ -1328,8 +1368,9 @@ append_voluntary_measure ( mj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", ms->check_name), - GNUNET_JSON_pack_string ("prog_name", - ms->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + ms->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", ms->context))); @@ -1481,8 +1522,9 @@ TALER_KYCLOGIC_get_jmeasures ( mi = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", ms->check_name), - GNUNET_JSON_pack_string ("prog_name", - ms->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + ms->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", ms->context))); @@ -1515,8 +1557,9 @@ TALER_KYCLOGIC_check_to_jmeasures ( NULL == check ? "SKIP" : check->check_name), - GNUNET_JSON_pack_string ("prog_name", - kcc->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + kcc->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", (json_t *) kcc->context))); @@ -1545,8 +1588,9 @@ TALER_KYCLOGIC_measure_to_jmeasures ( mi = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", m->check_name), - GNUNET_JSON_pack_string ("prog_name", - m->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + m->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", (json_t *) m->context))); @@ -2765,6 +2809,7 @@ add_measure (const struct GNUNET_CONFIGURATION_Handle *cfg, { bool voluntary; char *check_name = NULL; + struct TALER_KYCLOGIC_KycCheck *kc = NULL; char *context_str = NULL; char *program = NULL; json_t *context; @@ -2781,22 +2826,52 @@ add_measure (const struct GNUNET_CONFIGURATION_Handle *cfg, { check_name = GNUNET_strdup ("SKIP"); } + if (0 != strcasecmp (check_name, + "SKIP")) + { + kc = find_check (check_name); + if (NULL == kc) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "CHECK_NAME", + "check unknown"); + goto fail; + } + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, "PROGRAM", &program)) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "PROGRAM"); - goto fail; + if ( (NULL == kc) || + (TALER_KYCLOGIC_CT_INFO != kc->type) ) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "PROGRAM"); + goto fail; + } + } + else + { + /* AML program given, but do we want one? */ + if ( (NULL != kc) && + (TALER_KYCLOGIC_CT_INFO == kc->type) ) + { + GNUNET_log_config_invalid ( + GNUNET_ERROR_TYPE_WARNING, + section, + "PROGRAM", + "AML program specified for a check of type INFO (ignored)"); + GNUNET_free (program); + } } voluntary = (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "VOLUNTARY")); - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, @@ -3118,7 +3193,6 @@ TALER_KYCLOGIC_kyc_init ( const struct TALER_KYCLOGIC_AmlProgram *program = aml_programs[i]; const struct TALER_KYCLOGIC_Measure *m; - const struct TALER_KYCLOGIC_AmlProgram *fprogram; m = find_measure (&default_rules, program->fallback); @@ -3140,16 +3214,21 @@ TALER_KYCLOGIC_kyc_init ( m->check_name); return GNUNET_SYSERR; } - fprogram = find_program (m->prog_name); - GNUNET_assert (NULL != fprogram); - if (API_NONE != (fprogram->input_mask & (API_CONTEXT | API_ATTRIBUTES))) + if (NULL != m->prog_name) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fallback program %s of fallback measure `%s' used in AML program `%s' has required inputs, but fallback measures must not require any inputs\n", - m->prog_name, - program->program_name, - m->check_name); - return GNUNET_SYSERR; + const struct TALER_KYCLOGIC_AmlProgram *fprogram; + + fprogram = find_program (m->prog_name); + GNUNET_assert (NULL != fprogram); + if (API_NONE != (fprogram->input_mask & (API_CONTEXT | API_ATTRIBUTES))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fallback program %s of fallback measure `%s' used in AML program `%s' has required inputs, but fallback measures must not require any inputs\n", + m->prog_name, + program->program_name, + m->check_name); + return GNUNET_SYSERR; + } } } @@ -3158,7 +3237,6 @@ TALER_KYCLOGIC_kyc_init ( struct TALER_KYCLOGIC_KycCheck *kyc_check = kyc_checks[i]; const struct TALER_KYCLOGIC_Measure *measure; - const struct TALER_KYCLOGIC_AmlProgram *fprogram; measure = find_measure (&default_rules, kyc_check->fallback); @@ -3180,16 +3258,21 @@ TALER_KYCLOGIC_kyc_init ( measure->check_name); return GNUNET_SYSERR; } - fprogram = find_program (measure->prog_name); - GNUNET_assert (NULL != fprogram); - if (API_NONE != (fprogram->input_mask & (API_CONTEXT | API_ATTRIBUTES))) + if (NULL != measure->prog_name) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "AML program `%s' used fallback measure `%s' of KYC check `%s' has required inputs, but fallback measures must not require any inputs\n", - measure->prog_name, - kyc_check->fallback, - kyc_check->check_name); - return GNUNET_SYSERR; + const struct TALER_KYCLOGIC_AmlProgram *fprogram; + + fprogram = find_program (measure->prog_name); + GNUNET_assert (NULL != fprogram); + if (API_NONE != (fprogram->input_mask & (API_CONTEXT | API_ATTRIBUTES))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "AML program `%s' used fallback measure `%s' of KYC check `%s' has required inputs, but fallback measures must not require any inputs\n", + measure->prog_name, + kyc_check->fallback, + kyc_check->check_name); + return GNUNET_SYSERR; + } } } @@ -3706,7 +3789,6 @@ TALER_KYCLOGIC_kyc_test_required ( json_t * TALER_KYCLOGIC_measure_to_requirement ( const char *check_name, - const char *prog_name, const json_t *context, const struct TALER_AccountAccessTokenP *access_token, size_t offset, @@ -3816,8 +3898,9 @@ TALER_KYCLOGIC_get_measure_configuration ( jm = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", m->check_name), - GNUNET_JSON_pack_string ("prog_name", - m->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + m->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", m->context))); @@ -3993,8 +4076,10 @@ TALER_KYCLOGIC_select_measure ( struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_string ("check_name", check_name), - GNUNET_JSON_spec_string ("prog_name", - prog_name), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("prog_name", + prog_name), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("context", context), @@ -4070,6 +4155,12 @@ TALER_KYCLOGIC_check_form ( GNUNET_break_op (0); return TALER_EC_EXCHANGE_KYC_NOT_A_FORM; } + if (NULL == prog_name) + { + /* non-INFO checks must have an AML program */ + GNUNET_break (0); + return TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_BUG; + } for (unsigned int i = 0; i<kc->num_outputs; i++) { const char *rattr = kc->outputs[i]; @@ -4547,6 +4638,21 @@ TALER_KYCLOGIC_run_aml_program ( return NULL; } } + if (NULL == prog_name) + { + /* Trying to run AML program on a measure that does not + have one, and that should thus be an INFO check which + should never lead here. Very strange. */ + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Measure %u with check `%s' does not have an AML program!\n", + measure_index, + check_name); + json_dumpf (jmeasures, + stderr, + JSON_INDENT (2)); + return NULL; + } return TALER_KYCLOGIC_run_aml_program2 (prog_name, context, is_wallet, diff --git a/src/lib/exchange_api_add_aml_decision.c b/src/lib/exchange_api_add_aml_decision.c @@ -214,8 +214,9 @@ TALER_EXCHANGE_post_aml_decision ( measure = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("check_name", mi->check_name), - GNUNET_JSON_pack_string ("prog_name", - mi->prog_name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("prog_name", + mi->prog_name)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ("context", (json_t *) mi->context)) diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c @@ -40,12 +40,12 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define EXCHANGE_PROTOCOL_CURRENT 29 +#define EXCHANGE_PROTOCOL_CURRENT 30 /** * How many versions are we backwards compatible with? */ -#define EXCHANGE_PROTOCOL_AGE 3 +#define EXCHANGE_PROTOCOL_AGE 4 /** * Set to 1 for extra debug logging. diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c @@ -141,8 +141,10 @@ parse_limits (struct TALER_EXCHANGE_LookupAmlDecisions *lh, struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_string ("check_name", &mi->check_name), - GNUNET_JSON_spec_string ("prog_name", - &mi->prog_name), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("prog_name", + &mi->prog_name), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("context", &mi->context), diff --git a/src/testing/testing_api_cmd_take_aml_decision.c b/src/testing/testing_api_cmd_take_aml_decision.c @@ -321,8 +321,10 @@ take_aml_decision_run (void *cls, GNUNET_JSON_spec_string ("check_name", &mi->check_name), NULL), - GNUNET_JSON_spec_string ("prog_name", - &mi->prog_name), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("prog_name", + &mi->prog_name), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("context", &mi->context),