summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_withdraw.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-08-11 23:35:33 +0200
committerChristian Grothoff <christian@grothoff.org>2022-08-11 23:35:33 +0200
commit1009084e94b8e8cf19e3b5568c3cccaba2bd2209 (patch)
treea346997dedd05f685ba7addc59e288dfa550ad0e /src/exchange/taler-exchange-httpd_withdraw.c
parentb061ea85c84facfc78c34edface367c5f040bc9c (diff)
downloadexchange-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.c170
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;