Merge branch 'master' into age_restriction
This commit is contained in:
commit
83ce1b7c3d
@ -95,6 +95,9 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
|
|||||||
NULL);
|
NULL);
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
// FIXME: why do we even return the transaction
|
||||||
|
// history here!? This is a coin with multiple
|
||||||
|
// associated denominations, after all...
|
||||||
*mhd_ret
|
*mhd_ret
|
||||||
= TEH_RESPONSE_reply_coin_insufficient_funds (
|
= TEH_RESPONSE_reply_coin_insufficient_funds (
|
||||||
connection,
|
connection,
|
||||||
|
@ -139,11 +139,12 @@ struct DepositContext
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if /deposit is already in the database. IF it returns a non-error
|
* Execute database transaction for /deposit. Runs the transaction
|
||||||
* code, the transaction logic MUST NOT queue a MHD response. IF it returns
|
* logic; IF it returns a non-error code, the transaction logic MUST
|
||||||
* an hard error, the transaction logic MUST queue a MHD response and set @a
|
* NOT queue a MHD response. IF it returns an hard error, the
|
||||||
* mhd_ret. We do return a "hard" error also if we found the deposit in the
|
* transaction logic MUST queue a MHD response and set @a mhd_ret. IF
|
||||||
* database and generated a regular response.
|
* it returns the soft error code, the function MAY be called again to
|
||||||
|
* retry and MUST not queue a MHD response.
|
||||||
*
|
*
|
||||||
* @param cls a `struct DepositContext`
|
* @param cls a `struct DepositContext`
|
||||||
* @param connection MHD request context
|
* @param connection MHD request context
|
||||||
@ -151,15 +152,24 @@ struct DepositContext
|
|||||||
* @return transaction status
|
* @return transaction status
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_DB_QueryStatus
|
static enum GNUNET_DB_QueryStatus
|
||||||
deposit_precheck (void *cls,
|
deposit_transaction (void *cls,
|
||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
MHD_RESULT *mhd_ret)
|
MHD_RESULT *mhd_ret)
|
||||||
{
|
{
|
||||||
struct DepositContext *dc = cls;
|
struct DepositContext *dc = cls;
|
||||||
const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
|
const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
|
||||||
struct TALER_Amount deposit_fee;
|
struct TALER_Amount spent;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
struct TALER_Amount deposit_fee;
|
||||||
|
|
||||||
|
/* make sure coin is 'known' in database */
|
||||||
|
qs = TEH_make_coin_known (&deposit->coin,
|
||||||
|
connection,
|
||||||
|
mhd_ret);
|
||||||
|
if (qs < 0)
|
||||||
|
return qs;
|
||||||
|
|
||||||
|
/* Check for idempotency: did we get this request before? */
|
||||||
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
|
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
|
||||||
deposit,
|
deposit,
|
||||||
&deposit_fee,
|
&deposit_fee,
|
||||||
@ -196,51 +206,8 @@ deposit_precheck (void *cls,
|
|||||||
deposit->wire_deadline,
|
deposit->wire_deadline,
|
||||||
&deposit->merchant_pub,
|
&deposit->merchant_pub,
|
||||||
&amount_without_fee);
|
&amount_without_fee);
|
||||||
/* Treat as 'hard' DB error as we want to rollback and
|
|
||||||
never try again. */
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute database transaction for /deposit. 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, the
|
|
||||||
* transaction logic MUST queue a MHD response and set @a mhd_ret. IF
|
|
||||||
* it returns the soft error code, the function MAY be called again to
|
|
||||||
* retry and MUST not queue a MHD response.
|
|
||||||
*
|
|
||||||
* @param cls a `struct DepositContext`
|
|
||||||
* @param connection MHD request context
|
|
||||||
* @param[out] mhd_ret set to MHD status on error
|
|
||||||
* @return transaction status
|
|
||||||
*/
|
|
||||||
static enum GNUNET_DB_QueryStatus
|
|
||||||
deposit_transaction (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
MHD_RESULT *mhd_ret)
|
|
||||||
{
|
|
||||||
struct DepositContext *dc = cls;
|
|
||||||
const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
|
|
||||||
struct TALER_Amount spent;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
/* make sure coin is 'known' in database */
|
|
||||||
qs = TEH_make_coin_known (&deposit->coin,
|
|
||||||
connection,
|
|
||||||
mhd_ret);
|
|
||||||
if (qs < 0)
|
|
||||||
return qs;
|
|
||||||
/* Theoretically, someone other threat may have received
|
|
||||||
and committed the deposit in the meantime. Check now
|
|
||||||
that we are in the transaction scope. */
|
|
||||||
qs = deposit_precheck (cls,
|
|
||||||
connection,
|
|
||||||
mhd_ret);
|
|
||||||
if (qs < 0)
|
|
||||||
return qs;
|
|
||||||
|
|
||||||
/* Start with fee for THIS transaction */
|
/* Start with fee for THIS transaction */
|
||||||
spent = deposit->amount_with_fee;
|
spent = deposit->amount_with_fee;
|
||||||
@ -412,22 +379,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
|||||||
TALER_merchant_wire_signature_hash (dc.payto_uri,
|
TALER_merchant_wire_signature_hash (dc.payto_uri,
|
||||||
&deposit.wire_salt,
|
&deposit.wire_salt,
|
||||||
&dc.h_wire);
|
&dc.h_wire);
|
||||||
/* Check for idempotency: did we get this request before? */
|
|
||||||
dc.deposit = &deposit;
|
dc.deposit = &deposit;
|
||||||
{
|
|
||||||
MHD_RESULT mhd_ret;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TEH_DB_run_transaction (connection,
|
|
||||||
"precheck deposit",
|
|
||||||
&mhd_ret,
|
|
||||||
&deposit_precheck,
|
|
||||||
&dc))
|
|
||||||
{
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return mhd_ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* new deposit */
|
/* new deposit */
|
||||||
dc.exchange_timestamp = GNUNET_TIME_absolute_get ();
|
dc.exchange_timestamp = GNUNET_TIME_absolute_get ();
|
||||||
|
@ -2110,6 +2110,8 @@ TEH_keys_denomination_by_hash2 (
|
|||||||
&h_denom_pub->hash);
|
&h_denom_pub->hash);
|
||||||
if (NULL == dk)
|
if (NULL == dk)
|
||||||
{
|
{
|
||||||
|
if (NULL == conn)
|
||||||
|
return NULL;
|
||||||
*mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
|
*mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
|
||||||
h_denom_pub);
|
h_denom_pub);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -311,16 +311,6 @@ melt_transaction (void *cls,
|
|||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
uint32_t noreveal_index;
|
uint32_t noreveal_index;
|
||||||
|
|
||||||
/* First, make sure coin is 'known' in database */
|
|
||||||
if (! rmc->coin_is_dirty)
|
|
||||||
{
|
|
||||||
qs = TEH_make_coin_known (&rmc->refresh_session.coin,
|
|
||||||
connection,
|
|
||||||
mhd_ret);
|
|
||||||
if (qs < 0)
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we already created a matching refresh_session */
|
/* Check if we already created a matching refresh_session */
|
||||||
qs = TEH_plugin->get_melt_index (TEH_plugin->cls,
|
qs = TEH_plugin->get_melt_index (TEH_plugin->cls,
|
||||||
&rmc->refresh_session.rc,
|
&rmc->refresh_session.rc,
|
||||||
@ -420,7 +410,22 @@ handle_melt (struct MHD_Connection *connection,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* run database transaction */
|
/* first, make sure coin is known */
|
||||||
|
if (! rmc->coin_is_dirty)
|
||||||
|
{
|
||||||
|
MHD_RESULT mhd_ret = MHD_NO;
|
||||||
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
|
qs = TEH_make_coin_known (&rmc->refresh_session.coin,
|
||||||
|
connection,
|
||||||
|
&mhd_ret);
|
||||||
|
/* no transaction => no serialization failures should be possible */
|
||||||
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
|
||||||
|
if (qs < 0)
|
||||||
|
return mhd_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* run main database transaction */
|
||||||
{
|
{
|
||||||
MHD_RESULT mhd_ret;
|
MHD_RESULT mhd_ret;
|
||||||
|
|
||||||
@ -513,10 +518,6 @@ check_for_denomination_key (struct MHD_Connection *connection,
|
|||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"coin denomination");
|
"coin denomination");
|
||||||
}
|
}
|
||||||
/* sanity check */
|
|
||||||
GNUNET_break (0 ==
|
|
||||||
GNUNET_memcmp (&denom_hash,
|
|
||||||
&rmc->refresh_session.coin.denom_pub_hash));
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
||||||
{
|
{
|
||||||
struct GNUNET_TIME_Absolute now;
|
struct GNUNET_TIME_Absolute now;
|
||||||
@ -531,11 +532,23 @@ check_for_denomination_key (struct MHD_Connection *connection,
|
|||||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
||||||
"MELT");
|
"MELT");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Minor optimization: no need to run the
|
/* Minor optimization: no need to run the
|
||||||
"ensure_coin_known" part of the transaction */
|
"ensure_coin_known" part of the transaction */
|
||||||
rmc->coin_is_dirty = true;
|
rmc->coin_is_dirty = true;
|
||||||
|
/* sanity check */
|
||||||
|
if (0 !=
|
||||||
|
GNUNET_memcmp (&denom_hash,
|
||||||
|
&rmc->refresh_session.coin.denom_pub_hash))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
// => this is probably the wrong call, as this
|
||||||
|
// is NOT about insufficient funds!
|
||||||
|
// (see also taler-exchange-httpd_db.c for an equivalent issue)
|
||||||
|
return TEH_RESPONSE_reply_coin_insufficient_funds (
|
||||||
|
connection,
|
||||||
|
TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
|
||||||
|
&rmc->refresh_session.coin.coin_pub,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
rmc->zombie_required = true; /* check later that zombie is satisfied */
|
rmc->zombie_required = true; /* check later that zombie is satisfied */
|
||||||
}
|
}
|
||||||
|
@ -235,11 +235,13 @@ reserve_history_transaction (void *cls,
|
|||||||
MHD_RESULT *mhd_ret)
|
MHD_RESULT *mhd_ret)
|
||||||
{
|
{
|
||||||
struct ReserveHistoryContext *rsc = cls;
|
struct ReserveHistoryContext *rsc = cls;
|
||||||
|
struct TALER_Amount balance;
|
||||||
|
|
||||||
(void) connection;
|
(void) connection;
|
||||||
(void) mhd_ret;
|
(void) mhd_ret;
|
||||||
return TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
return TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||||
&rsc->reserve_pub,
|
&rsc->reserve_pub,
|
||||||
|
&balance,
|
||||||
&rsc->rh);
|
&rsc->rh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,14 +33,6 @@
|
|||||||
#include "taler-exchange-httpd_keys.h"
|
#include "taler-exchange-httpd_keys.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform RSA signature before checking with the database?
|
|
||||||
* Reduces time spent in transaction, but may cause us to
|
|
||||||
* waste CPU time if DB check fails.
|
|
||||||
*/
|
|
||||||
#define OPTIMISTIC_SIGN 1
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send reserve history information to client with the
|
* Send reserve history information to client with the
|
||||||
* message that we have insufficient funds for the
|
* message that we have insufficient funds for the
|
||||||
@ -99,21 +91,6 @@ struct WithdrawContext
|
|||||||
*/
|
*/
|
||||||
struct TALER_WithdrawRequestPS wsrd;
|
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.
|
* Blinded planchet.
|
||||||
*/
|
*/
|
||||||
@ -134,39 +111,9 @@ struct WithdrawContext
|
|||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
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
|
* Function implementing withdraw transaction. Runs the
|
||||||
* transaction logic; IF it returns a non-error code, the transaction
|
* transaction logic; IF it returns a non-error code, the transaction
|
||||||
@ -175,15 +122,8 @@ accumulate_withdraws (void *cls,
|
|||||||
* IF it returns the soft error code, the function MAY be called again
|
* IF it returns the soft error code, the function MAY be called again
|
||||||
* to retry and MUST not queue a MHD response.
|
* to retry and MUST not queue a MHD response.
|
||||||
*
|
*
|
||||||
* Note that "wc->collectable.sig" may already be set before entering
|
* Note that "wc->collectable.sig" is set before entering this function as we
|
||||||
* this function, either because OPTIMISTIC_SIGN was used and we signed
|
* signed before entering the transaction.
|
||||||
* before entering the transaction, or because this function is run
|
|
||||||
* twice (!) by #TEH_DB_run_transaction() and the first time created
|
|
||||||
* the signature and then failed to commit. Furthermore, we may get
|
|
||||||
* a 2nd correct signature briefly if "get_withdraw_info" succeeds and
|
|
||||||
* finds one in the DB. To avoid signing twice, the function may
|
|
||||||
* return a valid signature in "wc->collectable.sig" **even if it failed**.
|
|
||||||
* The caller must thus free the signature in either case.
|
|
||||||
*
|
*
|
||||||
* @param cls a `struct WithdrawContext *`
|
* @param cls a `struct WithdrawContext *`
|
||||||
* @param connection MHD request which triggered the transaction
|
* @param connection MHD request which triggered the transaction
|
||||||
@ -197,71 +137,34 @@ withdraw_transaction (void *cls,
|
|||||||
MHD_RESULT *mhd_ret)
|
MHD_RESULT *mhd_ret)
|
||||||
{
|
{
|
||||||
struct WithdrawContext *wc = cls;
|
struct WithdrawContext *wc = cls;
|
||||||
struct TALER_EXCHANGEDB_Reserve r;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
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;
|
||||||
|
|
||||||
#if OPTIMISTIC_SIGN
|
now = GNUNET_TIME_absolute_get ();
|
||||||
/* store away optimistic signature to protect
|
(void) GNUNET_TIME_round_abs (&now);
|
||||||
it from being overwritten by get_withdraw_info */
|
|
||||||
denom_sig = wc->collectable.sig;
|
|
||||||
memset (&wc->collectable.sig,
|
|
||||||
0,
|
|
||||||
sizeof (wc->collectable.sig));
|
|
||||||
#endif
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
|
||||||
"Asked to withdraw from %s amount of %s\n",
|
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
||||||
TALER_B2S (&wc->wsrd.reserve_pub),
|
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
|
||||||
TALER_amount2s (&wc->amount_required));
|
&wc->collectable,
|
||||||
/* Don't sign again if we have already signed the coin */
|
now,
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
&found,
|
||||||
{
|
&balance_ok,
|
||||||
/* Toss out the optimistic signature, we got another one from the DB;
|
&wc->kyc,
|
||||||
optimization trade-off loses in this case: we unnecessarily computed
|
&reserve_uuid);
|
||||||
a signature :-( */
|
|
||||||
#if OPTIMISTIC_SIGN
|
|
||||||
TALER_blinded_denom_sig_free (&denom_sig);
|
|
||||||
#endif
|
|
||||||
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; /* Note: might still be NULL if we didn't do OPTIMISTIC_SIGN */
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"reserves");
|
"do_withdraw");
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
if (! found)
|
||||||
{
|
{
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_NOT_FOUND,
|
MHD_HTTP_NOT_FOUND,
|
||||||
@ -269,29 +172,29 @@ withdraw_transaction (void *cls,
|
|||||||
NULL);
|
NULL);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
if (0 < TALER_amount_cmp (&wc->amount_required,
|
if (! balance_ok)
|
||||||
&r.balance))
|
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGEDB_ReserveHistory *rh;
|
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
|
/* The reserve does not have the required amount (actual
|
||||||
* amount + withdraw fee) */
|
* 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,
|
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||||
&wc->wsrd.reserve_pub,
|
&wc->wsrd.reserve_pub,
|
||||||
|
&balance,
|
||||||
&rh);
|
&rh);
|
||||||
if (NULL == rh)
|
if (NULL == rh)
|
||||||
{
|
{
|
||||||
@ -303,41 +206,41 @@ withdraw_transaction (void *cls,
|
|||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
*mhd_ret = reply_withdraw_insufficient_funds (connection,
|
*mhd_ret = reply_withdraw_insufficient_funds (connection,
|
||||||
&r.balance,
|
&balance,
|
||||||
rh);
|
rh);
|
||||||
TEH_plugin->free_reserve_history (TEH_plugin->cls,
|
TEH_plugin->free_reserve_history (TEH_plugin->cls,
|
||||||
rh);
|
rh);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||||
"KYC status is %s for %s\n",
|
(! wc->kyc.ok) &&
|
||||||
wc->kyc.ok ? "ok" : "missing",
|
|
||||||
TALER_B2S (&r.pub));
|
|
||||||
if ( (! wc->kyc.ok) &&
|
|
||||||
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
|
||||||
(TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
|
(TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
|
||||||
{
|
{
|
||||||
/* Wallet-to-wallet payments _always_ require KYC */
|
/* Wallet-to-wallet payments _always_ require KYC */
|
||||||
wc->kyc_denied = true;
|
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||||
return qs;
|
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) &&
|
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||||
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
(! wc->kyc.ok) &&
|
||||||
(TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
|
(TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
|
||||||
(! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
|
(! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
|
||||||
{
|
{
|
||||||
/* Withdraws require KYC if above threshold */
|
/* Withdraws require KYC if above threshold */
|
||||||
struct TALER_Amount acc;
|
|
||||||
enum GNUNET_DB_QueryStatus qs2;
|
enum GNUNET_DB_QueryStatus qs2;
|
||||||
|
bool below_limit;
|
||||||
|
|
||||||
acc = wc->amount_required;
|
qs2 = TEH_plugin->do_withdraw_limit_check (
|
||||||
qs2 = TEH_plugin->select_withdraw_amounts_by_account (
|
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&wc->wsrd.reserve_pub,
|
reserve_uuid,
|
||||||
TEH_kyc_config.withdraw_period,
|
GNUNET_TIME_absolute_subtract (now,
|
||||||
&accumulate_withdraws,
|
TEH_kyc_config.withdraw_period),
|
||||||
&acc);
|
&TEH_kyc_config.withdraw_limit,
|
||||||
|
&below_limit);
|
||||||
if (0 > qs2)
|
if (0 > qs2)
|
||||||
{
|
{
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
|
||||||
@ -345,74 +248,64 @@ withdraw_transaction (void *cls,
|
|||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"withdraw details");
|
"do_withdraw_limit_check");
|
||||||
return qs2;
|
return qs2;
|
||||||
}
|
}
|
||||||
|
if (! below_limit)
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_amount_is_valid (&acc))
|
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||||
*mhd_ret = TALER_MHD_reply_with_ec (connection,
|
connection,
|
||||||
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
|
MHD_HTTP_ACCEPTED,
|
||||||
NULL);
|
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||||
|
wc->kyc.payment_target_uuid));
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
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;
|
return qs;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Balance is good, sign the coin! */
|
|
||||||
#if ! OPTIMISTIC_SIGN
|
|
||||||
if (NULL == wc->collectable.sig.rsa_signature)
|
|
||||||
{
|
|
||||||
enum TALER_ErrorCode ec = TALER_EC_NONE;
|
|
||||||
|
|
||||||
wc->collectable.sig
|
/**
|
||||||
= TEH_keys_denomination_sign (&wc->denom_pub_hash,
|
* Check if the @a rc is replayed and we already have an
|
||||||
wc->blinded_msg,
|
* answer. If so, replay the existing answer and return the
|
||||||
wc->blinded_msg_len,
|
* HTTP response.
|
||||||
&ec);
|
*
|
||||||
if (TALER_EC_NONE != ec)
|
* @param rc request context
|
||||||
{
|
* @param[in,out] wc parsed request data
|
||||||
GNUNET_break (0);
|
* @param[out] mret HTTP status, set if we return true
|
||||||
*mhd_ret = TALER_MHD_reply_with_ec (connection,
|
* @return true if the request is idempotent with an existing request
|
||||||
ec,
|
* false if we did not find the request in the DB and did not set @a mret
|
||||||
NULL);
|
*/
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
static bool
|
||||||
}
|
check_request_idempotent (struct TEH_RequestContext *rc,
|
||||||
}
|
struct WithdrawContext *wc,
|
||||||
#endif
|
MHD_RESULT *mret)
|
||||||
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
|
{
|
||||||
wc->collectable.amount_with_fee = wc->amount_required;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
|
|
||||||
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
|
||||||
wc->collectable.reserve_sig = wc->signature;
|
&wc->wsrd.h_coin_envelope,
|
||||||
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);
|
&wc->collectable);
|
||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mret = TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"withdraw details");
|
"get_withdraw_info");
|
||||||
return qs;
|
return true; /* well, kind-of */
|
||||||
}
|
}
|
||||||
return qs;
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
|
return false;
|
||||||
|
/* generate idempotent reply */
|
||||||
|
*mret = TALER_MHD_REPLY_JSON_PACK (
|
||||||
|
rc->connection,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
TALER_JSON_pack_blinded_denom_sig ("ev_sig",
|
||||||
|
&wc->collectable.sig));
|
||||||
|
TALER_blinded_denom_sig_free (&wc->collectable.sig);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -427,9 +320,9 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
(void **) &wc.blinded_msg,
|
(void **) &wc.blinded_msg,
|
||||||
&wc.blinded_msg_len),
|
&wc.blinded_msg_len),
|
||||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||||
&wc.signature),
|
&wc.collectable.reserve_sig),
|
||||||
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
||||||
&wc.denom_pub_hash),
|
&wc.collectable.denom_pub_hash),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
enum TALER_ErrorCode ec;
|
enum TALER_ErrorCode ec;
|
||||||
@ -463,12 +356,39 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
{
|
{
|
||||||
MHD_RESULT mret;
|
MHD_RESULT mret;
|
||||||
struct GNUNET_TIME_Absolute now;
|
struct GNUNET_TIME_Absolute now;
|
||||||
|
struct TEH_KeyStateHandle *ksh;
|
||||||
|
|
||||||
dk = TEH_keys_denomination_by_hash (&wc.denom_pub_hash,
|
ksh = TEH_keys_get_state ();
|
||||||
rc->connection,
|
if (NULL == ksh)
|
||||||
&mret);
|
{
|
||||||
|
if (! check_request_idempotent (rc,
|
||||||
|
&wc,
|
||||||
|
&mret))
|
||||||
|
{
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
return mret;
|
||||||
|
}
|
||||||
|
dk = TEH_keys_denomination_by_hash2 (ksh,
|
||||||
|
&wc.collectable.denom_pub_hash,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
if (NULL == dk)
|
if (NULL == dk)
|
||||||
{
|
{
|
||||||
|
if (! check_request_idempotent (rc,
|
||||||
|
&wc,
|
||||||
|
&mret))
|
||||||
|
{
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
|
||||||
|
rc->connection,
|
||||||
|
&wc.collectable.denom_pub_hash);
|
||||||
|
}
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return mret;
|
return mret;
|
||||||
}
|
}
|
||||||
@ -481,25 +401,33 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
now = GNUNET_TIME_absolute_get ();
|
now = GNUNET_TIME_absolute_get ();
|
||||||
(void) GNUNET_TIME_round_abs (&now);
|
(void) GNUNET_TIME_round_abs (&now);
|
||||||
/* This denomination is past the expiration time for withdraws */
|
/* This denomination is past the expiration time for withdraws */
|
||||||
|
if (! check_request_idempotent (rc,
|
||||||
|
&wc,
|
||||||
|
&mret))
|
||||||
|
{
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
&wc.denom_pub_hash,
|
&wc.collectable.denom_pub_hash,
|
||||||
now,
|
now,
|
||||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
||||||
"WITHDRAW");
|
"WITHDRAW");
|
||||||
}
|
}
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
return mret;
|
||||||
|
}
|
||||||
if (GNUNET_TIME_absolute_is_future (dk->meta.start))
|
if (GNUNET_TIME_absolute_is_future (dk->meta.start))
|
||||||
{
|
{
|
||||||
struct GNUNET_TIME_Absolute now;
|
struct GNUNET_TIME_Absolute now;
|
||||||
|
|
||||||
now = GNUNET_TIME_absolute_get ();
|
now = GNUNET_TIME_absolute_get ();
|
||||||
(void) GNUNET_TIME_round_abs (&now);
|
(void) GNUNET_TIME_round_abs (&now);
|
||||||
/* This denomination is not yet valid */
|
/* This denomination is not yet valid, no need to check
|
||||||
|
for idempotency! */
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
&wc.denom_pub_hash,
|
&wc.collectable.denom_pub_hash,
|
||||||
now,
|
now,
|
||||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
|
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
|
||||||
"WITHDRAW");
|
"WITHDRAW");
|
||||||
@ -511,19 +439,26 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
now = GNUNET_TIME_absolute_get ();
|
now = GNUNET_TIME_absolute_get ();
|
||||||
(void) GNUNET_TIME_round_abs (&now);
|
(void) GNUNET_TIME_round_abs (&now);
|
||||||
/* This denomination has been revoked */
|
/* This denomination has been revoked */
|
||||||
|
if (! check_request_idempotent (rc,
|
||||||
|
&wc,
|
||||||
|
&mret))
|
||||||
|
{
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
&wc.denom_pub_hash,
|
&wc.collectable.denom_pub_hash,
|
||||||
now,
|
now,
|
||||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
|
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
|
||||||
"WITHDRAW");
|
"WITHDRAW");
|
||||||
}
|
}
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
return mret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if (0 >
|
if (0 >
|
||||||
TALER_amount_add (&wc.amount_required,
|
TALER_amount_add (&wc.collectable.amount_with_fee,
|
||||||
&dk->meta.value,
|
&dk->meta.value,
|
||||||
&dk->meta.fee_withdraw))
|
&dk->meta.fee_withdraw))
|
||||||
{
|
{
|
||||||
@ -534,7 +469,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
TALER_amount_hton (&wc.wsrd.amount_with_fee,
|
TALER_amount_hton (&wc.wsrd.amount_with_fee,
|
||||||
&wc.amount_required);
|
&wc.collectable.amount_with_fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* verify signature! */
|
/* verify signature! */
|
||||||
@ -543,14 +478,15 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
wc.wsrd.purpose.purpose
|
wc.wsrd.purpose.purpose
|
||||||
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
||||||
wc.wsrd.h_denomination_pub
|
wc.wsrd.h_denomination_pub
|
||||||
= wc.denom_pub_hash;
|
= wc.collectable.denom_pub_hash;
|
||||||
TALER_coin_ev_hash (wc.blinded_msg,
|
TALER_coin_ev_hash (wc.blinded_msg,
|
||||||
wc.blinded_msg_len,
|
wc.blinded_msg_len,
|
||||||
&wc.wsrd.h_coin_envelope);
|
&wc.wsrd.h_coin_envelope);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
GNUNET_CRYPTO_eddsa_verify (
|
||||||
|
TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
||||||
&wc.wsrd,
|
&wc.wsrd,
|
||||||
&wc.signature.eddsa_signature,
|
&wc.collectable.reserve_sig.eddsa_signature,
|
||||||
&wc.wsrd.reserve_pub.eddsa_pub))
|
&wc.wsrd.reserve_pub.eddsa_pub))
|
||||||
{
|
{
|
||||||
TALER_LOG_WARNING (
|
TALER_LOG_WARNING (
|
||||||
@ -562,11 +498,10 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OPTIMISTIC_SIGN
|
|
||||||
/* Sign before transaction! */
|
/* Sign before transaction! */
|
||||||
ec = TALER_EC_NONE;
|
ec = TALER_EC_NONE;
|
||||||
wc.collectable.sig
|
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,
|
||||||
wc.blinded_msg_len,
|
wc.blinded_msg_len,
|
||||||
&ec);
|
&ec);
|
||||||
@ -578,10 +513,8 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
ec,
|
ec,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* run transaction and sign (if not optimistically signed before) */
|
/* run transaction and sign (if not optimistically signed before) */
|
||||||
wc.kyc_denied = false;
|
|
||||||
{
|
{
|
||||||
MHD_RESULT mhd_ret;
|
MHD_RESULT mhd_ret;
|
||||||
|
|
||||||
@ -603,16 +536,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
/* Clean up and send back final response */
|
/* Clean up and send back final response */
|
||||||
GNUNET_JSON_parse_free (spec);
|
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;
|
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-postgres
|
||||||
test-exchangedb-signkeys
|
test-exchangedb-signkeys
|
||||||
test-perf-taler-exchangedb
|
test-perf-taler-exchangedb
|
||||||
|
bench-db-postgres
|
||||||
|
@ -379,7 +379,7 @@ run (void *cls)
|
|||||||
|
|
||||||
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||||
TOTAL);
|
TOTAL);
|
||||||
if (! bm_select ((0 == f)? conn2 : conn,
|
if (! bm_select ((0 == f) ? conn2 : conn,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_PQ_disconnect (conn);
|
GNUNET_PQ_disconnect (conn);
|
||||||
@ -422,7 +422,7 @@ run (void *cls)
|
|||||||
|
|
||||||
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||||
TOTAL);
|
TOTAL);
|
||||||
if (! bhm_select ((0 == f)? conn2 : conn,
|
if (! bhm_select ((0 == f) ? conn2 : conn,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_PQ_disconnect (conn);
|
GNUNET_PQ_disconnect (conn);
|
||||||
@ -465,7 +465,7 @@ run (void *cls)
|
|||||||
|
|
||||||
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||||
TOTAL);
|
TOTAL);
|
||||||
if (! bem_select ((0 == f)? conn2 : conn,
|
if (! bem_select ((0 == f) ? conn2 : conn,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_PQ_disconnect (conn);
|
GNUNET_PQ_disconnect (conn);
|
||||||
|
@ -54,6 +54,10 @@ DROP TABLE IF EXISTS reserves CASCADE;
|
|||||||
DROP TABLE IF EXISTS denomination_revocations CASCADE;
|
DROP TABLE IF EXISTS denomination_revocations CASCADE;
|
||||||
DROP TABLE IF EXISTS denominations 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...
|
-- And we're out of here...
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS wire_targets
|
|||||||
,PRIMARY KEY (h_payto)
|
,PRIMARY KEY (h_payto)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE wire_targets
|
COMMENT ON TABLE wire_targets
|
||||||
IS 'All recipients of money via the exchange';
|
IS 'All senders and recipients of money via the exchange';
|
||||||
COMMENT ON COLUMN wire_targets.payto_uri
|
COMMENT ON COLUMN wire_targets.payto_uri
|
||||||
IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
|
IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
|
||||||
COMMENT ON COLUMN wire_targets.h_payto
|
COMMENT ON COLUMN wire_targets.h_payto
|
||||||
@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS reserves_in
|
|||||||
COMMENT ON TABLE reserves_in
|
COMMENT ON TABLE reserves_in
|
||||||
IS 'list of transfers of funds into the reserves, one per incoming wire transfer';
|
IS 'list of transfers of funds into the reserves, one per incoming wire transfer';
|
||||||
COMMENT ON COLUMN reserves_in.wire_source_serial_id
|
COMMENT ON COLUMN reserves_in.wire_source_serial_id
|
||||||
IS 'Identifies the debited bank account and KYC status';-- FIXME: explain 'wire_reference'!
|
IS 'Identifies the debited bank account and KYC status';
|
||||||
CREATE INDEX IF NOT EXISTS reserves_in_execution_index
|
CREATE INDEX IF NOT EXISTS reserves_in_execution_index
|
||||||
ON reserves_in
|
ON reserves_in
|
||||||
(exchange_account_section
|
(exchange_account_section
|
||||||
@ -263,7 +263,7 @@ CREATE TABLE IF NOT EXISTS signkey_revocations
|
|||||||
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
|
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE signkey_revocations
|
COMMENT ON TABLE signkey_revocations
|
||||||
IS 'remembering which online signing keys have been revoked';
|
IS 'Table storing which online signing keys have been revoked';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS known_coins
|
CREATE TABLE IF NOT EXISTS known_coins
|
||||||
@ -301,6 +301,12 @@ CREATE TABLE IF NOT EXISTS refresh_commitments
|
|||||||
);
|
);
|
||||||
COMMENT ON TABLE refresh_commitments
|
COMMENT ON TABLE refresh_commitments
|
||||||
IS 'Commitments made when melting coins and the gamma value chosen by the exchange.';
|
IS 'Commitments made when melting coins and the gamma value chosen by the exchange.';
|
||||||
|
COMMENT ON COLUMN refresh_commitments.noreveal_index
|
||||||
|
IS 'The gamma value chosen by the exchange in the cut-and-choose protocol';
|
||||||
|
COMMENT ON COLUMN refresh_commitments.rc
|
||||||
|
IS 'Commitment made by the client, hash over the various client inputs in the cut-and-choose protocol';
|
||||||
|
COMMENT ON COLUMN refresh_commitments.old_known_coin_id
|
||||||
|
IS 'Coin being melted in the refresh process.';
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS refresh_commitments_old_coin_id_index
|
CREATE INDEX IF NOT EXISTS refresh_commitments_old_coin_id_index
|
||||||
ON refresh_commitments
|
ON refresh_commitments
|
||||||
@ -526,11 +532,15 @@ CREATE TABLE IF NOT EXISTS recoup
|
|||||||
,reserve_out_serial_id INT8 NOT NULL REFERENCES reserves_out (reserve_out_serial_id) ON DELETE CASCADE
|
,reserve_out_serial_id INT8 NOT NULL REFERENCES reserves_out (reserve_out_serial_id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE recoup
|
COMMENT ON TABLE recoup
|
||||||
IS 'Information about recoups that were executed';
|
IS 'Information about recoups that were executed between a coin and a reserve. In this type of recoup, the amount is credited back to the reserve from which the coin originated.';
|
||||||
COMMENT ON COLUMN recoup.known_coin_id
|
COMMENT ON COLUMN recoup.known_coin_id
|
||||||
IS 'Do not CASCADE ON DROP on the known_coin_id, as we may keep the coin alive!';
|
IS 'Coin that is being debited in the recoup. Do not CASCADE ON DROP on the known_coin_id, as we may keep the coin alive!';
|
||||||
COMMENT ON COLUMN recoup.reserve_out_serial_id
|
COMMENT ON COLUMN recoup.reserve_out_serial_id
|
||||||
IS 'Identifies the h_blind_ev of the recouped coin.';
|
IS 'Identifies the h_blind_ev of the recouped coin and provides the link to the credited reserve.';
|
||||||
|
COMMENT ON COLUMN recoup.coin_sig
|
||||||
|
IS 'Signature by the coin affirming the recoup, of type TALER_SIGNATURE_WALLET_COIN_RECOUP';
|
||||||
|
COMMENT ON COLUMN recoup.coin_blind
|
||||||
|
IS 'Denomination blinding key used when creating the blinded coin from the planchet. Secret revealed during the recoup to provide the linkage between the coin and the withdraw operation.';
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS recoup_by_h_blind_ev
|
CREATE INDEX IF NOT EXISTS recoup_by_h_blind_ev
|
||||||
ON recoup
|
ON recoup
|
||||||
@ -552,10 +562,14 @@ CREATE TABLE IF NOT EXISTS recoup_refresh
|
|||||||
,timestamp INT8 NOT NULL
|
,timestamp INT8 NOT NULL
|
||||||
,rrc_serial INT8 NOT NULL UNIQUE REFERENCES refresh_revealed_coins (rrc_serial) ON DELETE CASCADE
|
,rrc_serial INT8 NOT NULL UNIQUE REFERENCES refresh_revealed_coins (rrc_serial) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
COMMENT ON TABLE recoup_refresh
|
||||||
|
IS 'Table of coins that originated from a refresh operation and that were recouped. Links the (fresh) coin to the melted operation (and thus the old coin). A recoup on a refreshed coin credits the old coin and debits the fresh coin.';
|
||||||
COMMENT ON COLUMN recoup_refresh.known_coin_id
|
COMMENT ON COLUMN recoup_refresh.known_coin_id
|
||||||
IS 'Do not CASCADE ON DROP on the known_coin_id, as we may keep the coin alive!';
|
IS 'Refreshed coin of a revoked denomination where the residual value is credited to the old coin. Do not CASCADE ON DROP on the known_coin_id, as we may keep the coin alive!';
|
||||||
COMMENT ON COLUMN recoup_refresh.rrc_serial
|
COMMENT ON COLUMN recoup_refresh.rrc_serial
|
||||||
IS 'Identifies the h_blind_ev of the recouped coin (as h_coin_ev).';
|
IS 'Link to the refresh operation. Also identifies the h_blind_ev of the recouped coin (as h_coin_ev).';
|
||||||
|
COMMENT ON COLUMN recoup_refresh.coin_blind
|
||||||
|
IS 'Denomination blinding key used when creating the blinded coin from the planchet. Secret revealed during the recoup to provide the linkage between the coin and the refresh operation.';
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS recoup_refresh_by_h_blind_ev
|
CREATE INDEX IF NOT EXISTS recoup_refresh_by_h_blind_ev
|
||||||
ON recoup_refresh
|
ON recoup_refresh
|
||||||
@ -680,6 +694,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
|
-- Complete transaction
|
||||||
|
@ -596,6 +596,34 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
"lock_withdraw",
|
"lock_withdraw",
|
||||||
"LOCK TABLE reserves_out;",
|
"LOCK TABLE reserves_out;",
|
||||||
0),
|
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
|
/* Used in #postgres_insert_withdraw_info() to store
|
||||||
the signature of a blinded coin with the blinded coin's
|
the signature of a blinded coin with the blinded coin's
|
||||||
details before returning it during /reserve/withdraw. We store
|
details before returning it during /reserve/withdraw. We store
|
||||||
@ -753,7 +781,8 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
",denom_sig"
|
",denom_sig"
|
||||||
") SELECT $1, denominations_serial, $3 "
|
") SELECT $1, denominations_serial, $3 "
|
||||||
" FROM denominations"
|
" FROM denominations"
|
||||||
" WHERE denom_pub_hash=$2;",
|
" WHERE denom_pub_hash=$2"
|
||||||
|
" ON CONFLICT DO NOTHING;",
|
||||||
3),
|
3),
|
||||||
|
|
||||||
/* Used in #postgres_insert_melt() to store
|
/* Used in #postgres_insert_melt() to store
|
||||||
@ -3378,11 +3407,11 @@ dominations_cb_helper (void *cls,
|
|||||||
struct TALER_DenominationPublicKey denom_pub;
|
struct TALER_DenominationPublicKey denom_pub;
|
||||||
struct TALER_MasterSignatureP master_sig;
|
struct TALER_MasterSignatureP master_sig;
|
||||||
struct TALER_DenominationHash h_denom_pub;
|
struct TALER_DenominationHash h_denom_pub;
|
||||||
uint8_t revoked;
|
bool revoked;
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
|
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
|
||||||
&master_sig),
|
&master_sig),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("revoked",
|
GNUNET_PQ_result_spec_bool ("revoked",
|
||||||
&revoked),
|
&revoked),
|
||||||
TALER_PQ_result_spec_absolute_time ("valid_from",
|
TALER_PQ_result_spec_absolute_time ("valid_from",
|
||||||
&meta.start),
|
&meta.start),
|
||||||
@ -3422,7 +3451,7 @@ dominations_cb_helper (void *cls,
|
|||||||
&h_denom_pub,
|
&h_denom_pub,
|
||||||
&meta,
|
&meta,
|
||||||
&master_sig,
|
&master_sig,
|
||||||
(0 != revoked));
|
revoked);
|
||||||
GNUNET_PQ_cleanup_result (rs);
|
GNUNET_PQ_cleanup_result (rs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3777,7 +3806,6 @@ postgres_reserves_get (void *cls,
|
|||||||
GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
|
GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
uint8_t ok8;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
|
||||||
&reserve->balance),
|
&reserve->balance),
|
||||||
@ -3787,19 +3815,16 @@ postgres_reserves_get (void *cls,
|
|||||||
&reserve->gc),
|
&reserve->gc),
|
||||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||||
&kyc->payment_target_uuid),
|
&kyc->payment_target_uuid),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||||
&ok8),
|
&kyc->ok),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||||
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"reserves_get_with_kyc",
|
"reserves_get_with_kyc",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
|
||||||
kyc->ok = (0 != ok8);
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3874,23 +3899,19 @@ postgres_get_kyc_status (void *cls,
|
|||||||
GNUNET_PQ_query_param_string (payto_uri),
|
GNUNET_PQ_query_param_string (payto_uri),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
uint8_t ok8;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||||
&kyc->payment_target_uuid),
|
&kyc->payment_target_uuid),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||||
&ok8),
|
&kyc->ok),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||||
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"get_kyc_status",
|
"get_kyc_status",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
|
||||||
kyc->ok = (0 != ok8);
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3914,24 +3935,20 @@ postgres_select_kyc_status (void *cls,
|
|||||||
GNUNET_PQ_query_param_uint64 (&payment_target_uuid),
|
GNUNET_PQ_query_param_uint64 (&payment_target_uuid),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
uint8_t ok8;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("h_payto",
|
GNUNET_PQ_result_spec_auto_from_type ("h_payto",
|
||||||
h_payto),
|
h_payto),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||||
&ok8),
|
&kyc->ok),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
kyc->type = TALER_EXCHANGEDB_KYC_UNKNOWN;
|
||||||
|
kyc->payment_target_uuid = payment_target_uuid;
|
||||||
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"select_kyc_status",
|
"select_kyc_status",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_UNKNOWN;
|
|
||||||
kyc->ok = (0 != ok8);
|
|
||||||
kyc->payment_target_uuid = payment_target_uuid;
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3962,12 +3979,11 @@ inselect_account_kyc_status (
|
|||||||
GNUNET_PQ_query_param_auto_from_type (&h_payto),
|
GNUNET_PQ_query_param_auto_from_type (&h_payto),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
uint8_t ok8 = 0;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_uint64 ("wire_target_serial_id",
|
GNUNET_PQ_result_spec_uint64 ("wire_target_serial_id",
|
||||||
&kyc->payment_target_uuid),
|
&kyc->payment_target_uuid),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||||
&ok8),
|
&kyc->ok),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3998,10 +4014,6 @@ inselect_account_kyc_status (
|
|||||||
return GNUNET_DB_STATUS_SOFT_ERROR;
|
return GNUNET_DB_STATUS_SOFT_ERROR;
|
||||||
kyc->ok = false;
|
kyc->ok = false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
kyc->ok = (0 != ok8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_BALANCE;
|
kyc->type = TALER_EXCHANGEDB_KYC_BALANCE;
|
||||||
return qs;
|
return qs;
|
||||||
@ -4478,86 +4490,104 @@ postgres_get_withdraw_info (
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store collectable bit coin under the corresponding
|
* Perform withdraw operation, checking for sufficient balance
|
||||||
* hash of the blinded message.
|
* and possibly persisting the withdrawal details.
|
||||||
*
|
*
|
||||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||||
* @param collectable corresponding collectable coin (blind signature)
|
* @param collectable corresponding collectable coin (blind signature)
|
||||||
* if a coin is found
|
* 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
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_DB_QueryStatus
|
static enum GNUNET_DB_QueryStatus
|
||||||
postgres_insert_withdraw_info (
|
postgres_do_withdraw (
|
||||||
void *cls,
|
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 PostgresClosure *pg = cls;
|
||||||
struct TALER_EXCHANGEDB_Reserve reserve;
|
|
||||||
struct GNUNET_TIME_Absolute now;
|
|
||||||
struct GNUNET_TIME_Absolute gc;
|
struct GNUNET_TIME_Absolute gc;
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
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),
|
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_pub),
|
||||||
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
|
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_absolute_time (&now),
|
||||||
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
|
TALER_PQ_query_param_absolute_time (&gc),
|
||||||
GNUNET_PQ_query_param_end
|
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,
|
gc = GNUNET_TIME_absolute_add (now,
|
||||||
pg->legal_reserve_expiration_time);
|
pg->legal_reserve_expiration_time);
|
||||||
reserve.gc = GNUNET_TIME_absolute_max (gc,
|
(void) GNUNET_TIME_round_abs (&gc);
|
||||||
reserve.gc);
|
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||||
(void) GNUNET_TIME_round_abs (&reserve.gc);
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
qs = reserves_update (pg,
|
"call_withdraw",
|
||||||
&reserve);
|
params,
|
||||||
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
|
rs);
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
{
|
}
|
||||||
GNUNET_break (0);
|
|
||||||
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
/**
|
||||||
return qs;
|
* 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 +4617,21 @@ struct ReserveHistoryContext
|
|||||||
*/
|
*/
|
||||||
struct PostgresClosure *pg;
|
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
|
* Set to #GNUNET_SYSERR on serious internal errors during
|
||||||
* the callbacks.
|
* the callbacks.
|
||||||
*/
|
*/
|
||||||
int status;
|
enum GNUNET_GenericReturnValue status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -4667,6 +4707,10 @@ add_bank_to_exchange (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_add (&rhc->balance_in,
|
||||||
|
&rhc->balance_in,
|
||||||
|
&bt->amount));
|
||||||
bt->reserve_pub = *rhc->reserve_pub;
|
bt->reserve_pub = *rhc->reserve_pub;
|
||||||
tail = append_rh (rhc);
|
tail = append_rh (rhc);
|
||||||
tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
|
tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
|
||||||
@ -4724,6 +4768,10 @@ add_withdraw_coin (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_add (&rhc->balance_out,
|
||||||
|
&rhc->balance_out,
|
||||||
|
&cbc->amount_with_fee));
|
||||||
cbc->reserve_pub = *rhc->reserve_pub;
|
cbc->reserve_pub = *rhc->reserve_pub;
|
||||||
tail = append_rh (rhc);
|
tail = append_rh (rhc);
|
||||||
tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
|
tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
|
||||||
@ -4784,6 +4832,10 @@ add_recoup (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_add (&rhc->balance_in,
|
||||||
|
&rhc->balance_in,
|
||||||
|
&recoup->value));
|
||||||
recoup->reserve_pub = *rhc->reserve_pub;
|
recoup->reserve_pub = *rhc->reserve_pub;
|
||||||
tail = append_rh (rhc);
|
tail = append_rh (rhc);
|
||||||
tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
|
tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
|
||||||
@ -4840,6 +4892,10 @@ add_exchange_to_bank (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_add (&rhc->balance_out,
|
||||||
|
&rhc->balance_out,
|
||||||
|
&closing->amount));
|
||||||
closing->reserve_pub = *rhc->reserve_pub;
|
closing->reserve_pub = *rhc->reserve_pub;
|
||||||
tail = append_rh (rhc);
|
tail = append_rh (rhc);
|
||||||
tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
|
tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
|
||||||
@ -4854,12 +4910,14 @@ add_exchange_to_bank (void *cls,
|
|||||||
*
|
*
|
||||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||||
* @param reserve_pub public key of the reserve
|
* @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)
|
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
|
||||||
* @return transaction status
|
* @return transaction status
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_DB_QueryStatus
|
static enum GNUNET_DB_QueryStatus
|
||||||
postgres_get_reserve_history (void *cls,
|
postgres_get_reserve_history (void *cls,
|
||||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
struct TALER_Amount *balance,
|
||||||
struct TALER_EXCHANGEDB_ReserveHistory **rhp)
|
struct TALER_EXCHANGEDB_ReserveHistory **rhp)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
@ -4902,6 +4960,12 @@ postgres_get_reserve_history (void *cls,
|
|||||||
rhc.rh_tail = NULL;
|
rhc.rh_tail = NULL;
|
||||||
rhc.pg = pg;
|
rhc.pg = pg;
|
||||||
rhc.status = GNUNET_OK;
|
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 */
|
qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
|
||||||
for (unsigned int i = 0; NULL != work[i].cb; i++)
|
for (unsigned int i = 0; NULL != work[i].cb; i++)
|
||||||
{
|
{
|
||||||
@ -4927,6 +4991,10 @@ postgres_get_reserve_history (void *cls,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*rhp = rhc.rh;
|
*rhp = rhc.rh;
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_subtract (balance,
|
||||||
|
&rhc.balance_in,
|
||||||
|
&rhc.balance_out));
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5308,13 +5376,12 @@ postgres_get_ready_deposit (void *cls,
|
|||||||
void *deposit_cb_cls)
|
void *deposit_cb_cls)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
uint8_t kyc_override = (kyc_off) ? 1 : 0;
|
|
||||||
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
|
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
TALER_PQ_query_param_absolute_time (&now),
|
TALER_PQ_query_param_absolute_time (&now),
|
||||||
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
||||||
GNUNET_PQ_query_param_uint64 (&end_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
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
struct TALER_Amount amount_with_fee;
|
struct TALER_Amount amount_with_fee;
|
||||||
@ -5701,16 +5768,24 @@ postgres_ensure_coin_known (void *cls,
|
|||||||
&denom_pub_hash),
|
&denom_pub_hash),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
#if EXPLICIT_LOCKS
|
|
||||||
struct GNUNET_PQ_QueryParam no_params[] = {
|
|
||||||
GNUNET_PQ_query_param_end
|
|
||||||
};
|
|
||||||
|
|
||||||
if (0 > (qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
/* First, try to simply insert it */
|
||||||
"lock_known_coins",
|
qs = insert_known_coin (pg,
|
||||||
no_params)))
|
coin);
|
||||||
return qs;
|
switch (qs)
|
||||||
#endif
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
|
/* continued below */
|
||||||
|
break;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
return TALER_EXCHANGEDB_CKS_ADDED;
|
||||||
|
}
|
||||||
|
|
||||||
/* check if the coin is already known */
|
/* check if the coin is already known */
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"get_known_coin_dh",
|
"get_known_coin_dh",
|
||||||
@ -5729,26 +5804,13 @@ postgres_ensure_coin_known (void *cls,
|
|||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return TALER_EXCHANGEDB_CKS_CONFLICT;
|
return TALER_EXCHANGEDB_CKS_CONFLICT;
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
break;
|
/* should be impossible */
|
||||||
}
|
|
||||||
|
|
||||||
/* if not known, insert it */
|
|
||||||
qs = insert_known_coin (pg,
|
|
||||||
coin);
|
|
||||||
switch (qs)
|
|
||||||
{
|
|
||||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
}
|
||||||
return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
|
/* we should never get here */
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
||||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TALER_EXCHANGEDB_CKS_ADDED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6609,7 +6671,6 @@ add_coin_deposit (void *cls,
|
|||||||
chc->have_deposit_or_melt = true;
|
chc->have_deposit_or_melt = true;
|
||||||
deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
|
deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
|
||||||
{
|
{
|
||||||
uint8_t done = 0;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||||
&deposit->amount_with_fee),
|
&deposit->amount_with_fee),
|
||||||
@ -6636,7 +6697,7 @@ add_coin_deposit (void *cls,
|
|||||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||||
&serial_id),
|
&serial_id),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||||
&done),
|
&deposit->done),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -6650,7 +6711,6 @@ add_coin_deposit (void *cls,
|
|||||||
chc->failed = true;
|
chc->failed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
deposit->done = (0 != done);
|
|
||||||
}
|
}
|
||||||
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
||||||
tl->next = chc->head;
|
tl->next = chc->head;
|
||||||
@ -7340,7 +7400,6 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
/* Check if transaction exists in deposits, so that we just
|
/* Check if transaction exists in deposits, so that we just
|
||||||
do not have a WTID yet. In that case, return without wtid
|
do not have a WTID yet. In that case, return without wtid
|
||||||
(by setting 'pending' true). */
|
(by setting 'pending' true). */
|
||||||
uint8_t ok8 = 0;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs2[] = {
|
struct GNUNET_PQ_ResultSpec rs2[] = {
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
||||||
&wire_salt),
|
&wire_salt),
|
||||||
@ -7349,7 +7408,7 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||||
&kyc->payment_target_uuid),
|
&kyc->payment_target_uuid),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||||
&ok8),
|
&kyc->ok),
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||||
amount_with_fee),
|
amount_with_fee),
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
||||||
@ -7377,7 +7436,6 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||||
}
|
}
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||||
kyc->ok = (0 != ok8);
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8164,7 +8222,7 @@ deposit_serial_helper_cb (void *cls,
|
|||||||
struct TALER_EXCHANGEDB_Deposit deposit;
|
struct TALER_EXCHANGEDB_Deposit deposit;
|
||||||
struct GNUNET_TIME_Absolute exchange_timestamp;
|
struct GNUNET_TIME_Absolute exchange_timestamp;
|
||||||
struct TALER_DenominationPublicKey denom_pub;
|
struct TALER_DenominationPublicKey denom_pub;
|
||||||
uint8_t done = 0;
|
bool done;
|
||||||
uint64_t rowid;
|
uint64_t rowid;
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||||
@ -8191,7 +8249,7 @@ deposit_serial_helper_cb (void *cls,
|
|||||||
&deposit.wire_salt),
|
&deposit.wire_salt),
|
||||||
GNUNET_PQ_result_spec_string ("receiver_wire_account",
|
GNUNET_PQ_result_spec_string ("receiver_wire_account",
|
||||||
&deposit.receiver_wire_account),
|
&deposit.receiver_wire_account),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
GNUNET_PQ_result_spec_bool ("done",
|
||||||
&done),
|
&done),
|
||||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||||
&rowid),
|
&rowid),
|
||||||
@ -8213,7 +8271,7 @@ deposit_serial_helper_cb (void *cls,
|
|||||||
exchange_timestamp,
|
exchange_timestamp,
|
||||||
&deposit,
|
&deposit,
|
||||||
&denom_pub,
|
&denom_pub,
|
||||||
(0 != done) ? true : false);
|
done);
|
||||||
GNUNET_PQ_cleanup_result (rs);
|
GNUNET_PQ_cleanup_result (rs);
|
||||||
if (GNUNET_OK != ret)
|
if (GNUNET_OK != ret)
|
||||||
break;
|
break;
|
||||||
@ -9783,8 +9841,8 @@ missing_wire_cb (void *cls,
|
|||||||
struct TALER_Amount amount;
|
struct TALER_Amount amount;
|
||||||
char *payto_uri;
|
char *payto_uri;
|
||||||
struct GNUNET_TIME_Absolute deadline;
|
struct GNUNET_TIME_Absolute deadline;
|
||||||
uint8_t tiny;
|
bool tiny;
|
||||||
uint8_t done;
|
bool done;
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||||
&rowid),
|
&rowid),
|
||||||
@ -9796,9 +9854,9 @@ missing_wire_cb (void *cls,
|
|||||||
&payto_uri),
|
&payto_uri),
|
||||||
TALER_PQ_result_spec_absolute_time ("wire_deadline",
|
TALER_PQ_result_spec_absolute_time ("wire_deadline",
|
||||||
&deadline),
|
&deadline),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("tiny",
|
GNUNET_PQ_result_spec_bool ("tiny",
|
||||||
&tiny),
|
&tiny),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
GNUNET_PQ_result_spec_bool ("done",
|
||||||
&done),
|
&done),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
@ -9923,22 +9981,18 @@ postgres_lookup_auditor_status (
|
|||||||
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
uint8_t enabled8 = 0;
|
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_string ("auditor_url",
|
GNUNET_PQ_result_spec_string ("auditor_url",
|
||||||
auditor_url),
|
auditor_url),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("is_active",
|
GNUNET_PQ_result_spec_bool ("is_active",
|
||||||
&enabled8),
|
enabled),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"lookup_auditor_status",
|
"lookup_auditor_status",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
*enabled = (0 != enabled8);
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -9996,12 +10050,11 @@ postgres_update_auditor (void *cls,
|
|||||||
bool enabled)
|
bool enabled)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
uint8_t enabled8 = enabled ? 1 : 0;
|
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
||||||
GNUNET_PQ_query_param_string (auditor_url),
|
GNUNET_PQ_query_param_string (auditor_url),
|
||||||
GNUNET_PQ_query_param_string (auditor_name),
|
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_absolute_time (&change_date),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
@ -10091,10 +10144,9 @@ postgres_update_wire (void *cls,
|
|||||||
bool enabled)
|
bool enabled)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
uint8_t enabled8 = enabled ? 1 : 0;
|
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_string (payto_uri),
|
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_absolute_time (&change_date),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
@ -11767,7 +11819,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
|||||||
plugin->get_latest_reserve_in_reference =
|
plugin->get_latest_reserve_in_reference =
|
||||||
&postgres_get_latest_reserve_in_reference;
|
&postgres_get_latest_reserve_in_reference;
|
||||||
plugin->get_withdraw_info = &postgres_get_withdraw_info;
|
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->get_reserve_history = &postgres_get_reserve_history;
|
||||||
plugin->select_withdraw_amounts_by_account
|
plugin->select_withdraw_amounts_by_account
|
||||||
= &postgres_select_withdraw_amounts_by_account;
|
= &postgres_select_withdraw_amounts_by_account;
|
||||||
|
@ -1659,10 +1659,26 @@ run (void *cls)
|
|||||||
cbc.reserve_pub = reserve_pub;
|
cbc.reserve_pub = reserve_pub;
|
||||||
cbc.amount_with_fee = value;
|
cbc.amount_with_fee = value;
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_set_zero (CURRENCY, &cbc.withdraw_fee));
|
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 !=
|
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||||
plugin->insert_withdraw_info (plugin->cls,
|
plugin->do_withdraw (plugin->cls,
|
||||||
&cbc));
|
&cbc,
|
||||||
|
now,
|
||||||
|
&found,
|
||||||
|
&balance_ok,
|
||||||
|
&kyc,
|
||||||
|
&ruuid));
|
||||||
|
GNUNET_assert (found);
|
||||||
|
GNUNET_assert (balance_ok);
|
||||||
|
GNUNET_assert (! kyc.ok);
|
||||||
|
}
|
||||||
FAILIF (GNUNET_OK !=
|
FAILIF (GNUNET_OK !=
|
||||||
check_reserve (&reserve_pub,
|
check_reserve (&reserve_pub,
|
||||||
value.value,
|
value.value,
|
||||||
@ -1780,9 +1796,14 @@ run (void *cls)
|
|||||||
value.currency));
|
value.currency));
|
||||||
|
|
||||||
result = 7;
|
result = 7;
|
||||||
|
{
|
||||||
|
struct TALER_Amount balance;
|
||||||
|
|
||||||
qs = plugin->get_reserve_history (plugin->cls,
|
qs = plugin->get_reserve_history (plugin->cls,
|
||||||
&reserve_pub,
|
&reserve_pub,
|
||||||
|
&balance,
|
||||||
&rh);
|
&rh);
|
||||||
|
}
|
||||||
FAILIF (0 > qs);
|
FAILIF (0 > qs);
|
||||||
FAILIF (NULL == rh);
|
FAILIF (NULL == rh);
|
||||||
rh_head = rh;
|
rh_head = rh;
|
||||||
|
@ -496,7 +496,7 @@ struct TALER_EXCHANGEDB_ClosingTransfer
|
|||||||
struct TALER_ReservePublicKeyP reserve_pub;
|
struct TALER_ReservePublicKeyP reserve_pub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount that was transferred to the exchange.
|
* Amount that was transferred from the exchange.
|
||||||
*/
|
*/
|
||||||
struct TALER_Amount amount;
|
struct TALER_Amount amount;
|
||||||
|
|
||||||
@ -2512,23 +2512,70 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
* @return statement execution status
|
* @return statement execution status
|
||||||
*/
|
*/
|
||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
(*insert_withdraw_info)(
|
(*insert_withdraw_infoXX)(
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
|
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
|
* Get all of the transaction history associated with the specified
|
||||||
* reserve.
|
* reserve.
|
||||||
*
|
*
|
||||||
* @param cls the @e cls of this struct with the plugin-specific state
|
* @param cls the @e cls of this struct with the plugin-specific state
|
||||||
* @param reserve_pub public key of the reserve
|
* @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)
|
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
|
||||||
* @return transaction status
|
* @return transaction status
|
||||||
*/
|
*/
|
||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
(*get_reserve_history)(void *cls,
|
(*get_reserve_history)(void *cls,
|
||||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
struct TALER_Amount *balance,
|
||||||
struct TALER_EXCHANGEDB_ReserveHistory **rhp);
|
struct TALER_EXCHANGEDB_ReserveHistory **rhp);
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,8 +46,9 @@ enum TALER_EXTENSION_ReturnValue
|
|||||||
* The default age mask represents the age groups
|
* The default age mask represents the age groups
|
||||||
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
|
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
|
||||||
*/
|
*/
|
||||||
#define TALER_EXTENSION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << \
|
#define TALER_EXTENSION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 \
|
||||||
14 | 1 << 16 | 1 << 18 | 1 << 21)
|
<< 14 | 1 << 16 | 1 << 18 | 1 \
|
||||||
|
<< 21)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param groups String representation of age groups, like: "8:10:12:14:16:18:21"
|
* @param groups String representation of age groups, like: "8:10:12:14:16:18:21"
|
||||||
|
@ -767,6 +767,8 @@ TALER_EXCHANGE_verify_coin_history (
|
|||||||
else if (0 == strcasecmp (type,
|
else if (0 == strcasecmp (type,
|
||||||
"RECOUP-REFRESH"))
|
"RECOUP-REFRESH"))
|
||||||
{
|
{
|
||||||
|
/* This is the coin that was subjected to a recoup,
|
||||||
|
the value being credited to the old coin. */
|
||||||
struct TALER_RecoupRefreshConfirmationPS pc = {
|
struct TALER_RecoupRefreshConfirmationPS pc = {
|
||||||
.purpose.size = htonl (sizeof (pc)),
|
.purpose.size = htonl (sizeof (pc)),
|
||||||
.purpose.purpose = htonl (
|
.purpose.purpose = htonl (
|
||||||
@ -836,6 +838,8 @@ TALER_EXCHANGE_verify_coin_history (
|
|||||||
else if (0 == strcasecmp (type,
|
else if (0 == strcasecmp (type,
|
||||||
"OLD-COIN-RECOUP"))
|
"OLD-COIN-RECOUP"))
|
||||||
{
|
{
|
||||||
|
/* This is the coin that was credited in a recoup,
|
||||||
|
the value being credited to the this coin. */
|
||||||
struct TALER_RecoupRefreshConfirmationPS pc = {
|
struct TALER_RecoupRefreshConfirmationPS pc = {
|
||||||
.purpose.size = htonl (sizeof (pc)),
|
.purpose.size = htonl (sizeof (pc)),
|
||||||
.purpose.purpose = htonl (
|
.purpose.purpose = htonl (
|
||||||
@ -878,7 +882,7 @@ TALER_EXCHANGE_verify_coin_history (
|
|||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
add = GNUNET_YES;
|
add = GNUNET_NO;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -911,7 +915,8 @@ TALER_EXCHANGE_verify_coin_history (
|
|||||||
However, for the implementation, we first *add* up all of
|
However, for the implementation, we first *add* up all of
|
||||||
these negative amounts, as we might get refunds before
|
these negative amounts, as we might get refunds before
|
||||||
deposits from a semi-evil exchange. Then, at the end, we do
|
deposits from a semi-evil exchange. Then, at the end, we do
|
||||||
the subtraction by calculating "total = total - rtotal" */GNUNET_assert (GNUNET_NO == add);
|
the subtraction by calculating "total = total - rtotal" */
|
||||||
|
GNUNET_assert (GNUNET_NO == add);
|
||||||
if (0 >
|
if (0 >
|
||||||
TALER_amount_add (&rtotal,
|
TALER_amount_add (&rtotal,
|
||||||
&rtotal,
|
&rtotal,
|
||||||
|
@ -83,6 +83,8 @@ try_connect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
|
|||||||
{
|
{
|
||||||
if (-1 != dh->sock)
|
if (-1 != dh->sock)
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Establishing connection!\n");
|
||||||
dh->sock = socket (AF_UNIX,
|
dh->sock = socket (AF_UNIX,
|
||||||
SOCK_STREAM,
|
SOCK_STREAM,
|
||||||
0);
|
0);
|
||||||
@ -103,6 +105,7 @@ try_connect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
|
|||||||
do_disconnect (dh);
|
do_disconnect (dh);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
|
TALER_CRYPTO_helper_rsa_poll (dh);
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +156,6 @@ TALER_CRYPTO_helper_rsa_connect (
|
|||||||
TALER_CRYPTO_helper_rsa_disconnect (dh);
|
TALER_CRYPTO_helper_rsa_disconnect (dh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
TALER_CRYPTO_helper_rsa_poll (dh);
|
|
||||||
return dh;
|
return dh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +399,8 @@ TALER_CRYPTO_helper_rsa_sign (
|
|||||||
.cipher = TALER_DENOMINATION_INVALID
|
.cipher = TALER_DENOMINATION_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Starting signature process\n");
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
try_connect (dh))
|
try_connect (dh))
|
||||||
{
|
{
|
||||||
@ -406,6 +410,8 @@ TALER_CRYPTO_helper_rsa_sign (
|
|||||||
return ds;
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Requesting signature\n");
|
||||||
{
|
{
|
||||||
char buf[sizeof (struct TALER_CRYPTO_SignRequest) + msg_size];
|
char buf[sizeof (struct TALER_CRYPTO_SignRequest) + msg_size];
|
||||||
struct TALER_CRYPTO_SignRequest *sr
|
struct TALER_CRYPTO_SignRequest *sr
|
||||||
@ -431,6 +437,8 @@ TALER_CRYPTO_helper_rsa_sign (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Awaiting reply\n");
|
||||||
{
|
{
|
||||||
char buf[UINT16_MAX];
|
char buf[UINT16_MAX];
|
||||||
size_t off = 0;
|
size_t off = 0;
|
||||||
@ -512,6 +520,8 @@ more:
|
|||||||
*ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
|
*ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Received signature\n");
|
||||||
*ec = TALER_EC_NONE;
|
*ec = TALER_EC_NONE;
|
||||||
finished = true;
|
finished = true;
|
||||||
ds.cipher = TALER_DENOMINATION_RSA;
|
ds.cipher = TALER_DENOMINATION_RSA;
|
||||||
@ -531,10 +541,14 @@ more:
|
|||||||
(const struct TALER_CRYPTO_SignFailure *) buf;
|
(const struct TALER_CRYPTO_SignFailure *) buf;
|
||||||
|
|
||||||
*ec = (enum TALER_ErrorCode) ntohl (sf->ec);
|
*ec = (enum TALER_ErrorCode) ntohl (sf->ec);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Signing failed!\n");
|
||||||
finished = true;
|
finished = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TALER_HELPER_RSA_MT_AVAIL:
|
case TALER_HELPER_RSA_MT_AVAIL:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Received new key!\n");
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
handle_mt_avail (dh,
|
handle_mt_avail (dh,
|
||||||
hdr))
|
hdr))
|
||||||
@ -546,6 +560,8 @@ more:
|
|||||||
}
|
}
|
||||||
break; /* while(1) loop ensures we recvfrom() again */
|
break; /* while(1) loop ensures we recvfrom() again */
|
||||||
case TALER_HELPER_RSA_MT_PURGE:
|
case TALER_HELPER_RSA_MT_PURGE:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Received revocation!\n");
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
handle_mt_purge (dh,
|
handle_mt_purge (dh,
|
||||||
hdr))
|
hdr))
|
||||||
|
@ -78,7 +78,7 @@ TES_transmit_raw (int sock,
|
|||||||
size_t end,
|
size_t end,
|
||||||
const void *pos)
|
const void *pos)
|
||||||
{
|
{
|
||||||
ssize_t off = 0;
|
size_t off = 0;
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Sending message of length %u\n",
|
"Sending message of length %u\n",
|
||||||
|
@ -388,6 +388,11 @@ handle_sign_request (struct TES_Client *client,
|
|||||||
GNUNET_YES));
|
GNUNET_YES));
|
||||||
ret = TES_transmit (client->csock,
|
ret = TES_transmit (client->csock,
|
||||||
&sr->header);
|
&sr->header);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Sent RSA signature after %s\n",
|
||||||
|
GNUNET_STRINGS_relative_time_to_string (
|
||||||
|
GNUNET_TIME_absolute_get_duration (now),
|
||||||
|
GNUNET_YES));
|
||||||
GNUNET_free (sr);
|
GNUNET_free (sr);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user