major rework of withdraw transaction to use stored procedure and (presumably) reduce serialization failures by avoiding SELECT before INSERT
This commit is contained in:
parent
c0d2af8a49
commit
67de20d26e
@ -2110,6 +2110,8 @@ TEH_keys_denomination_by_hash2 (
|
||||
&h_denom_pub->hash);
|
||||
if (NULL == dk)
|
||||
{
|
||||
if (NULL == conn)
|
||||
return NULL;
|
||||
*mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
|
||||
h_denom_pub);
|
||||
return NULL;
|
||||
|
@ -235,11 +235,13 @@ reserve_history_transaction (void *cls,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct ReserveHistoryContext *rsc = cls;
|
||||
struct TALER_Amount balance;
|
||||
|
||||
(void) connection;
|
||||
(void) mhd_ret;
|
||||
return TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||
&rsc->reserve_pub,
|
||||
&balance,
|
||||
&rsc->rh);
|
||||
}
|
||||
|
||||
|
@ -91,21 +91,6 @@ struct WithdrawContext
|
||||
*/
|
||||
struct TALER_WithdrawRequestPS wsrd;
|
||||
|
||||
/**
|
||||
* Value of the coin plus withdraw fee.
|
||||
*/
|
||||
struct TALER_Amount amount_required;
|
||||
|
||||
/**
|
||||
* Hash of the denomination public key.
|
||||
*/
|
||||
struct TALER_DenominationHash denom_pub_hash;
|
||||
|
||||
/**
|
||||
* Signature over the request.
|
||||
*/
|
||||
struct TALER_ReserveSignatureP signature;
|
||||
|
||||
/**
|
||||
* Blinded planchet.
|
||||
*/
|
||||
@ -126,39 +111,9 @@ struct WithdrawContext
|
||||
*/
|
||||
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
|
||||
@ -182,67 +137,34 @@ withdraw_transaction (void *cls,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct WithdrawContext *wc = cls;
|
||||
struct TALER_EXCHANGEDB_Reserve r;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct TALER_BlindedDenominationSignature denom_sig;
|
||||
bool found = false;
|
||||
bool balance_ok = false;
|
||||
uint64_t reserve_uuid;
|
||||
struct GNUNET_TIME_Absolute now;
|
||||
|
||||
/* store away optimistic signature to protect
|
||||
it from being overwritten by get_withdraw_info */
|
||||
denom_sig = wc->collectable.sig;
|
||||
memset (&wc->collectable.sig,
|
||||
0,
|
||||
sizeof (wc->collectable.sig));
|
||||
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
|
||||
&wc->wsrd.h_coin_envelope,
|
||||
&wc->collectable);
|
||||
if (0 > qs)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"withdraw details");
|
||||
wc->collectable.sig = denom_sig;
|
||||
return qs;
|
||||
}
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(void) GNUNET_TIME_round_abs (&now);
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Asked to withdraw from %s amount of %s\n",
|
||||
TALER_B2S (&wc->wsrd.reserve_pub),
|
||||
TALER_amount2s (&wc->amount_required));
|
||||
/* Don't sign again if we have already signed the coin */
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
||||
{
|
||||
/* Toss out the optimistic signature, we got another one from the DB;
|
||||
optimization trade-off loses in this case: we unnecessarily computed
|
||||
a signature :-( */
|
||||
TALER_blinded_denom_sig_free (&denom_sig);
|
||||
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
||||
}
|
||||
/* We should never get more than one result, and we handled
|
||||
the errors (negative case) above, so that leaves no results. */
|
||||
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
|
||||
wc->collectable.sig = denom_sig;
|
||||
|
||||
/* Check if balance is sufficient */
|
||||
r.pub = wc->wsrd.reserve_pub; /* other fields of 'r' initialized in reserves_get (if successful) */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Trying to withdraw from reserve: %s\n",
|
||||
TALER_B2S (&r.pub));
|
||||
qs = TEH_plugin->reserves_get (TEH_plugin->cls,
|
||||
&r,
|
||||
&wc->kyc);
|
||||
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
|
||||
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
||||
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
|
||||
&wc->collectable,
|
||||
now,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&wc->kyc,
|
||||
&reserve_uuid);
|
||||
if (0 > qs)
|
||||
{
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"reserves");
|
||||
"do_withdraw");
|
||||
return qs;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
if (! found)
|
||||
{
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
@ -250,29 +172,29 @@ withdraw_transaction (void *cls,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if (0 < TALER_amount_cmp (&wc->amount_required,
|
||||
&r.balance))
|
||||
if (! balance_ok)
|
||||
{
|
||||
struct TALER_EXCHANGEDB_ReserveHistory *rh;
|
||||
struct TALER_Amount balance;
|
||||
|
||||
TEH_plugin->rollback (TEH_plugin->cls);
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->start (TEH_plugin->cls,
|
||||
"get_reserve_history on insufficient balance"))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
if (NULL != mhd_ret)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_START_FAILED,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
/* The reserve does not have the required amount (actual
|
||||
* amount + withdraw fee) */
|
||||
#if GNUNET_EXTRA_LOGGING
|
||||
{
|
||||
char *amount_required;
|
||||
char *r_balance;
|
||||
|
||||
amount_required = TALER_amount_to_string (&wc->amount_required);
|
||||
r_balance = TALER_amount_to_string (&r.balance);
|
||||
TALER_LOG_DEBUG ("Asked %s over a reserve worth %s\n",
|
||||
amount_required,
|
||||
r_balance);
|
||||
GNUNET_free (amount_required);
|
||||
GNUNET_free (r_balance);
|
||||
}
|
||||
#endif
|
||||
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||
&wc->wsrd.reserve_pub,
|
||||
&balance,
|
||||
&rh);
|
||||
if (NULL == rh)
|
||||
{
|
||||
@ -284,41 +206,41 @@ withdraw_transaction (void *cls,
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
*mhd_ret = reply_withdraw_insufficient_funds (connection,
|
||||
&r.balance,
|
||||
&balance,
|
||||
rh);
|
||||
TEH_plugin->free_reserve_history (TEH_plugin->cls,
|
||||
rh);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC status is %s for %s\n",
|
||||
wc->kyc.ok ? "ok" : "missing",
|
||||
TALER_B2S (&r.pub));
|
||||
if ( (! wc->kyc.ok) &&
|
||||
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
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 */
|
||||
wc->kyc_denied = true;
|
||||
return qs;
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if ( (! wc->kyc.ok) &&
|
||||
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
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 */
|
||||
struct TALER_Amount acc;
|
||||
enum GNUNET_DB_QueryStatus qs2;
|
||||
bool below_limit;
|
||||
|
||||
acc = wc->amount_required;
|
||||
qs2 = TEH_plugin->select_withdraw_amounts_by_account (
|
||||
qs2 = TEH_plugin->do_withdraw_limit_check (
|
||||
TEH_plugin->cls,
|
||||
&wc->wsrd.reserve_pub,
|
||||
TEH_kyc_config.withdraw_period,
|
||||
&accumulate_withdraws,
|
||||
&acc);
|
||||
reserve_uuid,
|
||||
GNUNET_TIME_absolute_subtract (now,
|
||||
TEH_kyc_config.withdraw_period),
|
||||
&TEH_kyc_config.withdraw_limit,
|
||||
&below_limit);
|
||||
if (0 > qs2)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
|
||||
@ -326,52 +248,18 @@ withdraw_transaction (void *cls,
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"withdraw details");
|
||||
"do_withdraw_limit_check");
|
||||
return qs2;
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_is_valid (&acc))
|
||||
if (! below_limit)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
*mhd_ret = TALER_MHD_reply_with_ec (connection,
|
||||
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
|
||||
NULL);
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Amount withdrawn so far is %s\n",
|
||||
TALER_amount2s (&acc));
|
||||
if (1 == /* 1: acc > withdraw_limit */
|
||||
TALER_amount_cmp (&acc,
|
||||
&TEH_kyc_config.withdraw_limit))
|
||||
{
|
||||
wc->kyc_denied = true;
|
||||
return qs;
|
||||
}
|
||||
}
|
||||
|
||||
/* Balance is good, persist signature */
|
||||
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
|
||||
wc->collectable.amount_with_fee = wc->amount_required;
|
||||
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
|
||||
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
||||
wc->collectable.reserve_sig = wc->signature;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Persisting withdraw from %s over %s\n",
|
||||
TALER_B2S (&r.pub),
|
||||
TALER_amount2s (&wc->amount_required));
|
||||
qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
|
||||
&wc->collectable);
|
||||
if (0 > qs)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
||||
"withdraw details");
|
||||
return qs;
|
||||
}
|
||||
return qs;
|
||||
}
|
||||
@ -432,9 +320,9 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
(void **) &wc.blinded_msg,
|
||||
&wc.blinded_msg_len),
|
||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||
&wc.signature),
|
||||
&wc.collectable.reserve_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
||||
&wc.denom_pub_hash),
|
||||
&wc.collectable.denom_pub_hash),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
enum TALER_ErrorCode ec;
|
||||
@ -487,7 +375,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
return mret;
|
||||
}
|
||||
dk = TEH_keys_denomination_by_hash2 (ksh,
|
||||
&wc.denom_pub_hash,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
NULL,
|
||||
NULL);
|
||||
if (NULL == dk)
|
||||
@ -497,8 +385,9 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
&mret))
|
||||
{
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_unknown_denom_pub_hash (rc->connection,
|
||||
&wc.denom_pub_hash);
|
||||
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.collectable.denom_pub_hash);
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return mret;
|
||||
@ -519,7 +408,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.denom_pub_hash,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
||||
"WITHDRAW");
|
||||
@ -538,7 +427,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.denom_pub_hash,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
|
||||
"WITHDRAW");
|
||||
@ -557,7 +446,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.denom_pub_hash,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
|
||||
"WITHDRAW");
|
||||
@ -569,7 +458,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
|
||||
{
|
||||
if (0 >
|
||||
TALER_amount_add (&wc.amount_required,
|
||||
TALER_amount_add (&wc.collectable.amount_with_fee,
|
||||
&dk->meta.value,
|
||||
&dk->meta.fee_withdraw))
|
||||
{
|
||||
@ -580,7 +469,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
NULL);
|
||||
}
|
||||
TALER_amount_hton (&wc.wsrd.amount_with_fee,
|
||||
&wc.amount_required);
|
||||
&wc.collectable.amount_with_fee);
|
||||
}
|
||||
|
||||
/* verify signature! */
|
||||
@ -589,15 +478,16 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
wc.wsrd.purpose.purpose
|
||||
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
||||
wc.wsrd.h_denomination_pub
|
||||
= wc.denom_pub_hash;
|
||||
= wc.collectable.denom_pub_hash;
|
||||
TALER_coin_ev_hash (wc.blinded_msg,
|
||||
wc.blinded_msg_len,
|
||||
&wc.wsrd.h_coin_envelope);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
||||
&wc.wsrd,
|
||||
&wc.signature.eddsa_signature,
|
||||
&wc.wsrd.reserve_pub.eddsa_pub))
|
||||
GNUNET_CRYPTO_eddsa_verify (
|
||||
TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
||||
&wc.wsrd,
|
||||
&wc.collectable.reserve_sig.eddsa_signature,
|
||||
&wc.wsrd.reserve_pub.eddsa_pub))
|
||||
{
|
||||
TALER_LOG_WARNING (
|
||||
"Client supplied invalid signature for withdraw request\n");
|
||||
@ -611,7 +501,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
/* Sign before transaction! */
|
||||
ec = TALER_EC_NONE;
|
||||
wc.collectable.sig
|
||||
= TEH_keys_denomination_sign (&wc.denom_pub_hash,
|
||||
= TEH_keys_denomination_sign (&wc.collectable.denom_pub_hash,
|
||||
wc.blinded_msg,
|
||||
wc.blinded_msg_len,
|
||||
&ec);
|
||||
@ -625,7 +515,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
}
|
||||
|
||||
/* run transaction and sign (if not optimistically signed before) */
|
||||
wc.kyc_denied = false;
|
||||
{
|
||||
MHD_RESULT mhd_ret;
|
||||
|
||||
@ -647,16 +536,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
/* Clean up and send back final response */
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
|
||||
if (wc.kyc_denied)
|
||||
{
|
||||
TALER_blinded_denom_sig_free (&wc.collectable.sig);
|
||||
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;
|
||||
|
||||
|
1
src/exchangedb/.gitignore
vendored
1
src/exchangedb/.gitignore
vendored
@ -4,3 +4,4 @@ test-exchangedb-fees
|
||||
test-exchangedb-postgres
|
||||
test-exchangedb-signkeys
|
||||
test-perf-taler-exchangedb
|
||||
bench-db-postgres
|
||||
|
@ -54,6 +54,10 @@ DROP TABLE IF EXISTS reserves CASCADE;
|
||||
DROP TABLE IF EXISTS denomination_revocations CASCADE;
|
||||
DROP TABLE IF EXISTS denominations CASCADE;
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw(bigint,integer,bytea,bytea,bytea,bytea,bytea,bigint,bigint) ;
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw_limit_check(bigint,bigint,bigint,int) ;
|
||||
|
||||
|
||||
-- And we're out of here...
|
||||
COMMIT;
|
||||
|
@ -680,6 +680,211 @@ CREATE INDEX IF NOT EXISTS revolving_work_shards_index
|
||||
);
|
||||
|
||||
|
||||
-- Stored procedures
|
||||
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw(bigint,integer,bytea,bytea,bytea,bytea,bytea,bigint,bigint) ;
|
||||
|
||||
CREATE OR REPLACE FUNCTION exchange_do_withdraw(
|
||||
IN amount_val INT8,
|
||||
IN amount_frac INT4,
|
||||
IN h_denom_pub BYTEA,
|
||||
IN rpub BYTEA,
|
||||
IN reserve_sig BYTEA,
|
||||
IN h_coin_envelope BYTEA,
|
||||
IN denom_sig BYTEA,
|
||||
IN now INT8,
|
||||
IN min_reserve_gc INT8,
|
||||
OUT reserve_found BOOLEAN,
|
||||
OUT balance_ok BOOLEAN,
|
||||
OUT kycok BOOLEAN,
|
||||
OUT ruuid INT8,
|
||||
OUT account_uuid INT8)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
reserve_gc INT8;
|
||||
DECLARE
|
||||
denom_serial INT8;
|
||||
DECLARE
|
||||
reserve_val INT8;
|
||||
DECLARE
|
||||
reserve_frac INT4;
|
||||
BEGIN
|
||||
|
||||
SELECT denominations_serial INTO denom_serial
|
||||
FROM denominations
|
||||
WHERE denom_pub_hash=h_denom_pub;
|
||||
|
||||
IF NOT FOUND
|
||||
THEN
|
||||
-- denomination unknown, should be impossible!
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE;
|
||||
ruuid=0;
|
||||
account_uuid=0;
|
||||
ASSERT false, 'denomination unknown';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
SELECT
|
||||
reserves.reserve_uuid
|
||||
,current_balance_val
|
||||
,current_balance_frac
|
||||
,expiration_date
|
||||
,gc_date
|
||||
INTO
|
||||
ruuid
|
||||
,reserve_val
|
||||
,reserve_frac
|
||||
,reserve_gc
|
||||
FROM reserves
|
||||
WHERE reserves.reserve_pub=rpub;
|
||||
|
||||
IF NOT FOUND
|
||||
THEN
|
||||
-- reserve unknown
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE;
|
||||
account_uuid=0;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- We optimistically insert, and then on conflict declare
|
||||
-- the query successful due to idempotency.
|
||||
INSERT INTO reserves_out
|
||||
(h_blind_ev
|
||||
,denominations_serial
|
||||
,denom_sig
|
||||
,reserve_uuid
|
||||
,reserve_sig
|
||||
,execution_date
|
||||
,amount_with_fee_val
|
||||
,amount_with_fee_frac)
|
||||
VALUES
|
||||
(h_coin_envelope
|
||||
,denom_serial
|
||||
,denom_sig
|
||||
,ruuid
|
||||
,reserve_sig
|
||||
,now
|
||||
,amount_val
|
||||
,amount_frac)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
IF NOT FOUND
|
||||
THEN
|
||||
-- idempotent query, all constraints must be satisfied
|
||||
reserve_found=TRUE;
|
||||
balance_ok=TRUE;
|
||||
kycok=TRUE;
|
||||
account_uuid=0;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- Check reserve balance is sufficient.
|
||||
IF (reserve_val > amount_val)
|
||||
THEN
|
||||
IF (reserve_frac > amount_frac)
|
||||
THEN
|
||||
reserve_val=reserve_val - amount_val;
|
||||
reserve_frac=reserve_frac - amount_frac;
|
||||
ELSE
|
||||
reserve_val=reserve_val - amount_val - 1;
|
||||
reserve_frac=reserve_frac + 100000000 - amount_frac;
|
||||
END IF;
|
||||
ELSE
|
||||
IF (reserve_val = amount_val) AND (reserve_frac >= amount_frac)
|
||||
THEN
|
||||
reserve_val=0;
|
||||
reserve_frac=reserve_frac - amount_frac;
|
||||
ELSE
|
||||
reserve_found=TRUE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE; -- we do not really know or care
|
||||
account_uuid=0;
|
||||
RETURN;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Calculate new expiration dates.
|
||||
min_reserve_gc=GREATEST(min_reserve_gc,reserve_gc);
|
||||
|
||||
-- Update reserve balance.
|
||||
UPDATE reserves SET
|
||||
gc_date=min_reserve_gc
|
||||
,current_balance_val=reserve_val
|
||||
,current_balance_frac=reserve_frac
|
||||
WHERE
|
||||
reserves.reserve_uuid=ruuid;
|
||||
|
||||
reserve_found=TRUE;
|
||||
balance_ok=TRUE;
|
||||
|
||||
-- Obtain KYC status based on the last wire transfer into
|
||||
-- this reserve. FIXME: likely not adequate for reserves that got P2P transfers!
|
||||
SELECT
|
||||
kyc_ok
|
||||
,wire_source_serial_id
|
||||
INTO
|
||||
kycok
|
||||
,account_uuid
|
||||
FROM reserves_in
|
||||
JOIN wire_targets ON (wire_source_serial_id = wire_target_serial_id)
|
||||
WHERE reserve_uuid=ruuid
|
||||
LIMIT 1; -- limit 1 should not be required (without p2p transfers)
|
||||
|
||||
END $$;
|
||||
|
||||
COMMENT ON FUNCTION exchange_do_withdraw(INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
|
||||
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result';
|
||||
|
||||
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw_limit_check(bigint,bigint,bigint,int) ;
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION exchange_do_withdraw_limit_check(
|
||||
IN ruuid INT8,
|
||||
IN start_time INT8,
|
||||
IN upper_limit_val INT8,
|
||||
IN upper_limit_frac INT4,
|
||||
OUT below_limit BOOLEAN)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
total_val INT8;
|
||||
DECLARE
|
||||
total_frac INT8; -- INT4 could overflow during accumulation!
|
||||
BEGIN
|
||||
|
||||
SELECT
|
||||
SUM(amount_with_fee_val) -- overflow here is not plausible
|
||||
,SUM(CAST(amount_with_fee_frac AS INT8)) -- compute using 64 bits
|
||||
INTO
|
||||
total_val
|
||||
,total_frac
|
||||
FROM reserves_out
|
||||
WHERE reserves_out.reserve_uuid=ruuid
|
||||
AND execution_date > start_time;
|
||||
|
||||
-- normalize result
|
||||
total_val = total_val + total_frac / 100000000;
|
||||
total_frac = total_frac % 100000000;
|
||||
|
||||
-- compare to threshold
|
||||
below_limit = (total_val < upper_limit_val) OR
|
||||
( (total_val = upper_limit_val) AND
|
||||
(total_frac <= upper_limit_frac) );
|
||||
END $$;
|
||||
|
||||
COMMENT ON FUNCTION exchange_do_withdraw_limit_check(INT8, INT8, INT8, INT4)
|
||||
IS 'Check whether the withdrawals from the given reserve since the given time are below the given threshold';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Complete transaction
|
||||
|
@ -596,6 +596,34 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
"lock_withdraw",
|
||||
"LOCK TABLE reserves_out;",
|
||||
0),
|
||||
/* Used in #postgres_do_withdraw() to store
|
||||
the signature of a blinded coin with the blinded coin's
|
||||
details before returning it during /reserve/withdraw. We store
|
||||
the coin's denomination information (public key, signature)
|
||||
and the blinded message as well as the reserve that the coin
|
||||
is being withdrawn from and the signature of the message
|
||||
authorizing the withdrawal. */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"call_withdraw",
|
||||
"SELECT "
|
||||
" reserve_found"
|
||||
",balance_ok"
|
||||
",kycok AS kyc_ok"
|
||||
",ruuid AS reserve_uuid"
|
||||
",account_uuid AS payment_target_uuid"
|
||||
" FROM exchange_do_withdraw"
|
||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
|
||||
9),
|
||||
/* Used in #postgres_do_withdraw_limit_check() to check
|
||||
if the withdrawals remain below the limit under which
|
||||
KYC is not required. */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"call_withdraw_limit_check",
|
||||
"SELECT "
|
||||
" below_limit"
|
||||
" FROM exchange_do_withdraw_limit_check"
|
||||
" ($1,$2,$3,$4);",
|
||||
4),
|
||||
/* Used in #postgres_insert_withdraw_info() to store
|
||||
the signature of a blinded coin with the blinded coin's
|
||||
details before returning it during /reserve/withdraw. We store
|
||||
@ -3378,12 +3406,12 @@ dominations_cb_helper (void *cls,
|
||||
struct TALER_DenominationPublicKey denom_pub;
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
struct TALER_DenominationHash h_denom_pub;
|
||||
uint8_t revoked;
|
||||
bool revoked;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
|
||||
&master_sig),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("revoked",
|
||||
&revoked),
|
||||
GNUNET_PQ_result_spec_bool ("revoked",
|
||||
&revoked),
|
||||
TALER_PQ_result_spec_absolute_time ("valid_from",
|
||||
&meta.start),
|
||||
TALER_PQ_result_spec_absolute_time ("expire_withdraw",
|
||||
@ -3422,7 +3450,7 @@ dominations_cb_helper (void *cls,
|
||||
&h_denom_pub,
|
||||
&meta,
|
||||
&master_sig,
|
||||
(0 != revoked));
|
||||
revoked);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
}
|
||||
}
|
||||
@ -3777,7 +3805,6 @@ postgres_reserves_get (void *cls,
|
||||
GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
|
||||
&reserve->balance),
|
||||
@ -3787,19 +3814,16 @@ postgres_reserves_get (void *cls,
|
||||
&reserve->gc),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"reserves_get_with_kyc",
|
||||
params,
|
||||
rs);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
kyc->ok = (0 != ok8);
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"reserves_get_with_kyc",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -3874,23 +3898,19 @@ postgres_get_kyc_status (void *cls,
|
||||
GNUNET_PQ_query_param_string (payto_uri),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||
kyc->ok = (0 != ok8);
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -3914,24 +3934,20 @@ postgres_select_kyc_status (void *cls,
|
||||
GNUNET_PQ_query_param_uint64 (&payment_target_uuid),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("h_payto",
|
||||
h_payto),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"select_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_UNKNOWN;
|
||||
kyc->ok = (0 != ok8);
|
||||
kyc->payment_target_uuid = payment_target_uuid;
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"select_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -3962,12 +3978,11 @@ inselect_account_kyc_status (
|
||||
GNUNET_PQ_query_param_auto_from_type (&h_payto),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8 = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("wire_target_serial_id",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
@ -3998,10 +4013,6 @@ inselect_account_kyc_status (
|
||||
return GNUNET_DB_STATUS_SOFT_ERROR;
|
||||
kyc->ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
kyc->ok = (0 != ok8);
|
||||
}
|
||||
}
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_BALANCE;
|
||||
return qs;
|
||||
@ -4478,86 +4489,104 @@ postgres_get_withdraw_info (
|
||||
|
||||
|
||||
/**
|
||||
* Store collectable bit coin under the corresponding
|
||||
* hash of the blinded message.
|
||||
* Perform withdraw operation, checking for sufficient balance
|
||||
* and possibly persisting the withdrawal details.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param collectable corresponding collectable coin (blind signature)
|
||||
* if a coin is found
|
||||
* @param now current time (rounded)
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] kyc_ok set to true if the kyc status of the reserve is satisfied
|
||||
* @param[out] reserve_uuid set to the UUID of the reserve
|
||||
* @return query execution status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_insert_withdraw_info (
|
||||
postgres_do_withdraw (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Absolute now,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc,
|
||||
uint64_t *reserve_uuid)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct TALER_EXCHANGEDB_Reserve reserve;
|
||||
struct GNUNET_TIME_Absolute now;
|
||||
struct GNUNET_TIME_Absolute gc;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
|
||||
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
|
||||
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
|
||||
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
||||
TALER_PQ_query_param_absolute_time (&now),
|
||||
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
|
||||
TALER_PQ_query_param_absolute_time (&gc),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_bool ("reserve_found",
|
||||
found),
|
||||
GNUNET_PQ_result_spec_bool ("balance_ok",
|
||||
balance_ok),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
|
||||
reserve_uuid),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(void) GNUNET_TIME_round_abs (&now);
|
||||
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"insert_withdraw_info",
|
||||
params);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
return qs;
|
||||
}
|
||||
|
||||
/* update reserve balance */
|
||||
reserve.pub = collectable->reserve_pub;
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
(qs = reserves_get_internal (pg,
|
||||
&reserve)))
|
||||
{
|
||||
/* Should have been checked before we got here... */
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||
return qs;
|
||||
}
|
||||
if (0 >
|
||||
TALER_amount_subtract (&reserve.balance,
|
||||
&reserve.balance,
|
||||
&collectable->amount_with_fee))
|
||||
{
|
||||
/* The reserve history was checked to make sure there is enough of a balance
|
||||
left before we tried this; however, concurrent operations may have changed
|
||||
the situation by now, causing us to fail here. As reserves can no longer
|
||||
be topped up, retrying should not help either. */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Withdrawal from reserve `%s' refused due to insufficient balance.\n",
|
||||
TALER_B2S (&collectable->reserve_pub));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
gc = GNUNET_TIME_absolute_add (now,
|
||||
pg->legal_reserve_expiration_time);
|
||||
reserve.gc = GNUNET_TIME_absolute_max (gc,
|
||||
reserve.gc);
|
||||
(void) GNUNET_TIME_round_abs (&reserve.gc);
|
||||
qs = reserves_update (pg,
|
||||
&reserve);
|
||||
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
return qs;
|
||||
(void) GNUNET_TIME_round_abs (&gc);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_withdraw",
|
||||
params,
|
||||
rs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that reserve remains below threshold for KYC
|
||||
* checks after withdraw operation.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_uuid reserve to check
|
||||
* @param withdraw_start starting point to accumulate from
|
||||
* @param upper_limit maximum amount allowed
|
||||
* @param[out] below_limit set to true if the limit was not exceeded
|
||||
* @return query execution status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_do_withdraw_limit_check (
|
||||
void *cls,
|
||||
uint64_t reserve_uuid,
|
||||
struct GNUNET_TIME_Absolute withdraw_start,
|
||||
const struct TALER_Amount *upper_limit,
|
||||
bool *below_limit)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_uint64 (&reserve_uuid),
|
||||
TALER_PQ_query_param_absolute_time (&withdraw_start),
|
||||
TALER_PQ_query_param_amount (upper_limit),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_bool ("below_limit",
|
||||
below_limit),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_withdraw_limit_check",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -4587,11 +4616,21 @@ struct ReserveHistoryContext
|
||||
*/
|
||||
struct PostgresClosure *pg;
|
||||
|
||||
/**
|
||||
* Sum of all credit transactions.
|
||||
*/
|
||||
struct TALER_Amount balance_in;
|
||||
|
||||
/**
|
||||
* Sum of all debit transactions.
|
||||
*/
|
||||
struct TALER_Amount balance_out;
|
||||
|
||||
/**
|
||||
* Set to #GNUNET_SYSERR on serious internal errors during
|
||||
* the callbacks.
|
||||
*/
|
||||
int status;
|
||||
enum GNUNET_GenericReturnValue status;
|
||||
};
|
||||
|
||||
|
||||
@ -4667,6 +4706,10 @@ add_bank_to_exchange (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_in,
|
||||
&rhc->balance_in,
|
||||
&bt->amount));
|
||||
bt->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
|
||||
@ -4724,6 +4767,10 @@ add_withdraw_coin (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_out,
|
||||
&rhc->balance_out,
|
||||
&cbc->amount_with_fee));
|
||||
cbc->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
|
||||
@ -4784,6 +4831,10 @@ add_recoup (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_in,
|
||||
&rhc->balance_in,
|
||||
&recoup->value));
|
||||
recoup->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
|
||||
@ -4840,6 +4891,10 @@ add_exchange_to_bank (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_out,
|
||||
&rhc->balance_out,
|
||||
&closing->amount));
|
||||
closing->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
|
||||
@ -4854,12 +4909,14 @@ add_exchange_to_bank (void *cls,
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param[out] balance set to the reserve balance
|
||||
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_get_reserve_history (void *cls,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
struct TALER_Amount *balance,
|
||||
struct TALER_EXCHANGEDB_ReserveHistory **rhp)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
@ -4902,6 +4959,12 @@ postgres_get_reserve_history (void *cls,
|
||||
rhc.rh_tail = NULL;
|
||||
rhc.pg = pg;
|
||||
rhc.status = GNUNET_OK;
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (pg->currency,
|
||||
&rhc.balance_in));
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (pg->currency,
|
||||
&rhc.balance_out));
|
||||
qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
|
||||
for (unsigned int i = 0; NULL != work[i].cb; i++)
|
||||
{
|
||||
@ -4927,6 +4990,10 @@ postgres_get_reserve_history (void *cls,
|
||||
}
|
||||
}
|
||||
*rhp = rhc.rh;
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_subtract (balance,
|
||||
&rhc.balance_in,
|
||||
&rhc.balance_out));
|
||||
return qs;
|
||||
}
|
||||
|
||||
@ -5308,13 +5375,12 @@ postgres_get_ready_deposit (void *cls,
|
||||
void *deposit_cb_cls)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
uint8_t kyc_override = (kyc_off) ? 1 : 0;
|
||||
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
TALER_PQ_query_param_absolute_time (&now),
|
||||
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
||||
GNUNET_PQ_query_param_uint64 (&end_shard_row),
|
||||
GNUNET_PQ_query_param_auto_from_type (&kyc_override),
|
||||
GNUNET_PQ_query_param_bool (kyc_off),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct TALER_Amount amount_with_fee;
|
||||
@ -6609,7 +6675,6 @@ add_coin_deposit (void *cls,
|
||||
chc->have_deposit_or_melt = true;
|
||||
deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
|
||||
{
|
||||
uint8_t done = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
&deposit->amount_with_fee),
|
||||
@ -6636,7 +6701,7 @@ add_coin_deposit (void *cls,
|
||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||
&serial_id),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||
&done),
|
||||
&deposit->done),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
@ -6650,7 +6715,6 @@ add_coin_deposit (void *cls,
|
||||
chc->failed = true;
|
||||
return;
|
||||
}
|
||||
deposit->done = (0 != done);
|
||||
}
|
||||
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
||||
tl->next = chc->head;
|
||||
@ -7340,7 +7404,6 @@ postgres_lookup_transfer_by_deposit (
|
||||
/* Check if transaction exists in deposits, so that we just
|
||||
do not have a WTID yet. In that case, return without wtid
|
||||
(by setting 'pending' true). */
|
||||
uint8_t ok8 = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs2[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
||||
&wire_salt),
|
||||
@ -7349,7 +7412,7 @@ postgres_lookup_transfer_by_deposit (
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
&kyc->ok),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
amount_with_fee),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
||||
@ -7377,7 +7440,6 @@ postgres_lookup_transfer_by_deposit (
|
||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||
}
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||
kyc->ok = (0 != ok8);
|
||||
return qs;
|
||||
}
|
||||
}
|
||||
@ -8164,7 +8226,7 @@ deposit_serial_helper_cb (void *cls,
|
||||
struct TALER_EXCHANGEDB_Deposit deposit;
|
||||
struct GNUNET_TIME_Absolute exchange_timestamp;
|
||||
struct TALER_DenominationPublicKey denom_pub;
|
||||
uint8_t done = 0;
|
||||
bool done;
|
||||
uint64_t rowid;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
@ -8191,8 +8253,8 @@ deposit_serial_helper_cb (void *cls,
|
||||
&deposit.wire_salt),
|
||||
GNUNET_PQ_result_spec_string ("receiver_wire_account",
|
||||
&deposit.receiver_wire_account),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_bool ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||
&rowid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -8213,7 +8275,7 @@ deposit_serial_helper_cb (void *cls,
|
||||
exchange_timestamp,
|
||||
&deposit,
|
||||
&denom_pub,
|
||||
(0 != done) ? true : false);
|
||||
done);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
if (GNUNET_OK != ret)
|
||||
break;
|
||||
@ -9783,8 +9845,8 @@ missing_wire_cb (void *cls,
|
||||
struct TALER_Amount amount;
|
||||
char *payto_uri;
|
||||
struct GNUNET_TIME_Absolute deadline;
|
||||
uint8_t tiny;
|
||||
uint8_t done;
|
||||
bool tiny;
|
||||
bool done;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||
&rowid),
|
||||
@ -9796,10 +9858,10 @@ missing_wire_cb (void *cls,
|
||||
&payto_uri),
|
||||
TALER_PQ_result_spec_absolute_time ("wire_deadline",
|
||||
&deadline),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("tiny",
|
||||
&tiny),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_bool ("tiny",
|
||||
&tiny),
|
||||
GNUNET_PQ_result_spec_bool ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
@ -9923,22 +9985,18 @@ postgres_lookup_auditor_status (
|
||||
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t enabled8 = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_string ("auditor_url",
|
||||
auditor_url),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("is_active",
|
||||
&enabled8),
|
||||
GNUNET_PQ_result_spec_bool ("is_active",
|
||||
enabled),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"lookup_auditor_status",
|
||||
params,
|
||||
rs);
|
||||
*enabled = (0 != enabled8);
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"lookup_auditor_status",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -9996,12 +10054,11 @@ postgres_update_auditor (void *cls,
|
||||
bool enabled)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
uint8_t enabled8 = enabled ? 1 : 0;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
||||
GNUNET_PQ_query_param_string (auditor_url),
|
||||
GNUNET_PQ_query_param_string (auditor_name),
|
||||
GNUNET_PQ_query_param_auto_from_type (&enabled8),
|
||||
GNUNET_PQ_query_param_bool (enabled),
|
||||
GNUNET_PQ_query_param_absolute_time (&change_date),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
@ -10091,10 +10148,9 @@ postgres_update_wire (void *cls,
|
||||
bool enabled)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
uint8_t enabled8 = enabled ? 1 : 0;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_string (payto_uri),
|
||||
GNUNET_PQ_query_param_auto_from_type (&enabled8),
|
||||
GNUNET_PQ_query_param_bool (enabled),
|
||||
GNUNET_PQ_query_param_absolute_time (&change_date),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
@ -11767,7 +11823,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
||||
plugin->get_latest_reserve_in_reference =
|
||||
&postgres_get_latest_reserve_in_reference;
|
||||
plugin->get_withdraw_info = &postgres_get_withdraw_info;
|
||||
plugin->insert_withdraw_info = &postgres_insert_withdraw_info;
|
||||
// plugin->insert_withdraw_info = &postgres_insert_withdraw_info;
|
||||
plugin->do_withdraw = &postgres_do_withdraw;
|
||||
plugin->do_withdraw_limit_check = &postgres_do_withdraw_limit_check;
|
||||
plugin->get_reserve_history = &postgres_get_reserve_history;
|
||||
plugin->select_withdraw_amounts_by_account
|
||||
= &postgres_select_withdraw_amounts_by_account;
|
||||
|
@ -1659,10 +1659,26 @@ run (void *cls)
|
||||
cbc.reserve_pub = reserve_pub;
|
||||
cbc.amount_with_fee = value;
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (CURRENCY, &cbc.withdraw_fee));
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->insert_withdraw_info (plugin->cls,
|
||||
&cbc));
|
||||
TALER_amount_set_zero (CURRENCY,
|
||||
&cbc.withdraw_fee));
|
||||
{
|
||||
bool found;
|
||||
bool balance_ok;
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
uint64_t ruuid;
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->do_withdraw (plugin->cls,
|
||||
&cbc,
|
||||
now,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&kyc,
|
||||
&ruuid));
|
||||
GNUNET_assert (found);
|
||||
GNUNET_assert (balance_ok);
|
||||
GNUNET_assert (! kyc.ok);
|
||||
}
|
||||
FAILIF (GNUNET_OK !=
|
||||
check_reserve (&reserve_pub,
|
||||
value.value,
|
||||
|
@ -496,7 +496,7 @@ struct TALER_EXCHANGEDB_ClosingTransfer
|
||||
struct TALER_ReservePublicKeyP reserve_pub;
|
||||
|
||||
/**
|
||||
* Amount that was transferred to the exchange.
|
||||
* Amount that was transferred from the exchange.
|
||||
*/
|
||||
struct TALER_Amount amount;
|
||||
|
||||
@ -2512,23 +2512,70 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @return statement execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*insert_withdraw_info)(
|
||||
(*insert_withdraw_infoXX)(
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
|
||||
|
||||
|
||||
/**
|
||||
* Perform withdraw operation, checking for sufficient balance
|
||||
* and possibly persisting the withdrawal details.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param collectable corresponding collectable coin (blind signature)
|
||||
* if a coin is found
|
||||
* @param now current time (rounded)
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] kyc set to the KYC status of the reserve
|
||||
* @param[out] reserve_uuid set to the UUID of the reserve
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*do_withdraw)(
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Absolute now,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc_ok,
|
||||
uint64_t *reserve_uuid);
|
||||
|
||||
|
||||
/**
|
||||
* Check that reserve remains below threshold for KYC
|
||||
* checks after withdraw operation.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_uuid reserve to check
|
||||
* @param withdraw_start starting point to accumulate from
|
||||
* @param upper_limit maximum amount allowed
|
||||
* @param[out] below_limit set to true if the limit was not exceeded
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*do_withdraw_limit_check)(
|
||||
void *cls,
|
||||
uint64_t reserve_uuid,
|
||||
struct GNUNET_TIME_Absolute withdraw_start,
|
||||
const struct TALER_Amount *upper_limit,
|
||||
bool *below_limit);
|
||||
|
||||
|
||||
/**
|
||||
* Get all of the transaction history associated with the specified
|
||||
* reserve.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param[out] balance set to the reserve balance
|
||||
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
|
||||
* @return transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*get_reserve_history)(void *cls,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
struct TALER_Amount *balance,
|
||||
struct TALER_EXCHANGEDB_ReserveHistory **rhp);
|
||||
|
||||
|
||||
|
@ -78,7 +78,7 @@ TES_transmit_raw (int sock,
|
||||
size_t end,
|
||||
const void *pos)
|
||||
{
|
||||
ssize_t off = 0;
|
||||
size_t off = 0;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Sending message of length %u\n",
|
||||
|
Loading…
Reference in New Issue
Block a user