-basic logic for withdraw KYC checks

This commit is contained in:
Christian Grothoff 2021-10-14 21:06:47 +02:00
parent 7d62fa065b
commit ca12adced4
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 265 additions and 8 deletions

View File

@ -1083,10 +1083,13 @@ verify_reserve_balance (void *cls,
internal audit, as otherwise the balance of the 'reserves' table
is not replicated at the auditor. */
struct TALER_EXCHANGEDB_Reserve reserve;
struct TALER_EXCHANGEDB_KycStatus kyc;
reserve.pub = rs->reserve_pub;
qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls,
&reserve);
&reserve,
&kyc);
// FIXME: figure out what to do with KYC status!
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
/* If the exchange doesn't have this reserve in the summary, it

View File

@ -129,9 +129,44 @@ struct WithdrawContext
*/
struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
/**
* KYC status for the operation.
*/
struct TALER_EXCHANGEDB_KycStatus kyc;
/**
* Set to true if the operation was denied due to
* failing @e kyc checks.
*/
bool kyc_denied;
};
/**
* Function called with another amount that was
* already withdrawn. Accumulates all amounts in
* @a cls.
*
* @param[in,out] cls a `struct TALER_Amount`
* @param val value to add to @a cls
*/
static void
accumulate_withdraws (void *cls,
const struct TALER_Amount *val)
{
struct TALER_Amount *acc = cls;
if (GNUNET_OK !=
TALER_amount_is_valid (acc))
return; /* ignore */
GNUNET_break (0 <=
TALER_amount_add (acc,
acc,
val));
}
/**
* Function implementing withdraw transaction. Runs the
* transaction logic; IF it returns a non-error code, the transaction
@ -165,7 +200,6 @@ withdraw_transaction (void *cls,
struct TALER_EXCHANGEDB_Reserve r;
enum GNUNET_DB_QueryStatus qs;
struct TALER_DenominationSignature denom_sig;
struct TALER_EXCHANGEDB_KycStatus kyc;
#if OPTIMISTIC_SIGN
/* store away optimistic signature to protect
@ -211,7 +245,7 @@ withdraw_transaction (void *cls,
TALER_B2S (&r.pub));
qs = TEH_plugin->reserves_get (TEH_plugin->cls,
&r,
&kyc);
&wc->kyc);
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
@ -270,11 +304,60 @@ withdraw_transaction (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR;
}
if ( (! kyc.ok) &&
(TEH_KYC_NONE != TEH_kyc_config.mode) )
if ( (! wc->kyc.ok) &&
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
(TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
{
// FIXME: check if we are above the limit
// for KYC, and if so, deny the transaction!
/* Wallet-to-wallet payments _always_ require KYC */
wc->kyc_denied = true;
return qs;
}
if ( (! wc->kyc.ok) &&
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
(TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
(! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
{
/* Withdraws require KYC if above threshold */
struct TALER_Amount acc;
enum GNUNET_DB_QueryStatus qs2;
TALER_amount_set_zero (TEH_currency,
&acc);
accumulate_withdraws (&acc,
&wc->amount_required);
qs2 = TEH_plugin->select_withdraw_amounts_by_account (
TEH_plugin->cls,
&wc->wsrd.reserve_pub,
TEH_kyc_config.withdraw_period,
&accumulate_withdraws,
&acc);
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,
"withdraw details");
return qs2;
}
if (GNUNET_OK !=
TALER_amount_is_valid (&acc))
{
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_ec (connection,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (1 == /* 1: acc > withdraw_limit */
TALER_amount_cmp (&acc,
&TEH_kyc_config.withdraw_limit))
{
wc->kyc_denied = true;
return qs;
}
}
/* Balance is good, sign the coin! */
@ -338,6 +421,9 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
enum TALER_ErrorCode ec;
struct TEH_DenominationKey *dk;
memset (&wc,
0,
sizeof (wc));
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
@ -480,6 +566,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
#endif
/* run transaction and sign (if not optimistically signed before) */
wc.kyc_denied = false;
{
MHD_RESULT mhd_ret;
@ -499,9 +586,21 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
}
}
/* Clean up and send back final (positive) response */
/* Clean up and send back final response */
GNUNET_JSON_parse_free (spec);
if (wc.kyc_denied)
{
if (NULL != wc.collectable.sig.rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
return TALER_MHD_REPLY_JSON_PACK (
rc->connection,
MHD_HTTP_ACCEPTED,
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
wc.kyc.payment_target_uuid));
}
{
MHD_RESULT ret;

View File

@ -4388,6 +4388,126 @@ postgres_get_reserve_history (void *cls,
}
/**
* Closure for withdraw_amount_by_account_cb()
*/
struct WithdrawAmountByAccountContext
{
/**
* Function to call on each amount.
*/
TALER_EXCHANGEDB_WithdrawHistoryCallback cb;
/**
* Closure for @e cb
*/
void *cb_cls;
/**
* Our plugin's context.
*/
struct PostgresClosure *pg;
/**
* Set to true on failures.
*/
bool failed;
};
/**
* Helper function for #postgres_select_withdraw_amounts_by_account().
* To be called with the results of a SELECT statement
* that has returned @a num_results results.
*
* @param cls closure of type `struct WithdrawAmountByAccountContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
withdraw_amount_by_account_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct WithdrawAmountByAccountContext *wac = cls;
struct PostgresClosure *pg = wac->pg;
for (unsigned int i = 0; num_results; i++)
{
struct TALER_Amount val;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("val",
&val),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
wac->failed = true;
return;
}
wac->cb (wac->cb_cls,
&val);
}
}
/**
* Find out all of the amounts that have been withdrawn
* so far from the same bank account that created the
* given reserve.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param reserve_pub reserve to select withdrawals by
* @param duration how far back should we select withdrawals
* @param cb function to call on each amount withdrawn
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_select_withdraw_amounts_by_account (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct GNUNET_TIME_Relative duration,
TALER_EXCHANGEDB_WithdrawHistoryCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct WithdrawAmountByAccountContext wac = {
.pg = pg,
.cb = cb,
.cb_cls = cb_cls
};
struct GNUNET_TIME_Absolute start
= GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
duration);
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_absolute_time (&start),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"select_XXX",
params,
&withdraw_amount_by_account_cb,
&wac);
if (wac.failed)
{
GNUNET_break (0);
qs = GNUNET_DB_STATUS_HARD_ERROR;
}
return qs;
}
/**
* Check if we have the specified deposit already in the database.
*
@ -10957,6 +11077,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->get_withdraw_info = &postgres_get_withdraw_info;
plugin->insert_withdraw_info = &postgres_insert_withdraw_info;
plugin->get_reserve_history = &postgres_get_reserve_history;
plugin->select_withdraw_amounts_by_account
= &postgres_select_withdraw_amounts_by_account;
plugin->free_reserve_history = &common_free_reserve_history;
plugin->count_known_coins = &postgres_count_known_coins;
plugin->ensure_coin_known = &postgres_ensure_coin_known;

View File

@ -2011,6 +2011,18 @@ typedef enum GNUNET_GenericReturnValue
const struct TALER_WireTransferIdentifierRawP *wtid);
/**
* Function called with the amounts historically
* withdrawn from the same origin account.
*
* @param cls closure
* @param val one of the withdrawn amounts
*/
typedef void
(*TALER_EXCHANGEDB_WithdrawHistoryCallback)(
void *cls,
const struct TALER_Amount *val);
/**
* Function called with details about expired reserves.
*
@ -2450,6 +2462,27 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_EXCHANGEDB_ReserveHistory **rhp);
/**
* Find out all of the amounts that have been withdrawn
* so far from the same bank account that created the
* given reserve.
*
* @param cls closure
* @param reserve_pub reserve to select withdrawals by
* @param duration how far back should we select withdrawals
* @param cb function to call on each amount withdrawn
* @param cb_cls closure for @a cb
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
(*select_withdraw_amounts_by_account)(
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct GNUNET_TIME_Relative duration,
TALER_EXCHANGEDB_WithdrawHistoryCallback cb,
void *cb_cls);
/**
* Free memory associated with the given reserve history.
*