-basic logic for withdraw KYC checks
This commit is contained in:
parent
7d62fa065b
commit
ca12adced4
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user