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