iterate_kyc_accounts.c (9660B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 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 exchangedb/iterate_kyc_accounts.c 18 * @brief Implementation of the iterate_kyc_accounts function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "taler/taler_pq_lib.h" 22 #include "exchange-database/iterate_kyc_accounts.h" 23 #include "helper.h" 24 25 26 /** 27 * Hard upper bound on the number of records returned by a single 28 * call, regardless of the limit requested by the client. 29 */ 30 #define MAX_RECORDS 50000 31 32 33 /** 34 * Closure for #handle_aml_result. 35 */ 36 struct KycAccountResultContext 37 { 38 /** 39 * Function to call on each result. 40 */ 41 TALER_EXCHANGEDB_AmlAccountListCallback cb; 42 43 /** 44 * Closure for @e cb. 45 */ 46 void *cb_cls; 47 48 /** 49 * Plugin context. 50 */ 51 struct TALER_EXCHANGEDB_PostgresContext *pg; 52 53 /** 54 * Set to #GNUNET_SYSERR on serious errors. 55 */ 56 enum GNUNET_GenericReturnValue status; 57 }; 58 59 60 /** 61 * Function to be called with the results of a SELECT statement 62 * that has returned @a num_results results. Helper function 63 * for #TALER_EXCHANGEDB_iterate_kyc_accounts(). 64 * 65 * @param cls closure of type `struct KycAccountResultContext *` 66 * @param result the postgres result 67 * @param num_results the number of results in @a result 68 */ 69 static void 70 handle_kyc_account_cb (void *cls, 71 PGresult *result, 72 unsigned int num_results) 73 { 74 struct KycAccountResultContext *ctx = cls; 75 76 for (unsigned int i = 0; i<num_results; i++) 77 { 78 uint64_t rowid; 79 struct TALER_NormalizedPaytoHashP h_payto; 80 char *comments = NULL; 81 char *customer_label = NULL; 82 struct GNUNET_TIME_Timestamp open_time 83 = GNUNET_TIME_UNIT_FOREVER_TS; 84 struct GNUNET_TIME_Timestamp close_time 85 = GNUNET_TIME_UNIT_FOREVER_TS; 86 bool to_investigate; 87 bool high_risk; 88 struct TALER_FullPayto payto; 89 struct GNUNET_PQ_ResultSpec rs[] = { 90 GNUNET_PQ_result_spec_uint64 ("kyc_target_serial_id", 91 &rowid), 92 GNUNET_PQ_result_spec_auto_from_type ("h_payto", 93 &h_payto), 94 GNUNET_PQ_result_spec_allow_null ( 95 GNUNET_PQ_result_spec_string ("comments", 96 &comments), 97 NULL), 98 GNUNET_PQ_result_spec_allow_null ( 99 GNUNET_PQ_result_spec_string ("customer_label", 100 &customer_label), 101 NULL), 102 GNUNET_PQ_result_spec_allow_null ( 103 GNUNET_PQ_result_spec_timestamp ("open_time", 104 &open_time), 105 NULL), 106 GNUNET_PQ_result_spec_allow_null ( 107 GNUNET_PQ_result_spec_timestamp ("close_time", 108 &close_time), 109 NULL), 110 GNUNET_PQ_result_spec_bool ("to_investigate", 111 &to_investigate), 112 GNUNET_PQ_result_spec_bool ("high_risk", 113 &high_risk), 114 GNUNET_PQ_result_spec_string ("payto_uri", 115 &payto.full_payto), 116 GNUNET_PQ_result_spec_end 117 }; 118 119 if (GNUNET_OK != 120 GNUNET_PQ_extract_result (result, 121 rs, 122 i)) 123 { 124 GNUNET_break (0); 125 ctx->status = GNUNET_SYSERR; 126 return; 127 } 128 ctx->cb (ctx->cb_cls, 129 rowid, 130 &h_payto, 131 open_time, 132 close_time, 133 comments, 134 customer_label, 135 high_risk, 136 to_investigate, 137 payto); 138 GNUNET_PQ_cleanup_result (rs); 139 } 140 } 141 142 143 enum GNUNET_DB_QueryStatus 144 TALER_EXCHANGEDB_iterate_kyc_accounts ( 145 struct TALER_EXCHANGEDB_PostgresContext *pg, 146 const struct TALER_NormalizedPaytoHashP *h_payto, 147 enum TALER_EXCHANGE_YesNoAll investigation_only, 148 enum TALER_EXCHANGE_YesNoAll open_only, 149 enum TALER_EXCHANGE_YesNoAll high_risk_only, 150 uint64_t offset, 151 int64_t limit, 152 TALER_EXCHANGEDB_AmlAccountListCallback cb, 153 void *cb_cls) 154 { 155 uint64_t ulimit = GNUNET_MIN ((uint64_t) MAX_RECORDS, 156 TALER_EXCHANGEDB_abs_limit (limit)); 157 const struct TALER_NormalizedPaytoHashP no_h_payto = { 0 }; 158 struct GNUNET_PQ_QueryParam params[] = { 159 GNUNET_PQ_query_param_uint64 (&offset), 160 GNUNET_PQ_query_param_uint64 (&ulimit), 161 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == 162 investigation_only)), 163 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == 164 investigation_only)), 165 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == 166 open_only)), 167 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == 168 open_only)), 169 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == 170 high_risk_only)), 171 GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == 172 high_risk_only)), 173 GNUNET_PQ_query_param_bool (NULL == h_payto), 174 GNUNET_PQ_query_param_auto_from_type (NULL == h_payto 175 ? &no_h_payto 176 : h_payto), 177 GNUNET_PQ_query_param_end 178 }; 179 struct KycAccountResultContext ctx = { 180 .cb = cb, 181 .cb_cls = cb_cls, 182 .pg = pg, 183 .status = GNUNET_OK 184 }; 185 enum GNUNET_DB_QueryStatus qs; 186 const char *stmt = (limit > 0) 187 ? "iterate_kyc_accounts_inc" 188 : "iterate_kyc_accounts_dec"; 189 190 PREPARE (pg, 191 "iterate_kyc_accounts_inc", 192 "SELECT" 193 " kt.kyc_target_serial_id" 194 ",kt.h_normalized_payto AS h_payto" 195 ",kt.open_time" 196 ",kt.close_time" 197 ",lo.jproperties ->> 'FILE_NOTE' AS comments" 198 ",lo.jproperties ->> 'CUSTOMER_LABEL' AS customer_label" 199 ",COALESCE(lo.to_investigate,FALSE) AS to_investigate" 200 ",COALESCE((lo.jproperties ->> 'HIGH_RISK_CUSTOMER')::bool,FALSE) AS high_risk" 201 ",wt.payto_uri" 202 " FROM kyc_targets kt" 203 " LEFT JOIN legitimization_outcomes lo" 204 " ON (lo.h_payto = kt.h_normalized_payto)" 205 " LEFT JOIN LATERAL (" 206 " SELECT payto_uri" 207 " FROM wire_targets" 208 " WHERE h_normalized_payto = kt.h_normalized_payto" 209 " ORDER BY wire_target_serial_id DESC" 210 " LIMIT 1" 211 " ) wt ON true" 212 " WHERE (kyc_target_serial_id > $1)" 213 // select most recent outcomes only 214 " AND COALESCE (lo.is_active, TRUE)" 215 " AND ($3 OR (COALESCE(lo.to_investigate,FALSE) = $4))" 216 // An AML file is open between ACCOUNT_OPEN and ACCOUNT_IDLE. 217 " AND ($5 OR (((kt.open_time IS NOT NULL)" 218 " AND (kt.close_time IS NULL)) = $6))" 219 " AND ($7 OR ((COALESCE((lo.jproperties ->>'HIGH_RISK_CUSTOMER')::bool,FALSE) = $8)))" 220 " AND ($9 OR (kt.h_normalized_payto = $10))" 221 " ORDER BY kt.kyc_target_serial_id ASC" 222 " LIMIT $2"); 223 PREPARE (pg, 224 "iterate_kyc_accounts_dec", 225 "SELECT" 226 " kt.kyc_target_serial_id" 227 ",kt.h_normalized_payto AS h_payto" 228 ",kt.open_time" 229 ",kt.close_time" 230 ",lo.jproperties ->> 'FILE_NOTE' AS comments" 231 ",lo.jproperties ->> 'CUSTOMER_LABEL' AS customer_label" 232 ",COALESCE(lo.to_investigate,FALSE) AS to_investigate" 233 ",COALESCE((lo.jproperties ->> 'HIGH_RISK_CUSTOMER')::bool,FALSE) AS high_risk" 234 ",wt.payto_uri" 235 " FROM kyc_targets kt" 236 " LEFT JOIN legitimization_outcomes lo" 237 " ON (lo.h_payto = kt.h_normalized_payto)" 238 " LEFT JOIN LATERAL (" 239 " SELECT payto_uri" 240 " FROM wire_targets" 241 " WHERE h_normalized_payto = kt.h_normalized_payto" 242 " ORDER BY wire_target_serial_id DESC" 243 " LIMIT 1" 244 " ) wt ON true" 245 " WHERE (kyc_target_serial_id < $1)" 246 // select most recent outcomes only 247 " AND COALESCE (lo.is_active, TRUE)" 248 " AND ($3 OR (COALESCE(lo.to_investigate,FALSE) = $4))" 249 // An AML file is open between ACCOUNT_OPEN and ACCOUNT_IDLE. 250 " AND ($5 OR (((kt.open_time IS NOT NULL)" 251 " AND (kt.close_time IS NULL)) = $6))" 252 " AND ($7 OR ((COALESCE((lo.jproperties ->>'HIGH_RISK_CUSTOMER')::bool,FALSE) = $8)))" 253 " AND ($9 OR (kt.h_normalized_payto = $10))" 254 " ORDER BY kt.kyc_target_serial_id DESC" 255 " LIMIT $2"); 256 qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, 257 stmt, 258 params, 259 &handle_kyc_account_cb, 260 &ctx); 261 if (GNUNET_OK != ctx.status) 262 return GNUNET_DB_STATUS_HARD_ERROR; 263 return qs; 264 }