taler-exchange-httpd_kyc-wallet.c (9140B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2021, 2022, 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_kyc-wallet.c 18 * @brief Handle request for wallet for KYC check. 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include <gnunet/gnunet_json_lib.h> 24 #include <jansson.h> 25 #include <microhttpd.h> 26 #include <pthread.h> 27 #include "taler/taler_json_lib.h" 28 #include "taler/taler_mhd_lib.h" 29 #include "taler/taler_kyclogic_lib.h" 30 #include "taler-exchange-httpd_common_kyc.h" 31 #include "taler-exchange-httpd_kyc-wallet.h" 32 #include "taler-exchange-httpd_responses.h" 33 34 35 /** 36 * Context for the request. 37 */ 38 struct KycRequestContext 39 { 40 41 /** 42 * Kept in a DLL. 43 */ 44 struct KycRequestContext *next; 45 46 /** 47 * Kept in a DLL. 48 */ 49 struct KycRequestContext *prev; 50 51 /** 52 * Handle for legitimization check. 53 */ 54 struct TEH_LegitimizationCheckHandle *lch; 55 56 /** 57 * Payto URI of the reserve. 58 */ 59 struct TALER_NormalizedPayto payto_uri; 60 61 /** 62 * Request context. 63 */ 64 struct TEH_RequestContext *rc; 65 66 /** 67 * Response to return. Note that the response must 68 * be queued or destroyed by the callee. NULL 69 * if the legitimization check was successful and the handler should return 70 * a handler-specific result. 71 */ 72 struct MHD_Response *response; 73 74 /** 75 * Public key of the reserve/wallet this is about. 76 */ 77 struct TALER_NormalizedPaytoHashP h_payto; 78 79 /** 80 * The wallet's public key 81 */ 82 union TALER_AccountPublicKeyP wallet_pub; 83 84 /** 85 * Balance threshold crossed by the wallet. 86 */ 87 struct TALER_Amount balance; 88 89 /** 90 * KYC status, with row with the legitimization requirement. 91 */ 92 struct TALER_EXCHANGEDB_KycStatus kyc; 93 94 /** 95 * Smallest amount (over any timeframe) that may 96 * require additional KYC checks (if @a kyc.ok). 97 */ 98 struct TALER_Amount next_threshold; 99 100 /** 101 * When do the current KYC rules possibly expire. 102 * Only valid if @a kyc.ok. 103 */ 104 struct GNUNET_TIME_Timestamp expiration_date; 105 106 /** 107 * HTTP status code for @a response, or 0 108 */ 109 unsigned int http_status; 110 111 }; 112 113 114 /** 115 * Kept in a DLL. 116 */ 117 static struct KycRequestContext *krc_head; 118 119 /** 120 * Kept in a DLL. 121 */ 122 static struct KycRequestContext *krc_tail; 123 124 125 void 126 TEH_kyc_wallet_cleanup () 127 { 128 struct KycRequestContext *krc; 129 130 while (NULL != (krc = krc_head)) 131 { 132 GNUNET_CONTAINER_DLL_remove (krc_head, 133 krc_tail, 134 krc); 135 MHD_resume_connection (krc->rc->connection); 136 } 137 } 138 139 140 /** 141 * Function called to iterate over KYC-relevant 142 * transaction amounts for a particular time range. 143 * Returns the wallet balance. 144 * 145 * @param cls closure, a `struct KycRequestContext` 146 * @param limit maximum time-range for which events 147 * should be fetched (timestamp in the past) 148 * @param cb function to call on each event found, 149 * events must be returned in reverse chronological 150 * order 151 * @param cb_cls closure for @a cb 152 */ 153 static enum GNUNET_DB_QueryStatus 154 balance_iterator (void *cls, 155 struct GNUNET_TIME_Absolute limit, 156 TALER_EXCHANGEDB_KycAmountCallback cb, 157 void *cb_cls) 158 { 159 struct KycRequestContext *krc = cls; 160 enum GNUNET_GenericReturnValue ret; 161 162 (void) limit; 163 ret = cb (cb_cls, 164 &krc->balance, 165 GNUNET_TIME_absolute_get ()); 166 GNUNET_break (GNUNET_SYSERR != ret); 167 if (GNUNET_OK != ret) 168 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 169 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 170 } 171 172 173 /** 174 * Function called with the result of a legitimization 175 * check. 176 * 177 * @param cls must be a `struct KycRequestContext *` 178 * @param lcr legitimization check result 179 */ 180 static void 181 legi_result_cb ( 182 void *cls, 183 const struct TEH_LegitimizationCheckResult *lcr) 184 { 185 struct KycRequestContext *krc = cls; 186 187 TEH_plugin->preflight (TEH_plugin->cls); 188 krc->lch = NULL; 189 krc->http_status = lcr->http_status; 190 krc->response = lcr->response; 191 krc->kyc = lcr->kyc; 192 krc->next_threshold = lcr->next_threshold; 193 krc->expiration_date = lcr->expiration_date; 194 GNUNET_CONTAINER_DLL_remove (krc_head, 195 krc_tail, 196 krc); 197 MHD_resume_connection (krc->rc->connection); 198 TALER_MHD_daemon_trigger (); 199 } 200 201 202 /** 203 * Function to clean up our rh_ctx in @a rc 204 * 205 * @param[in,out] rc context to clean up 206 */ 207 static void 208 krc_cleaner (struct TEH_RequestContext *rc) 209 { 210 struct KycRequestContext *krc = rc->rh_ctx; 211 212 if (NULL != krc->lch) 213 { 214 TEH_legitimization_check_cancel (krc->lch); 215 krc->lch = NULL; 216 } 217 GNUNET_free (krc->payto_uri.normalized_payto); 218 GNUNET_free (krc); 219 } 220 221 222 MHD_RESULT 223 TEH_handler_kyc_wallet ( 224 struct TEH_RequestContext *rc, 225 const json_t *root, 226 const char *const args[]) 227 { 228 struct KycRequestContext *krc = rc->rh_ctx; 229 230 if (NULL == krc) 231 { 232 krc = GNUNET_new (struct KycRequestContext); 233 krc->rc = rc; 234 rc->rh_ctx = krc; 235 rc->rh_cleaner = &krc_cleaner; 236 { 237 struct TALER_ReserveSignatureP reserve_sig; 238 struct GNUNET_JSON_Specification spec[] = { 239 GNUNET_JSON_spec_fixed_auto ("reserve_sig", 240 &reserve_sig), 241 GNUNET_JSON_spec_fixed_auto ("reserve_pub", 242 &krc->wallet_pub.reserve_pub), 243 TALER_JSON_spec_amount ("balance", 244 TEH_currency, 245 &krc->balance), 246 GNUNET_JSON_spec_end () 247 }; 248 enum GNUNET_GenericReturnValue ret; 249 250 (void) args; 251 ret = TALER_MHD_parse_json_data (rc->connection, 252 root, 253 spec); 254 if (GNUNET_SYSERR == ret) 255 return MHD_NO; /* hard failure */ 256 if (GNUNET_NO == ret) 257 return MHD_YES; /* failure */ 258 259 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 260 if (GNUNET_OK != 261 TALER_wallet_account_setup_verify ( 262 &krc->wallet_pub.reserve_pub, 263 &krc->balance, 264 &reserve_sig)) 265 { 266 GNUNET_break_op (0); 267 return TALER_MHD_reply_with_error ( 268 rc->connection, 269 MHD_HTTP_FORBIDDEN, 270 TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID, 271 NULL); 272 } 273 } 274 krc->payto_uri 275 = TALER_reserve_make_payto (TEH_base_url, 276 &krc->wallet_pub.reserve_pub); 277 TALER_normalized_payto_hash (krc->payto_uri, 278 &krc->h_payto); 279 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 280 "h_payto of wallet %s is %s\n", 281 krc->payto_uri.normalized_payto, 282 TALER_B2S (&krc->h_payto)); 283 { 284 struct TALER_FullPayto fake_full_payto; 285 286 GNUNET_asprintf (&fake_full_payto.full_payto, 287 "%s?receiver-name=wallet", 288 krc->payto_uri.normalized_payto); 289 krc->lch = TEH_legitimization_check ( 290 &rc->async_scope_id, 291 TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE, 292 fake_full_payto, 293 &krc->h_payto, 294 &krc->wallet_pub, 295 &balance_iterator, 296 krc, 297 &legi_result_cb, 298 krc); 299 GNUNET_free (fake_full_payto.full_payto); 300 } 301 GNUNET_assert (NULL != krc->lch); 302 MHD_suspend_connection (rc->connection); 303 GNUNET_CONTAINER_DLL_insert (krc_head, 304 krc_tail, 305 krc); 306 return MHD_YES; 307 } 308 if (NULL != krc->response) 309 return MHD_queue_response (rc->connection, 310 krc->http_status, 311 krc->response); 312 if (krc->kyc.ok) 313 { 314 bool have_ts 315 = TALER_amount_is_valid (&krc->next_threshold); 316 317 /* KYC not required or already satisfied */ 318 return TALER_MHD_REPLY_JSON_PACK ( 319 rc->connection, 320 MHD_HTTP_OK, 321 GNUNET_JSON_pack_timestamp ("expiration_time", 322 krc->expiration_date), 323 GNUNET_JSON_pack_allow_null ( 324 TALER_JSON_pack_amount ("next_threshold", 325 have_ts 326 ? &krc->next_threshold 327 : NULL))); 328 } 329 return TEH_RESPONSE_reply_kyc_required (rc->connection, 330 &krc->h_payto, 331 &krc->kyc, 332 false); 333 } 334 335 336 /* end of taler-exchange-httpd_kyc-wallet.c */