exchange

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

commit 3a14fc370ff777d0a33ed1480573e19e4aba914d
parent 33f7da0f517d797cf7bac505260ca284d4c9b631
Author: Florian Dold <florian@dold.me>
Date:   Tue, 21 Jan 2025 15:36:45 +0100

implement new_measures handling for AMP results

Diffstat:
Msrc/exchange/taler-exchange-httpd_common_kyc.c | 66+++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/exchangedb/Makefile.am | 1-
Msrc/exchangedb/exchange_do_insert_aml_decision.sql | 66++++++++++++++++++++++++++++++++++++++----------------------------
Dsrc/exchangedb/exchange_do_insert_kyc_measure_result.sql | 101-------------------------------------------------------------------------------
Msrc/exchangedb/exchangedb_aml.c | 64+++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/exchangedb/pg_insert_aml_decision.c | 19++++++++++++++++---
Dsrc/exchangedb/pg_insert_kyc_measure_result.c | 98-------------------------------------------------------------------------------
Dsrc/exchangedb/pg_insert_kyc_measure_result.h | 57---------------------------------------------------------
Msrc/exchangedb/plugin_exchangedb_postgres.c | 3---
Msrc/exchangedb/procedures.sql.in | 1-
Msrc/include/taler_exchangedb_plugin.h | 29-----------------------------
Msrc/include/taler_kyclogic_lib.h | 5+++++
Msrc/kyclogic/kyclogic_api.c | 5+++++
13 files changed, 160 insertions(+), 355 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -611,6 +611,8 @@ handle_aml_fallback_result ( struct TEH_KycAmlFallback *fb = cls; enum GNUNET_DB_QueryStatus qs; struct GNUNET_AsyncScopeSave old_scope; + json_t *jmeasures = NULL; + struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL; fb->aprh = NULL; GNUNET_async_scope_enter (&fb->scope, @@ -631,46 +633,72 @@ handle_aml_fallback_result ( fb->cb (fb->cb_cls, false, 0); - TEH_kyc_fallback_cancel (fb); - GNUNET_async_scope_restore (&old_scope); - return; + goto cleanup; } /* 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; + goto cleanup; + } + + if (NULL != apr->details.success.new_measures) + { + lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules); + GNUNET_assert (NULL != lrs); + jmeasures = TALER_KYCLOGIC_get_jmeasures ( + lrs, + apr->details.success.new_measures); + GNUNET_assert (NULL != jmeasures); + } + + { + struct TALER_FullPayto null_payto_uri = { 0 }; + bool invalid_officer; + bool unknown_account; + struct GNUNET_TIME_Timestamp last_date; + uint64_t legitimization_measure_serial_id; + + qs = TEH_plugin->insert_aml_decision ( + TEH_plugin->cls, + null_payto_uri, + &fb->account_id, + GNUNET_TIME_timestamp_get (), + apr->details.success.expiration_time, + apr->details.success.account_properties, + apr->details.success.new_rules, + apr->details.success.to_investigate, + apr->details.success.new_measures, + jmeasures, + NULL, /* justification */ + NULL, /* decider_pub */ + NULL, /* decider_sig */ + apr->details.success.num_events, + apr->details.success.events, + &invalid_officer, + &unknown_account, + &last_date, + &legitimization_measure_serial_id); } - qs = TEH_plugin->insert_kyc_measure_result ( - TEH_plugin->cls, - fb->orig_requirement_row, - &fb->account_id, - apr->details.success.expiration_time, - apr->details.success.account_properties, - apr->details.success.new_rules, - apr->details.success.to_investigate, - apr->details.success.num_events, - apr->details.success.events); 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; + goto cleanup; } /* Finally, return result to main handler */ fb->cb (fb->cb_cls, true, 0); +cleanup: TEH_kyc_fallback_cancel (fb); GNUNET_async_scope_restore (&old_scope); + TALER_KYCLOGIC_rules_free (lrs); + json_decref (jmeasures); } diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am @@ -210,7 +210,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_insert_drain_profit.h pg_insert_drain_profit.c \ pg_insert_global_fee.h pg_insert_global_fee.c \ pg_insert_kyc_failure.h pg_insert_kyc_failure.c \ - pg_insert_kyc_measure_result.h pg_insert_kyc_measure_result.c \ pg_insert_kyc_requirement_process.h pg_insert_kyc_requirement_process.c \ pg_insert_partner.h pg_insert_partner.c \ pg_insert_purse_request.h pg_insert_purse_request.c \ diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -26,9 +26,9 @@ CREATE FUNCTION exchange_do_insert_aml_decision( IN in_to_investigate BOOLEAN, IN in_new_measure_name TEXT, -- can be NULL IN in_jmeasures TEXT, -- can be NULL - IN in_justification TEXT, - IN in_decider_pub BYTEA, - IN in_decider_sig BYTEA, + IN in_justification TEXT, -- can be NULL + IN in_decider_pub BYTEA, -- can be NULL + IN in_decider_sig BYTEA, -- can be NULL IN in_notify_s TEXT, IN ina_events TEXT[], OUT out_invalid_officer BOOLEAN, @@ -47,18 +47,26 @@ BEGIN out_account_unknown=FALSE; out_legitimization_measure_serial_id=0; --- Check officer is eligible to make decisions. -PERFORM - FROM aml_staff - WHERE decider_pub=in_decider_pub - AND is_active - AND NOT read_only; -IF NOT FOUND +IF in_decider_pub IS NOT NULL THEN - out_invalid_officer=TRUE; - out_last_date=0; - RETURN; + IF in_justification IS NULL OR in_decider_sig IS NULL + THEN + RAISE EXCEPTION 'Got in_decider_sig without justification or signature.'; + END IF; + -- Check officer is eligible to make decisions. + PERFORM + FROM aml_staff + WHERE decider_pub=in_decider_pub + AND is_active + AND NOT read_only; + IF NOT FOUND + THEN + out_invalid_officer=TRUE; + out_last_date=0; + RETURN; + END IF; END IF; + out_invalid_officer=FALSE; -- Check no more recent decision exists. @@ -71,9 +79,9 @@ SELECT decision_time IF FOUND THEN - IF out_last_date > in_decision_time + IF in_decider_pub IS NOT NULL AND out_last_date > in_decision_time THEN - -- Refuse to insert older decision. + -- Refuse to insert older decision for officer decisions. RETURN; END IF; UPDATE legitimization_outcomes @@ -180,20 +188,22 @@ INSERT INTO legitimization_outcomes INTO my_outcome_serial_id; - -INSERT INTO aml_history - (h_payto - ,outcome_serial_id - ,justification - ,decider_pub - ,decider_sig - ) VALUES - (in_h_normalized_payto - ,my_outcome_serial_id - ,in_justification - ,in_decider_pub - ,in_decider_sig +IF in_decider_pub IS NOT NULL +THEN + INSERT INTO aml_history + (h_payto + ,outcome_serial_id + ,justification + ,decider_pub + ,decider_sig + ) VALUES + (in_h_normalized_payto + ,my_outcome_serial_id + ,in_justification + ,in_decider_pub + ,in_decider_sig ); +END IF; -- Trigger events FOR i IN 1..COALESCE(array_length(ina_events,1),0) diff --git a/src/exchangedb/exchange_do_insert_kyc_measure_result.sql b/src/exchangedb/exchange_do_insert_kyc_measure_result.sql @@ -1,101 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2023, 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/> --- - -DROP FUNCTION IF EXISTS exchange_do_insert_kyc_measure_result; -CREATE FUNCTION exchange_do_insert_kyc_measure_result( - IN in_process_row INT8, - IN in_h_payto BYTEA, - IN in_decision_time INT8, - IN in_expiration_time_ts INT8, - IN in_account_properties TEXT, - IN in_new_rules TEXT, - IN ina_events TEXT[], - IN in_to_investigate BOOLEAN, - IN in_kyc_completed_notify_s TEXT, - OUT out_ok BOOLEAN) -LANGUAGE plpgsql -AS $$ -DECLARE - my_trigger_outcome_serial INT8; - my_lmsi INT8; - my_i INT4; - ini_event TEXT; -BEGIN - --- Disactivate all previous outcomes. -UPDATE legitimization_outcomes - SET is_active=FALSE - WHERE h_payto=in_h_payto; - --- Insert new rules -INSERT INTO legitimization_outcomes - (h_payto - ,decision_time - ,expiration_time - ,jproperties - ,to_investigate - ,jnew_rules) -VALUES - (in_h_payto - ,in_decision_time - ,in_expiration_time_ts - ,in_account_properties - ,in_to_investigate - ,in_new_rules) -RETURNING - outcome_serial_id -INTO - my_trigger_outcome_serial; - --- Mark measure as complete -UPDATE legitimization_measures - SET is_finished=TRUE - WHERE legitimization_measure_serial_id= - (SELECT legitimization_measure_serial_id - FROM legitimization_processes - WHERE h_payto=in_h_payto - AND legitimization_process_serial_id=in_process_row); -out_ok = FOUND; - --- Trigger events -FOR i IN 1..COALESCE(array_length(ina_events,1),0) -LOOP - ini_event = ina_events[i]; - INSERT INTO kyc_events - (event_timestamp - ,event_type) - VALUES - (in_decision_time - ,ini_event); -END LOOP; - --- Notify about KYC update -EXECUTE FORMAT ( - 'NOTIFY %s' - ,in_kyc_completed_notify_s); - -INSERT INTO kyc_alerts - (h_payto - ,trigger_type) - VALUES - (in_h_payto,1) - ON CONFLICT DO NOTHING; - -END $$; - - -COMMENT ON FUNCTION exchange_do_insert_kyc_measure_result(INT8, BYTEA, INT8, INT8, TEXT, TEXT, TEXT[], BOOL, TEXT) - IS 'Inserts AML program outcome and updates the status of the legitimization process and the AML status for the account'; diff --git a/src/exchangedb/exchangedb_aml.c b/src/exchangedb/exchangedb_aml.c @@ -40,6 +40,18 @@ TALER_EXCHANGEDB_persist_aml_program_result ( const struct TALER_KYCLOGIC_AmlProgramResult *apr) { enum GNUNET_DB_QueryStatus qs; + json_t *jmeasures = NULL; + struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL; + + if (NULL != apr->details.success.new_measures) + { + lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules); + GNUNET_assert (NULL != lrs); + jmeasures = TALER_KYCLOGIC_get_jmeasures ( + lrs, + apr->details.success.new_measures); + GNUNET_assert (NULL != jmeasures); + } qs = plugin->clear_aml_lock ( plugin->cls, @@ -54,23 +66,45 @@ TALER_EXCHANGEDB_persist_aml_program_result ( apr->details.failure.error_message, apr->details.failure.ec); GNUNET_break (qs > 0); - return qs; + goto cleanup; case TALER_KYCLOGIC_AMLR_SUCCESS: - qs = plugin->insert_kyc_measure_result ( - plugin->cls, - process_row, - account_id, - apr->details.success.expiration_time, - apr->details.success.account_properties, - apr->details.success.new_rules, - apr->details.success.to_investigate, - apr->details.success.num_events, - apr->details.success.events); - GNUNET_break (qs > 0); - return qs; + { + struct TALER_FullPayto null_payto_uri = { 0 }; + bool invalid_officer; + bool unknown_account; + struct GNUNET_TIME_Timestamp last_date; + uint64_t legitimization_measure_serial_id; + + qs = plugin->insert_aml_decision ( + plugin->cls, + null_payto_uri, + account_id, + GNUNET_TIME_timestamp_get (), + apr->details.success.expiration_time, + apr->details.success.account_properties, + apr->details.success.new_rules, + apr->details.success.to_investigate, + apr->details.success.new_measures, + jmeasures, + NULL, /* justification */ + NULL, /* decider_pub */ + NULL, /* decider_sig */ + apr->details.success.num_events, + apr->details.success.events, + &invalid_officer, + &unknown_account, + &last_date, + &legitimization_measure_serial_id); + GNUNET_break (qs > 0); + goto cleanup; + } } - GNUNET_assert (0); - return GNUNET_DB_STATUS_HARD_ERROR; + GNUNET_break (0); + qs = GNUNET_DB_STATUS_HARD_ERROR; +cleanup: + TALER_KYCLOGIC_rules_free (lrs); + json_decref (jmeasures); + return qs; } diff --git a/src/exchangedb/pg_insert_aml_decision.c b/src/exchangedb/pg_insert_aml_decision.c @@ -79,9 +79,15 @@ TEH_PG_insert_aml_decision ( NULL != jmeasures ? TALER_PQ_query_param_json (jmeasures) : GNUNET_PQ_query_param_null (), - GNUNET_PQ_query_param_string (justification), - GNUNET_PQ_query_param_auto_from_type (decider_pub), - GNUNET_PQ_query_param_auto_from_type (decider_sig), + NULL != justification + ? GNUNET_PQ_query_param_string (justification) + : GNUNET_PQ_query_param_null (), + NULL != decider_pub + ? GNUNET_PQ_query_param_auto_from_type (decider_pub) + : GNUNET_PQ_query_param_null (), + NULL != decider_sig + ? GNUNET_PQ_query_param_auto_from_type (decider_sig) + : GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_string (notify_s), GNUNET_PQ_query_param_array_ptrs_string (num_events, events, @@ -101,6 +107,13 @@ TEH_PG_insert_aml_decision ( }; enum GNUNET_DB_QueryStatus qs; + GNUNET_assert ( ( (NULL == decider_pub) && + (NULL == decider_sig) && + (NULL == justification) ) || + ( (NULL != decider_pub) && + (NULL != decider_sig) && + (NULL != justification) ) ); + if (NULL != payto_uri.full_payto) TALER_full_payto_hash (payto_uri, &h_full_payto); diff --git a/src/exchangedb/pg_insert_kyc_measure_result.c b/src/exchangedb/pg_insert_kyc_measure_result.c @@ -1,98 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022, 2023, 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_insert_kyc_measure_result.c - * @brief Implementation of the insert_kyc_measure_result 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_insert_kyc_measure_result.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TEH_PG_insert_kyc_measure_result ( - void *cls, - uint64_t process_row, - const struct TALER_NormalizedPaytoHashP *h_payto, - struct GNUNET_TIME_Timestamp expiration_time, - const json_t *account_properties, - const json_t *new_rules, - bool to_investigate, - unsigned int num_events, - const char **events) -{ - struct PostgresClosure *pg = cls; - struct TALER_KycCompletedEventP rep = { - .header.size = htons (sizeof (rep)), - .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), - .h_payto = *h_payto - }; - struct GNUNET_TIME_Timestamp now - = GNUNET_TIME_timestamp_get (); - char *kyc_completed_notify_s - = GNUNET_PQ_get_event_notify_channel (&rep.header); - struct GNUNET_PQ_QueryParam params[] = { - (0 == process_row) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_uint64 (&process_row), - GNUNET_PQ_query_param_auto_from_type (h_payto), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_timestamp (&expiration_time), - NULL == account_properties - ? GNUNET_PQ_query_param_null () - : TALER_PQ_query_param_json (account_properties), - NULL == new_rules - ? GNUNET_PQ_query_param_null () - : TALER_PQ_query_param_json (new_rules), - GNUNET_PQ_query_param_array_ptrs_string (num_events, - events, - pg->conn), - GNUNET_PQ_query_param_bool (to_investigate), - GNUNET_PQ_query_param_string (kyc_completed_notify_s), - GNUNET_PQ_query_param_end - }; - bool ok; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_bool ("out_ok", - &ok), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Inserting KYC attributes, wake up on %s\n", - kyc_completed_notify_s); - GNUNET_break (NULL != h_payto); - PREPARE (pg, - "insert_kyc_measure_result", - "SELECT " - " out_ok" - " FROM exchange_do_insert_kyc_measure_result " - "($1, $2, $3, $4, $5, $6, $7, $8, $9);"); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "insert_kyc_measure_result", - params, - rs); - GNUNET_PQ_cleanup_query_params_closures (params); - GNUNET_free (kyc_completed_notify_s); - GNUNET_PQ_event_do_poll (pg->conn); - GNUNET_break (ok); - return qs; -} diff --git a/src/exchangedb/pg_insert_kyc_measure_result.h b/src/exchangedb/pg_insert_kyc_measure_result.h @@ -1,57 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022, 2023 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_insert_kyc_measure_result.h - * @brief implementation of the insert_kyc_measure_result function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_INSERT_KYC_ATTRIBUTES_H -#define PG_INSERT_KYC_ATTRIBUTES_H - -#include "taler_util.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" - - -/** - * Update KYC process status and AML status for the given account based on AML - * program result. - * - * @param cls closure - * @param process_row KYC process row to update - * @param h_payto account for which the attribute data is stored - * @param expiration_time when do the @a new_rules expire - * @param account_properties new account properties - * @param new_rules new KYC rules to apply to the account - * @param to_investigate true to flag account for investigation - * @param num_events length of the @a events array - * @param events array of KYC events to trigger - * @return database transaction status - */ -enum GNUNET_DB_QueryStatus -TEH_PG_insert_kyc_measure_result ( - void *cls, - uint64_t process_row, - const struct TALER_NormalizedPaytoHashP *h_payto, - struct GNUNET_TIME_Timestamp expiration_time, - const json_t *account_properties, - const json_t *new_rules, - bool to_investigate, - unsigned int num_events, - const char **events); - - -#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c @@ -114,7 +114,6 @@ #include "pg_insert_drain_profit.h" #include "pg_insert_global_fee.h" #include "pg_insert_kyc_failure.h" -#include "pg_insert_kyc_measure_result.h" #include "pg_insert_kyc_requirement_process.h" #include "pg_insert_partner.h" #include "pg_insert_purse_request.h" @@ -751,8 +750,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_set_purse_balance; plugin->get_pending_kyc_requirement_process = &TEH_PG_get_pending_kyc_requirement_process; - plugin->insert_kyc_measure_result - = &TEH_PG_insert_kyc_measure_result; plugin->select_kyc_attributes = &TEH_PG_select_kyc_attributes; plugin->insert_aml_officer diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in @@ -46,7 +46,6 @@ SET search_path TO exchange; #include "exchange_do_insert_aml_decision.sql" #include "exchange_do_insert_successor_measure.sql" #include "exchange_do_insert_aml_officer.sql" -#include "exchange_do_insert_kyc_measure_result.sql" #include "exchange_do_reserves_in_insert.sql" #include "exchange_do_batch_reserves_update.sql" #include "exchange_do_get_link_data.sql" diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -7352,35 +7352,6 @@ struct TALER_EXCHANGEDB_Plugin /** - * Update KYC process status and - * AML status for the given account based on - * AML program outcome. - * - * @param cls closure - * @param process_row KYC process row to update - * @param h_payto account for which the attribute data is stored - * @param expiration_time when do the @a new_rules expire - * @param account_properties new account properties - * @param new_rules new KYC rules to apply to the account - * @param to_investigate true to flag account for investigation - * @param num_events length of the @a events array - * @param events array of KYC events to trigger - * @return database transaction status - */ - enum GNUNET_DB_QueryStatus - (*insert_kyc_measure_result)( - void *cls, - uint64_t process_row, - const struct TALER_NormalizedPaytoHashP *h_payto, - struct GNUNET_TIME_Timestamp expiration_time, - const json_t *account_properties, - const json_t *new_rules, - bool to_investigate, - unsigned int num_events, - const char **events); - - - /** * Update sanction list hit status of the given account. * * @param cls closure diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -920,6 +920,11 @@ struct TALER_KYCLOGIC_AmlProgramResult const json_t *new_rules; /** + * New measures to trigger immediately. + */ + const char *new_measures; + + /** * When do the new rules expire. */ struct GNUNET_TIME_Timestamp expiration_time; diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -3990,6 +3990,11 @@ handle_aml_output ( GNUNET_JSON_spec_object_const ( "new_rules", &apr->details.success.new_rules), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ( + "new_measures", + &apr->details.success.new_measures), + NULL), GNUNET_JSON_spec_end () }; const char *err;