diff --git a/contrib/gana b/contrib/gana index 14cb23ef0..3a616a04f 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 14cb23ef0e31d3470a0671fe165a1557eddc0590 +Subproject commit 3a616a04f1cd946bf0641b54cd71f1b858174f74 diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 7d83e70c5..62bd9a9dc 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -488,13 +488,55 @@ handle_get_aml (struct TEH_RequestContext *rc, TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, "AML GET operations must specify an operation identifier"); } - if (1) // FIXME: check AML officer GET signature! { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, - NULL); + const char *sig_hdr; + struct TALER_AmlOfficerSignatureP officer_sig; + + sig_hdr = MHD_lookup_connection_value (rc->connection, + MHD_HEADER_KIND, + TALER_AML_OFFICER_SIGNATURE_HEADER); + if ( (NULL == sig_hdr) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (sig_hdr, + strlen (sig_hdr), + &officer_sig, + sizeof (officer_sig))) || + (GNUNET_OK != + TALER_officer_aml_query_verify (&officer_pub, + &officer_sig)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, + sig_hdr); + } + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + } + + { + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->test_aml_officer (TEH_plugin->cls, + &officer_pub); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } } for (unsigned int i = 0; NULL != h[i].op; i++) if (0 == strcmp (h[i].op, diff --git a/src/exchange/taler-exchange-httpd_aml-decision-get.c b/src/exchange/taler-exchange-httpd_aml-decision-get.c index 09f9c8e86..e0252c614 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision-get.c +++ b/src/exchange/taler-exchange-httpd_aml-decision-get.c @@ -101,7 +101,7 @@ static void aml_history_cb ( void *cls, const struct TALER_Amount *new_threshold, - enum TALER_AmlDecisionState new_status, + enum TALER_AmlDecisionState new_state, struct GNUNET_TIME_Timestamp decision_time, const char *justification, const struct TALER_AmlOfficerPublicKeyP *decider_pub, @@ -120,8 +120,8 @@ aml_history_cb ( justification), TALER_JSON_pack_amount ("new_threshold", new_threshold), - GNUNET_JSON_pack_int64 ("new_status", - new_status), + GNUNET_JSON_pack_int64 ("new_state", + new_state), GNUNET_JSON_pack_timestamp ("decision_time", decision_time) ))); @@ -134,7 +134,6 @@ TEH_handler_aml_decision_get ( const struct TALER_AmlOfficerPublicKeyP *officer_pub, const char *const args[]) { - struct TALER_AmlOfficerSignatureP officer_sig; struct TALER_PaytoHashP h_payto; if ( (NULL == args[0]) || @@ -159,30 +158,6 @@ TEH_handler_aml_decision_get ( TALER_EC_GENERIC_ENDPOINT_UNKNOWN, args[1]); } - { - const char *sig_hdr; - - sig_hdr = MHD_lookup_connection_value (rc->connection, - MHD_HEADER_KIND, - TALER_AML_OFFICER_SIGNATURE_HEADER); - if ( (NULL == sig_hdr) || - (GNUNET_OK != - GNUNET_STRINGS_string_to_data (sig_hdr, - strlen (sig_hdr), - &officer_sig, - sizeof (officer_sig))) || - (GNUNET_OK != - TALER_officer_aml_query_verify (officer_pub, - &officer_sig)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, - sig_hdr); - } - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; - } { json_t *aml_history; diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c index ae2667c1d..16dc0d965 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision.c +++ b/src/exchange/taler-exchange-httpd_aml-decision.c @@ -76,7 +76,10 @@ TEH_handler_post_aml_decision ( if (GNUNET_SYSERR == res) return MHD_NO; /* hard failure */ if (GNUNET_NO == res) + { + GNUNET_break_op (0); return MHD_YES; /* failure */ + } } new_state = (enum TALER_AmlDecisionState) new_state32; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; diff --git a/src/exchange/taler-exchange-httpd_aml-decisions-get.c b/src/exchange/taler-exchange-httpd_aml-decisions-get.c index 7c4a137d9..ad160b821 100644 --- a/src/exchange/taler-exchange-httpd_aml-decisions-get.c +++ b/src/exchange/taler-exchange-httpd_aml-decisions-get.c @@ -79,7 +79,6 @@ TEH_handler_aml_decisions_get ( const struct TALER_AmlOfficerPublicKeyP *officer_pub, const char *const args[]) { - struct TALER_AmlOfficerSignatureP officer_sig; enum TALER_AmlDecisionState decision; int delta = -20; unsigned long long start = INT64_MAX; @@ -118,30 +117,6 @@ TEH_handler_aml_decisions_get ( TALER_EC_GENERIC_ENDPOINT_UNKNOWN, args[1]); } - { - const char *sig_hdr; - - sig_hdr = MHD_lookup_connection_value (rc->connection, - MHD_HEADER_KIND, - TALER_AML_OFFICER_SIGNATURE_HEADER); - if ( (NULL == sig_hdr) || - (GNUNET_OK != - GNUNET_STRINGS_string_to_data (sig_hdr, - strlen (sig_hdr), - &officer_sig, - sizeof (officer_sig))) || - (GNUNET_OK != - TALER_officer_aml_query_verify (officer_pub, - &officer_sig)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, - sig_hdr); - } - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; - } { const char *p; diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 8ea9a4123..e8ef104e1 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -136,7 +136,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_select_similar_kyc_attributes.h pg_select_similar_kyc_attributes.c \ pg_select_kyc_attributes.h pg_select_kyc_attributes.c \ pg_insert_aml_officer.h pg_insert_aml_officer.c \ - pg_update_aml_officer.h pg_update_aml_officer.c \ + pg_test_aml_officer.h pg_test_aml_officer.c \ pg_lookup_aml_officer.h pg_lookup_aml_officer.c \ pg_trigger_aml_process.h pg_trigger_aml_process.c \ pg_select_aml_process.h pg_select_aml_process.c \ diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql index b3f77c8c4..ef3e60048 100644 --- a/src/exchangedb/exchange_do_insert_aml_decision.sql +++ b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -56,8 +56,8 @@ THEN RETURN; END IF; UPDATE exchange.aml_status - SET threshold_val=in_threshold_val - ,threshold_frac=in_threshold_frac + SET threshold_val=in_new_threshold_val + ,threshold_frac=in_new_threshold_frac ,status=in_new_status WHERE h_payto=in_h_payto; ASSERT FOUND, 'cannot have AML decision history but no AML status'; @@ -70,8 +70,8 @@ ELSE ,status) VALUES (in_h_payto - ,in_threshold_val - ,in_threshold_frac + ,in_new_threshold_val + ,in_new_threshold_frac ,in_new_status); END IF; diff --git a/src/exchangedb/pg_select_aml_history.c b/src/exchangedb/pg_select_aml_history.c index ac7fe5842..c54a3ef0c 100644 --- a/src/exchangedb/pg_select_aml_history.c +++ b/src/exchangedb/pg_select_aml_history.c @@ -138,18 +138,13 @@ TEH_PG_select_aml_history ( PREPARE (pg, "lookup_aml_history", "SELECT" - " aggregation_serial_id" - ",deposits.h_contract_terms" - ",payto_uri" - ",wire_targets.wire_target_h_payto" - ",kc.coin_pub" - ",deposits.merchant_pub" - ",wire_out.execution_date" - ",deposits.amount_with_fee_val" - ",deposits.amount_with_fee_frac" - ",denom.fee_deposit_val" - ",denom.fee_deposit_frac" - ",denom.denom_pub" + " new_threshold_val" + ",new_threshold_frac" + ",new_status" + ",decision_time" + ",justification" + ",decider_pub" + ",decider_sig" " FROM aml_history" " WHERE h_payto=$1;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, diff --git a/src/exchangedb/pg_update_aml_officer.c b/src/exchangedb/pg_test_aml_officer.c similarity index 55% rename from src/exchangedb/pg_update_aml_officer.c rename to src/exchangedb/pg_test_aml_officer.c index 2f5b0ff90..b00828244 100644 --- a/src/exchangedb/pg_update_aml_officer.c +++ b/src/exchangedb/pg_test_aml_officer.c @@ -14,49 +14,35 @@ TALER; see the file COPYING. If not, see */ /** - * @file exchangedb/pg_update_aml_officer.c - * @brief Implementation of the update_aml_officer function for Postgres + * @file exchangedb/pg_test_aml_officer.c + * @brief Implementation of the test_aml_officer 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_update_aml_officer.h" +#include "pg_test_aml_officer.h" #include "pg_helper.h" enum GNUNET_DB_QueryStatus -TEH_PG_update_aml_officer ( +TEH_PG_test_aml_officer ( void *cls, - const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_MasterSignatureP *master_sig, - const char *decider_name, - bool is_active, - bool read_only, - struct GNUNET_TIME_Absolute last_change) + const struct TALER_AmlOfficerPublicKeyP *decider_pub) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (decider_pub), - GNUNET_PQ_query_param_auto_from_type (master_sig), - GNUNET_PQ_query_param_string (decider_name), - GNUNET_PQ_query_param_bool (is_active), - GNUNET_PQ_query_param_bool (read_only), - GNUNET_PQ_query_param_absolute_time (&last_change), GNUNET_PQ_query_param_end }; PREPARE (pg, - "update_aml_staff", - "UPDATE aml_staff SET " - " master_sig=$2" - ",decider_name=$3" - ",is_active=$4" - ",read_only=$5" - ",last_change=$6" - " WHERE decider_pub=$1 AND last_change < $6;"); + "test_aml_staff", + "SELECT 1 FROM aml_staff" + " WHERE decider_pub=$1" + " AND is_active;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_aml_staff", + "test_aml_staff", params); } diff --git a/src/exchangedb/pg_update_aml_officer.h b/src/exchangedb/pg_test_aml_officer.h similarity index 65% rename from src/exchangedb/pg_update_aml_officer.h rename to src/exchangedb/pg_test_aml_officer.h index 8e3592395..9cecd6f03 100644 --- a/src/exchangedb/pg_update_aml_officer.h +++ b/src/exchangedb/pg_test_aml_officer.h @@ -27,25 +27,17 @@ /** - * Update AML staff record. + * Test if the given AML staff member is active + * (at least read-only). * * @param cls closure * @param decider_pub public key of the staff member - * @param master_sig offline signature affirming the AML officer - * @param decider_name full name of the staff member - * @param is_active true to enable, false to set as inactive - * @param read_only true to set read-only access - * @param last_change when was the change made effective - * @return database transaction status + * @return database transaction status, if member is unknown or not active, 1 if member is active */ enum GNUNET_DB_QueryStatus -TEH_PG_update_aml_officer ( +TEH_PG_test_aml_officer ( void *cls, - const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_MasterSignatureP *master_sig, - const char *decider_name, - bool is_active, - bool read_only, - struct GNUNET_TIME_Absolute last_change); + const struct TALER_AmlOfficerPublicKeyP *decider_pub); + #endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index cd5773a05..a6e932211 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -210,7 +210,7 @@ #include "pg_select_similar_kyc_attributes.h" #include "pg_select_kyc_attributes.h" #include "pg_insert_aml_officer.h" -#include "pg_update_aml_officer.h" +#include "pg_test_aml_officer.h" #include "pg_lookup_aml_officer.h" #include "pg_trigger_aml_process.h" #include "pg_select_aml_process.h" @@ -761,8 +761,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_select_kyc_attributes; plugin->insert_aml_officer = &TEH_PG_insert_aml_officer; - plugin->update_aml_officer - = &TEH_PG_update_aml_officer; + plugin->test_aml_officer + = &TEH_PG_test_aml_officer; plugin->lookup_aml_officer = &TEH_PG_lookup_aml_officer; plugin->trigger_aml_process diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 4eb0a8410..3e40c985e 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -6616,26 +6616,17 @@ struct TALER_EXCHANGEDB_Plugin /** - * Update AML staff record. + * Test if the given AML staff member is active + * (at least read-only). * * @param cls closure * @param decider_pub public key of the staff member - * @param master_sig offline signature affirming the AML officer - * @param decider_name full name of the staff member - * @param is_active true to enable, false to set as inactive - * @param read_only true to set read-only access - * @param last_change when was the change made effective - * @return database transaction status + * @return database transaction status, if member is unknown or not active, 1 if member is active */ enum GNUNET_DB_QueryStatus - (*update_aml_officer)( + (*test_aml_officer)( void *cls, - const struct TALER_AmlOfficerPublicKeyP *decider_pub, - const struct TALER_MasterSignatureP *master_sig, - const char *decider_name, - bool is_active, - bool read_only, - struct GNUNET_TIME_Absolute last_change); + const struct TALER_AmlOfficerPublicKeyP *decider_pub); /** diff --git a/src/lib/exchange_api_add_aml_decision.c b/src/lib/exchange_api_add_aml_decision.c index 0a1b70cdf..5e5383f25 100644 --- a/src/lib/exchange_api_add_aml_decision.c +++ b/src/lib/exchange_api_add_aml_decision.c @@ -188,7 +188,7 @@ TALER_EXCHANGE_add_aml_decision ( &officer_sig), GNUNET_JSON_pack_data_auto ("h_payto", h_payto), - GNUNET_JSON_pack_uint64 ("state", + GNUNET_JSON_pack_uint64 ("new_state", (uint32_t) new_state), TALER_JSON_pack_amount ("new_threshold", new_threshold), diff --git a/src/lib/exchange_api_lookup_aml_decision.c b/src/lib/exchange_api_lookup_aml_decision.c index 409a35c08..897a21f10 100644 --- a/src/lib/exchange_api_lookup_aml_decision.c +++ b/src/lib/exchange_api_lookup_aml_decision.c @@ -262,6 +262,12 @@ handle_lookup_finished (void *cls, /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ break; + case MHD_HTTP_FORBIDDEN: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Nothing really to verify, exchange says this coin was not melted; we + should pass the JSON reply to the application */ + break; case MHD_HTTP_NOT_FOUND: lr.hr.ec = TALER_JSON_get_error_code (j); lr.hr.hint = TALER_JSON_get_error_hint (j); @@ -280,7 +286,7 @@ handle_lookup_finished (void *cls, lr.hr.ec = TALER_JSON_get_error_code (j); lr.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange link\n", + "Unexpected response code %u/%d for exchange lookup AML decision\n", (unsigned int) response_code, (int) lr.hr.ec); break; @@ -332,7 +338,7 @@ TALER_EXCHANGE_lookup_aml_decision ( *end = '\0'; GNUNET_snprintf (arg_str, sizeof (arg_str), - "/aml/%s/decision/%s", + "aml/%s/decision/%s", pub_str, pt_str); } diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c index da17aa784..3b1db4556 100644 --- a/src/lib/exchange_api_lookup_aml_decisions.c +++ b/src/lib/exchange_api_lookup_aml_decisions.c @@ -202,6 +202,12 @@ handle_lookup_finished (void *cls, /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ break; + case MHD_HTTP_FORBIDDEN: + lr.hr.ec = TALER_JSON_get_error_code (j); + lr.hr.hint = TALER_JSON_get_error_hint (j); + /* Nothing really to verify, exchange says this coin was not melted; we + should pass the JSON reply to the application */ + break; case MHD_HTTP_NOT_FOUND: lr.hr.ec = TALER_JSON_get_error_code (j); lr.hr.hint = TALER_JSON_get_error_hint (j); @@ -220,7 +226,7 @@ handle_lookup_finished (void *cls, lr.hr.ec = TALER_JSON_get_error_code (j); lr.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange link\n", + "Unexpected response code %u/%d for lookup AML decisions\n", (unsigned int) response_code, (int) lr.hr.ec); break; @@ -279,7 +285,7 @@ TALER_EXCHANGE_lookup_aml_decisions ( *end = '\0'; GNUNET_snprintf (arg_str, sizeof (arg_str), - "/aml/%s/decisions/%s", + "aml/%s/decisions/%s", pub_str, state_str); } diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c index 44f302c6d..517138530 100644 --- a/src/testing/test_kyc_api.c +++ b/src/testing/test_kyc_api.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-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 @@ -410,7 +410,72 @@ run (void *cls, MHD_HTTP_OK), TALER_TESTING_cmd_end () }; - + struct TALER_TESTING_Command aml[] = { + /* Trigger something upon which an AML officer could act */ + TALER_TESTING_cmd_wallet_kyc_get ("wallet-trigger-kyc-for-aml", + NULL, + "EUR:1000", + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1", + NULL, + "Peter Falk", + true, + false), + TALER_TESTING_cmd_sleep ("sleep-1a", + 1), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1-disable", + "create-aml-officer-1", + "Peter Falk", + true, + true), + /* Test that we are not allowed to take AML decisions as our + AML staff account is on read-only */ + TALER_TESTING_cmd_take_aml_decision ("aml-decide-while-disabled", + "create-aml-officer-1", + "wallet-trigger-kyc-for-aml", + "EUR:10000", + "party time", + TALER_AML_NORMAL, + MHD_HTTP_FORBIDDEN), + /* Check that no decision was taken, but that we are allowed + to read this information */ + TALER_TESTING_cmd_check_aml_decision ("check-aml-decision-empty", + "create-aml-officer-1", + "aml-decide-while-disabled", + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_sleep ("sleep-1b", + 1), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1-enable", + "create-aml-officer-1", + "Peter Falk", + true, + false), + TALER_TESTING_cmd_take_aml_decision ("aml-decide", + "create-aml-officer-1", + "wallet-trigger-kyc-for-aml", + "EUR:10000", + "party time", + TALER_AML_NORMAL, + MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_check_aml_decision ("check-aml-decision", + "create-aml-officer-1", + "aml-decide", + MHD_HTTP_OK), + TALER_TESTING_cmd_sleep ("sleep-1c", + 1), + TALER_TESTING_cmd_set_officer ("create-aml-officer-1-disable", + "create-aml-officer-1", + "Peter Falk", + false, + true), + /* Test that we are NOT allowed to read AML decisions now that + our AML staff account is disabled */ + TALER_TESTING_cmd_check_aml_decision ("check-aml-decision-disabled", + "create-aml-officer-1", + "aml-decide", + MHD_HTTP_FORBIDDEN), + TALER_TESTING_cmd_end () + }; struct TALER_TESTING_Command commands[] = { TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees", @@ -452,6 +517,8 @@ run (void *cls, push), TALER_TESTING_cmd_batch ("pull", pull), + TALER_TESTING_cmd_batch ("aml", + aml), TALER_TESTING_cmd_end () }; diff --git a/src/testing/testing_api_cmd_check_aml_decision.c b/src/testing/testing_api_cmd_check_aml_decision.c index 89939a41e..d77e9b6b9 100644 --- a/src/testing/testing_api_cmd_check_aml_decision.c +++ b/src/testing/testing_api_cmd_check_aml_decision.c @@ -107,7 +107,6 @@ check_aml_decision_cb (void *cls, TALER_TESTING_interpreter_fail (ds->is); return; } - // FIXME: check returned details... GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_aml_justification (ref, &justification)); @@ -117,7 +116,7 @@ check_aml_decision_cb (void *cls, GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_amount (ref, &amount)); - for (unsigned int i = 1; idetails.success.aml_history_length; i++) + for (unsigned int i = 0; idetails.success.aml_history_length; i++) { const struct TALER_EXCHANGE_AmlDecisionDetail *aml_history = &adr->details.success.aml_history[i];