iterate_aml_history_above_serial_id.c (7673B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/exchangedb/iterate_aml_history_above_serial_id.c 18 * @brief Implementation of the iterate_aml_history_above_serial_id function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "taler/taler_pq_lib.h" 22 #include "helper.h" 23 #include "exchange-database/iterate_aml_history_above_serial_id.h" 24 25 26 /** 27 * Closure for #aml_decision_cb(). 28 */ 29 struct AmlDecisionContext 30 { 31 /** 32 * Function to call for each decision. 33 */ 34 TALER_EXCHANGEDB_AmlDecisionCallback cb; 35 36 /** 37 * Closure for @e cb. 38 */ 39 void *cb_cls; 40 41 /** 42 * Query status to return. 43 */ 44 enum GNUNET_DB_QueryStatus qs; 45 }; 46 47 48 /** 49 * Helper function for 50 * #TALER_EXCHANGEDB_iterate_aml_history_above_serial_id(). 51 * To be called with the results of a SELECT statement 52 * that has returned @a num_results results. 53 * 54 * @param cls closure of type `struct AmlDecisionContext *` 55 * @param result the postgres result 56 * @param num_results the number of results in @a result 57 */ 58 static void 59 aml_decision_cb (void *cls, 60 PGresult *result, 61 unsigned int num_results) 62 { 63 struct AmlDecisionContext *adc = cls; 64 65 for (unsigned int i = 0; i < num_results; i++) 66 { 67 uint64_t rowid; 68 struct TALER_NormalizedPaytoHashP h_payto; 69 char *justification = NULL; 70 struct TALER_AmlOfficerPublicKeyP decider_pub; 71 struct TALER_AmlOfficerSignatureP decider_sig; 72 struct GNUNET_TIME_Timestamp decision_time; 73 json_t *jproperties = NULL; 74 json_t *jnew_rules = NULL; 75 char *new_measure_name = NULL; 76 bool to_investigate; 77 struct GNUNET_TIME_Timestamp attributes_expiration 78 = GNUNET_TIME_UNIT_ZERO_TS; 79 struct GNUNET_HashCode h_attributes; 80 bool no_decider_pub; 81 bool no_decider_sig; 82 bool no_new_rules; 83 bool no_measure; 84 bool no_attributes_expiration; 85 bool no_h_attributes; 86 struct GNUNET_PQ_ResultSpec rs[] = { 87 GNUNET_PQ_result_spec_uint64 ("aml_history_serial_id", 88 &rowid), 89 GNUNET_PQ_result_spec_auto_from_type ("h_payto", 90 &h_payto), 91 GNUNET_PQ_result_spec_string ("justification", 92 &justification), 93 /* decider_pub, decider_sig, kyc_attributes_hash and jnew_rules all 94 lack a NOT NULL constraint. An honest decision has all of them; 95 reporting the ones that do not is the auditor's job, so they must 96 not abort the query here. */ 97 GNUNET_PQ_result_spec_allow_null ( 98 GNUNET_PQ_result_spec_auto_from_type ("decider_pub", 99 &decider_pub), 100 &no_decider_pub), 101 GNUNET_PQ_result_spec_allow_null ( 102 GNUNET_PQ_result_spec_auto_from_type ("decider_sig", 103 &decider_sig), 104 &no_decider_sig), 105 GNUNET_PQ_result_spec_timestamp ("decision_time", 106 &decision_time), 107 GNUNET_PQ_result_spec_allow_null ( 108 TALER_PQ_result_spec_json ("jproperties", 109 &jproperties), 110 NULL), 111 GNUNET_PQ_result_spec_allow_null ( 112 TALER_PQ_result_spec_json ("jnew_rules", 113 &jnew_rules), 114 &no_new_rules), 115 GNUNET_PQ_result_spec_allow_null ( 116 GNUNET_PQ_result_spec_string ("new_measure_name", 117 &new_measure_name), 118 &no_measure), 119 GNUNET_PQ_result_spec_bool ("to_investigate", 120 &to_investigate), 121 GNUNET_PQ_result_spec_allow_null ( 122 GNUNET_PQ_result_spec_timestamp ("attributes_expiration", 123 &attributes_expiration), 124 &no_attributes_expiration), 125 GNUNET_PQ_result_spec_allow_null ( 126 GNUNET_PQ_result_spec_auto_from_type ("kyc_attributes_hash", 127 &h_attributes), 128 &no_h_attributes), 129 GNUNET_PQ_result_spec_end 130 }; 131 enum GNUNET_GenericReturnValue rval; 132 133 if (GNUNET_OK != 134 GNUNET_PQ_extract_result (result, 135 rs, 136 i)) 137 { 138 GNUNET_break (0); 139 adc->qs = GNUNET_DB_STATUS_HARD_ERROR; 140 return; 141 } 142 if (no_attributes_expiration) 143 attributes_expiration = GNUNET_TIME_UNIT_ZERO_TS; 144 adc->qs = i + 1; 145 rval = adc->cb (adc->cb_cls, 146 rowid, 147 &h_payto, 148 justification, 149 no_decider_pub ? NULL : &decider_pub, 150 no_decider_sig ? NULL : &decider_sig, 151 decision_time, 152 jproperties, 153 no_new_rules ? NULL : jnew_rules, 154 no_measure ? NULL : new_measure_name, 155 to_investigate, 156 attributes_expiration, 157 no_h_attributes ? NULL : &h_attributes); 158 GNUNET_PQ_cleanup_result (rs); 159 if (GNUNET_OK != rval) 160 break; 161 } 162 } 163 164 165 enum GNUNET_DB_QueryStatus 166 TALER_EXCHANGEDB_iterate_aml_history_above_serial_id ( 167 struct TALER_EXCHANGEDB_PostgresContext *pg, 168 uint64_t serial_id, 169 TALER_EXCHANGEDB_AmlDecisionCallback cb, 170 TALER_EXCHANGEDB_AML_DECISION_RESULT_CLOSURE *cb_cls) 171 { 172 struct GNUNET_PQ_QueryParam params[] = { 173 GNUNET_PQ_query_param_uint64 (&serial_id), 174 GNUNET_PQ_query_param_end 175 }; 176 struct AmlDecisionContext adc = { 177 .cb = cb, 178 .cb_cls = cb_cls 179 }; 180 enum GNUNET_DB_QueryStatus qs; 181 182 /* legitimization_outcomes is joined on a NOT NULL column with a foreign 183 key, so an inner join here cannot silently drop a decision. The 184 attributes are optional, hence the LEFT JOIN for their expiration. */ 185 PREPARE (pg, 186 "iterate_aml_history_above_serial_id", 187 "SELECT" 188 " ah.aml_history_serial_id" 189 ",ah.h_payto" 190 ",ah.justification" 191 ",ah.decider_pub" 192 ",ah.decider_sig" 193 ",ah.kyc_attributes_hash" 194 ",lo.decision_time" 195 /* JSONB comes back with a binary version prefix, so it has to 196 be cast; the cast is what TALER_json_hash() then re-reads. */ 197 ",lo.jproperties::TEXT" 198 ",lo.jnew_rules::TEXT" 199 ",lo.new_measure_name" 200 ",lo.to_investigate" 201 ",ka.expiration_time AS attributes_expiration" 202 " FROM aml_history ah" 203 " JOIN legitimization_outcomes lo" 204 " USING (outcome_serial_id)" 205 " LEFT JOIN kyc_attributes ka" 206 " ON (ah.kyc_attributes_serial_id = ka.kyc_attributes_serial_id)" 207 " WHERE ah.aml_history_serial_id>=$1" 208 " ORDER BY ah.aml_history_serial_id ASC;"); 209 qs = GNUNET_PQ_eval_prepared_multi_select ( 210 pg->conn, 211 "iterate_aml_history_above_serial_id", 212 params, 213 &aml_decision_cb, 214 &adc); 215 if (qs > 0) 216 return adc.qs; 217 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); 218 return qs; 219 }