taler-exchange-kyc-trigger.c (11040B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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 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 taler-exchange-kyc-trigger.c 18 * @brief Support for manually triggering KYC/AML processes for testing 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_json_lib.h> 23 #include <gnunet/gnunet_util_lib.h> 24 #include <microhttpd.h> 25 #include "taler/taler_json_lib.h" 26 #include "taler/taler-exchange/get-kyc-check-H_NORMALIZED_PAYTO.h" 27 #include "taler/taler_exchange_service.h" 28 29 30 /** 31 * Our private key. 32 */ 33 static struct TALER_ReservePrivateKeyP reserve_priv; 34 35 /** 36 * Our public key. 37 */ 38 static struct TALER_ReservePublicKeyP reserve_pub; 39 40 /** 41 * Our context for making HTTP requests. 42 */ 43 static struct GNUNET_CURL_Context *ctx; 44 45 /** 46 * Reschedule context for #ctx. 47 */ 48 static struct GNUNET_CURL_RescheduleContext *rc; 49 50 /** 51 * Handle to the exchange's configuration 52 */ 53 static const struct GNUNET_CONFIGURATION_Handle *kcfg; 54 55 /** 56 * Handle for exchange interaction. 57 */ 58 static struct TALER_EXCHANGE_PostKycWalletHandle *kwh; 59 60 /** 61 * Handle for exchange kyc-check interaction. 62 */ 63 static struct TALER_EXCHANGE_GetKycCheckHandle *kc; 64 65 /** 66 * Balance threshold to report to the exchange. 67 */ 68 static struct TALER_Amount balance; 69 70 /** 71 * Return value from main(). 72 */ 73 static int global_ret; 74 75 /** 76 * Currency we have configured. 77 */ 78 static char *currency; 79 80 /** 81 * URL of the exchange we are interacting with 82 * as per our configuration. 83 */ 84 static char *CFG_exchange_url; 85 86 87 /** 88 * Function called with the result of a KYC check. 89 * 90 * @param cls closure 91 * @param ks the account's KYC status details 92 */ 93 static void 94 kyc_status_cb ( 95 void *cls, 96 const struct TALER_EXCHANGE_GetKycCheckResponse *ks) 97 { 98 kc = NULL; 99 switch (ks->hr.http_status) 100 { 101 case MHD_HTTP_OK: 102 { 103 char *buf; 104 105 buf = GNUNET_STRINGS_data_to_string_alloc ( 106 &ks->details.ok.access_token, 107 sizeof (ks->details.ok.access_token)); 108 fprintf (stdout, 109 "KYC SPA at %skyc-spa/%s\n", 110 CFG_exchange_url, 111 buf); 112 GNUNET_free (buf); 113 } 114 break; 115 case MHD_HTTP_ACCEPTED: 116 { 117 char *buf; 118 119 buf = GNUNET_STRINGS_data_to_string_alloc ( 120 &ks->details.ok.access_token, 121 sizeof (ks->details.ok.access_token)); 122 fprintf (stdout, 123 "KYC SPA at %skyc-spa/%s\n", 124 CFG_exchange_url, 125 buf); 126 GNUNET_free (buf); 127 } 128 break; 129 case MHD_HTTP_FORBIDDEN: 130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 131 "KYC auth now required?\n"); 132 break; 133 } 134 GNUNET_SCHEDULER_shutdown (); 135 } 136 137 138 /** 139 * Function called with the result for a wallet looking 140 * up its KYC payment target. 141 * 142 * @param cls closure 143 * @param ks the wallets KYC payment target details 144 */ 145 static void 146 kyc_wallet_cb ( 147 void *cls, 148 const struct TALER_EXCHANGE_PostKycWalletResponse *ks) 149 { 150 kwh = NULL; 151 switch (ks->hr.http_status) 152 { 153 case MHD_HTTP_OK: 154 fprintf (stdout, 155 "OK, next threshold at %s\n", 156 TALER_amount2s (&ks->details.ok.next_threshold)); 157 break; 158 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 159 { 160 const struct TALER_EXCHANGE_KycNeededRedirect *knr 161 = &ks->details.unavailable_for_legal_reasons; 162 char *ps; 163 164 ps = GNUNET_STRINGS_data_to_string_alloc (&knr->h_payto, 165 sizeof (knr->h_payto)); 166 fprintf (stderr, 167 "KYC needed (%llu, %s) for %s\n", 168 (unsigned long long) knr->requirement_row, 169 knr->bad_kyc_auth 170 ? "KYC auth needed" 171 : "KYC auth OK", 172 ps); 173 GNUNET_free (ps); 174 if (! knr->bad_kyc_auth) 175 { 176 struct TALER_NormalizedPaytoHashP h_payto; 177 union TALER_AccountPrivateKeyP pk; 178 struct TALER_NormalizedPayto np; 179 180 np = TALER_reserve_make_payto (CFG_exchange_url, 181 &reserve_pub); 182 TALER_normalized_payto_hash (np, 183 &h_payto); 184 GNUNET_free (np.normalized_payto); 185 pk.reserve_priv = reserve_priv; 186 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 187 "Requesting /kyc-check to determine KYC entrypoint\n"); 188 kc = TALER_EXCHANGE_get_kyc_check_create (ctx, 189 CFG_exchange_url, 190 &h_payto, 191 &pk); 192 GNUNET_break (NULL != kc); 193 if (NULL == kc) 194 break; 195 GNUNET_assert (TALER_EC_NONE == 196 TALER_EXCHANGE_get_kyc_check_start ( 197 kc, 198 &kyc_status_cb, 199 NULL)); 200 return; 201 } 202 } 203 break; 204 default: 205 fprintf (stdout, 206 "Unexpected HTTP status %u\n", 207 ks->hr.http_status); 208 break; 209 } 210 GNUNET_SCHEDULER_shutdown (); 211 } 212 213 214 /** 215 * Shutdown task. Invoked when the application is being terminated. 216 * 217 * @param cls NULL 218 */ 219 static void 220 do_shutdown (void *cls) 221 { 222 (void) cls; 223 if (NULL != kwh) 224 { 225 TALER_EXCHANGE_post_kyc_wallet_cancel (kwh); 226 kwh = NULL; 227 } 228 if (NULL != kc) 229 { 230 TALER_EXCHANGE_get_kyc_check_cancel (kc); 231 kc = NULL; 232 } 233 if (NULL != ctx) 234 { 235 GNUNET_CURL_fini (ctx); 236 ctx = NULL; 237 } 238 if (NULL != rc) 239 { 240 GNUNET_CURL_gnunet_rc_destroy (rc); 241 rc = NULL; 242 } 243 } 244 245 246 /** 247 * Load the reserve key. 248 * 249 * @param do_create #GNUNET_YES if the key may be created 250 * @return #GNUNET_OK on success 251 */ 252 static enum GNUNET_GenericReturnValue 253 load_reserve_key (int do_create) 254 { 255 char *fn; 256 257 if (GNUNET_OK == 258 GNUNET_CONFIGURATION_get_value_filename (kcfg, 259 "exchange-testing", 260 "RESERVE_PRIV_FILE", 261 &fn)) 262 { 263 enum GNUNET_GenericReturnValue ret; 264 265 if (GNUNET_YES != 266 GNUNET_DISK_file_test (fn)) 267 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 268 "Account private key `%s' does not exist yet, creating it!\n", 269 fn); 270 ret = GNUNET_CRYPTO_eddsa_key_from_file (fn, 271 do_create, 272 &reserve_priv.eddsa_priv); 273 if (GNUNET_SYSERR == ret) 274 { 275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 276 "Failed to initialize master key from file `%s': %s\n", 277 fn, 278 "could not create file"); 279 GNUNET_free (fn); 280 return GNUNET_SYSERR; 281 } 282 GNUNET_free (fn); 283 } 284 else 285 { 286 GNUNET_CRYPTO_eddsa_key_create (&reserve_priv.eddsa_priv); 287 } 288 GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv, 289 &reserve_pub.eddsa_pub); 290 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 291 "Using reserve public key %s\n", 292 TALER_B2S (&reserve_pub)); 293 return GNUNET_OK; 294 } 295 296 297 /** 298 * Main function that will be run. 299 * 300 * @param cls closure 301 * @param args remaining command-line arguments 302 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 303 * @param cfg configuration 304 */ 305 static void 306 run (void *cls, 307 char *const *args, 308 const char *cfgfile, 309 const struct GNUNET_CONFIGURATION_Handle *cfg) 310 { 311 (void) cls; 312 (void) cfgfile; 313 kcfg = cfg; 314 315 if (GNUNET_OK != 316 load_reserve_key (GNUNET_YES)) 317 { 318 GNUNET_break (0); 319 global_ret = EXIT_FAILURE; 320 return; 321 } 322 if (GNUNET_OK != 323 TALER_config_get_currency (kcfg, 324 "exchange", 325 ¤cy)) 326 { 327 global_ret = EXIT_NOTCONFIGURED; 328 return; 329 } 330 if ( (GNUNET_OK != 331 TALER_amount_is_valid (&balance)) || 332 (0 != strcmp (balance.currency, 333 currency)) ) 334 { 335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 336 "Invalid balance threshold `%s'\n", 337 TALER_amount2s (&balance)); 338 global_ret = EXIT_FAILURE; 339 return; 340 } 341 if ( (NULL == CFG_exchange_url) && 342 (GNUNET_OK != 343 GNUNET_CONFIGURATION_get_value_string (kcfg, 344 "exchange", 345 "BASE_URL", 346 &CFG_exchange_url)) ) 347 { 348 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 349 "exchange", 350 "BASE_URL"); 351 global_ret = EXIT_NOTCONFIGURED; 352 GNUNET_SCHEDULER_shutdown (); 353 return; 354 } 355 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 356 &rc); 357 rc = GNUNET_CURL_gnunet_rc_create (ctx); 358 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 359 NULL); 360 kwh = TALER_EXCHANGE_post_kyc_wallet_create (ctx, 361 CFG_exchange_url, 362 &reserve_priv, 363 &balance); 364 if (NULL == kwh) 365 { 366 GNUNET_break (0); 367 GNUNET_SCHEDULER_shutdown (); 368 } 369 GNUNET_assert (TALER_EC_NONE == 370 TALER_EXCHANGE_post_kyc_wallet_start (kwh, 371 &kyc_wallet_cb, 372 NULL)); 373 } 374 375 376 /** 377 * The main function of the taler-exchange-kyc-trigger tool. 378 * 379 * @param argc number of arguments from the command line 380 * @param argv command line arguments 381 * @return 0 ok, 1 on error 382 */ 383 int 384 main (int argc, 385 char *const *argv) 386 { 387 struct GNUNET_GETOPT_CommandLineOption options[] = { 388 TALER_getopt_get_amount ('b', 389 "balance", 390 "AMOUNT", 391 "balance threshold to report to the exchange", 392 &balance), 393 GNUNET_GETOPT_OPTION_END 394 }; 395 enum GNUNET_GenericReturnValue ret; 396 397 ret = GNUNET_PROGRAM_run ( 398 TALER_EXCHANGE_project_data (), 399 argc, argv, 400 "taler-exchange-kyc-trigger", 401 gettext_noop ( 402 "Trigger KYC/AML measures based on high wallet balance for testing"), 403 options, 404 &run, NULL); 405 if (GNUNET_SYSERR == ret) 406 return EXIT_INVALIDARGUMENT; 407 if (GNUNET_NO == ret) 408 return EXIT_SUCCESS; 409 return global_ret; 410 } 411 412 413 /* end of taler-exchange-kyc-trigger.c */