diff options
author | Christian Grothoff <christian@grothoff.org> | 2022-08-11 23:35:33 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2022-08-11 23:35:33 +0200 |
commit | 1009084e94b8e8cf19e3b5568c3cccaba2bd2209 (patch) | |
tree | a346997dedd05f685ba7addc59e288dfa550ad0e /src/exchange/taler-exchange-httpd_withdraw.c | |
parent | b061ea85c84facfc78c34edface367c5f040bc9c (diff) | |
download | exchange-1009084e94b8e8cf19e3b5568c3cccaba2bd2209.tar.gz exchange-1009084e94b8e8cf19e3b5568c3cccaba2bd2209.tar.bz2 exchange-1009084e94b8e8cf19e3b5568c3cccaba2bd2209.zip |
major rework of the KYC logic, making it more configurable, not complete, but tests pass again
Diffstat (limited to 'src/exchange/taler-exchange-httpd_withdraw.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_withdraw.c | 170 |
1 files changed, 104 insertions, 66 deletions
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index ee2f410d0..255f7d948 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -27,6 +27,7 @@ #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include "taler_json_lib.h" +#include "taler_kyclogic_lib.h" #include "taler_mhd_lib.h" #include "taler-exchange-httpd_withdraw.h" #include "taler-exchange-httpd_responses.h" @@ -45,15 +46,6 @@ struct WithdrawContext struct TALER_BlindedCoinHashP h_coin_envelope; /** - * Value of the coin being exchanged (matching the denomination key) - * plus the transaction fee. We include this in what is being - * signed so that we can verify a reserve's remaining total balance - * without needing to access the respective denomination key - * information each time. - */ - struct TALER_Amount amount_with_fee; - - /** * Blinded planchet. */ struct TALER_BlindedPlanchet blinded_planchet; @@ -68,10 +60,67 @@ struct WithdrawContext */ struct TALER_EXCHANGEDB_KycStatus kyc; + /** + * Hash of the payto-URI representing the reserve + * from which we are withdrawing. + */ + struct TALER_PaytoHashP h_payto; + + /** + * Current time for the DB transaction. + */ + struct GNUNET_TIME_Timestamp now; + }; /** + * Function called to iterate over KYC-relevant + * transaction amounts for a particular time range. + * Called within a database transaction, so must + * not start a new one. + * + * @param cls closure, identifies the event type and + * account to iterate over events for + * @param limit maximum time-range for which events + * should be fetched (timestamp in the past) + * @param cb function to call on each event found, + * events must be returned in reverse chronological + * order + * @param cb_cls closure for @a cb + */ +static void +withdraw_amount_cb (void *cls, + struct GNUNET_TIME_Absolute limit, + TALER_EXCHANGEDB_KycAmountCallback cb, + void *cb_cls) +{ + struct WithdrawContext *wc = cls; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signaling amount %s for KYC check\n", + TALER_amount2s (&wc->collectable.amount_with_fee)); + if (GNUNET_OK != + cb (cb_cls, + &wc->collectable.amount_with_fee, + wc->now.abs_time)) + return; + qs = TEH_plugin->select_withdraw_amounts_for_kyc_check ( + TEH_plugin->cls, + &wc->h_payto, + limit, + cb, + cb_cls); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got %d additional transactions for this withdrawal and limit %llu\n", + qs, + (unsigned long long) limit.abs_value_us); + GNUNET_break (qs >= 0); +} + + +/** * Function implementing withdraw transaction. Runs the * transaction logic; IF it returns a non-error code, the transaction * logic MUST NOT queue a MHD response. IF it returns an hard error, @@ -98,25 +147,55 @@ withdraw_transaction (void *cls, bool found = false; bool balance_ok = false; bool nonce_ok = false; - struct GNUNET_TIME_Timestamp now; uint64_t ruuid; const struct TALER_CsNonce *nonce; const struct TALER_BlindedPlanchet *bp; + const char *kyc_required; - now = GNUNET_TIME_timestamp_get (); + wc->now = GNUNET_TIME_timestamp_get (); + qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls, + &wc->collectable.reserve_pub, + &wc->h_payto); + if (qs < 0) + return qs; + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + kyc_required = TALER_KYCLOGIC_kyc_test_required ( + TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, + &wc->h_payto, + TEH_plugin->select_satisfied_kyc_processes, + TEH_plugin->cls, + &withdraw_amount_cb, + wc); + if (NULL != kyc_required) + { + /* insert KYC requirement into DB! */ + wc->kyc.ok = false; + return TEH_plugin->insert_kyc_requirement_for_account ( + TEH_plugin->cls, + kyc_required, + &wc->h_payto, + &wc->kyc.payment_target_uuid); + } + wc->kyc.ok = true; bp = &wc->blinded_planchet; - nonce = - (TALER_DENOMINATION_CS == bp->cipher) + nonce = (TALER_DENOMINATION_CS == bp->cipher) ? &bp->details.cs_blinded_planchet.nonce : NULL; qs = TEH_plugin->do_withdraw (TEH_plugin->cls, nonce, &wc->collectable, - now, + wc->now, &found, &balance_ok, &nonce_ok, - &wc->kyc, &ruuid); if (0 > qs) { @@ -153,56 +232,8 @@ withdraw_transaction (void *cls, NULL); return GNUNET_DB_STATUS_HARD_ERROR; } - if ( (TEH_KYC_NONE != TEH_kyc_config.mode) && - (! wc->kyc.ok) && - (TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) ) - { - /* Wallet-to-wallet payments _always_ require KYC */ - *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, - GNUNET_JSON_pack_uint64 ("payment_target_uuid", - wc->kyc.payment_target_uuid)); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if ( (TEH_KYC_NONE != TEH_kyc_config.mode) && - (! wc->kyc.ok) && - (TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) && - (! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) ) - { - /* Withdraws require KYC if above threshold */ - enum GNUNET_DB_QueryStatus qs2; - bool below_limit; - - qs2 = TEH_plugin->do_withdraw_limit_check ( - TEH_plugin->cls, - ruuid, - GNUNET_TIME_absolute_subtract (now.abs_time, - TEH_kyc_config.withdraw_period), - &TEH_kyc_config.withdraw_limit, - &below_limit); - if (0 > qs2) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2); - if (GNUNET_DB_STATUS_HARD_ERROR == qs2) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "do_withdraw_limit_check"); - return qs2; - } - if (! below_limit) - { - *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, - GNUNET_JSON_pack_uint64 ("payment_target_uuid", - wc->kyc.payment_target_uuid)); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - TEH_METRICS_num_success[TEH_MT_SUCCESS_BATCH_WITHDRAW]++; + TEH_METRICS_num_success[TEH_MT_SUCCESS_WITHDRAW]++; return qs; } @@ -274,7 +305,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc, 0, sizeof (wc)); wc.collectable.reserve_pub = *reserve_pub; - { enum GNUNET_GenericReturnValue res; @@ -459,6 +489,14 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc, /* Clean up and send back final response */ GNUNET_JSON_parse_free (spec); + if (! wc.kyc.ok) + { + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, + GNUNET_JSON_pack_uint64 ("payment_target_uuid", + wc.kyc.payment_target_uuid)); + } { MHD_RESULT ret; |