Compare commits
10 Commits
f2d7e172d1
...
6f1693c334
Author | SHA1 | Date | |
---|---|---|---|
6f1693c334 | |||
|
7dcd217b60 | ||
|
f3629bdafc | ||
|
6ba9f934c4 | ||
|
bb6c11ad9e | ||
|
19de0f604f | ||
|
67de20d26e | ||
|
c0d2af8a49 | ||
|
e61a53806e | ||
|
ec45eaae18 |
@ -1866,8 +1866,8 @@ run (void *cls,
|
||||
MHD_OPTION_CONNECTION_TIMEOUT,
|
||||
connection_timeout,
|
||||
(0 == allow_address_reuse)
|
||||
? MHD_OPTION_END
|
||||
: MHD_OPTION_LISTENING_ADDRESS_REUSE,
|
||||
? MHD_OPTION_END
|
||||
: MHD_OPTION_LISTENING_ADDRESS_REUSE,
|
||||
(unsigned int) allow_address_reuse,
|
||||
MHD_OPTION_END);
|
||||
if (NULL == mhd)
|
||||
|
@ -95,6 +95,9 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
|
||||
NULL);
|
||||
return qs;
|
||||
}
|
||||
// FIXME: why do we even return the transaction
|
||||
// history here!? This is a coin with multiple
|
||||
// associated denominations, after all...
|
||||
*mhd_ret
|
||||
= TEH_RESPONSE_reply_coin_insufficient_funds (
|
||||
connection,
|
||||
|
@ -139,11 +139,12 @@ struct DepositContext
|
||||
|
||||
|
||||
/**
|
||||
* Check if /deposit is already in the database. 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. We do return a "hard" error also if we found the deposit in the
|
||||
* database and generated a regular response.
|
||||
* 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
|
||||
@ -151,15 +152,24 @@ struct DepositContext
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
deposit_precheck (void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
MHD_RESULT *mhd_ret)
|
||||
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 deposit_fee;
|
||||
struct TALER_Amount spent;
|
||||
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,
|
||||
deposit,
|
||||
&deposit_fee,
|
||||
@ -196,51 +206,8 @@ deposit_precheck (void *cls,
|
||||
deposit->wire_deadline,
|
||||
&deposit->merchant_pub,
|
||||
&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_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 */
|
||||
spent = deposit->amount_with_fee;
|
||||
@ -412,22 +379,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
||||
TALER_merchant_wire_signature_hash (dc.payto_uri,
|
||||
&deposit.wire_salt,
|
||||
&dc.h_wire);
|
||||
/* Check for idempotency: did we get this request before? */
|
||||
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 */
|
||||
dc.exchange_timestamp = GNUNET_TIME_absolute_get ();
|
||||
|
@ -2110,6 +2110,8 @@ TEH_keys_denomination_by_hash2 (
|
||||
&h_denom_pub->hash);
|
||||
if (NULL == dk)
|
||||
{
|
||||
if (NULL == conn)
|
||||
return NULL;
|
||||
*mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
|
||||
h_denom_pub);
|
||||
return NULL;
|
||||
@ -2388,8 +2390,8 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
|
||||
MHD_HTTP_OK,
|
||||
(MHD_YES ==
|
||||
TALER_MHD_can_compress (rc->connection))
|
||||
? krd->response_compressed
|
||||
: krd->response_uncompressed);
|
||||
? krd->response_compressed
|
||||
: krd->response_uncompressed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,16 +311,6 @@ melt_transaction (void *cls,
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
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 */
|
||||
qs = TEH_plugin->get_melt_index (TEH_plugin->cls,
|
||||
&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;
|
||||
|
||||
@ -513,10 +518,6 @@ check_for_denomination_key (struct MHD_Connection *connection,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"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)
|
||||
{
|
||||
struct GNUNET_TIME_Absolute now;
|
||||
@ -531,11 +532,23 @@ check_for_denomination_key (struct MHD_Connection *connection,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
||||
"MELT");
|
||||
}
|
||||
else
|
||||
/* Minor optimization: no need to run the
|
||||
"ensure_coin_known" part of the transaction */
|
||||
rmc->coin_is_dirty = true;
|
||||
/* sanity check */
|
||||
if (0 !=
|
||||
GNUNET_memcmp (&denom_hash,
|
||||
&rmc->refresh_session.coin.denom_pub_hash))
|
||||
{
|
||||
/* Minor optimization: no need to run the
|
||||
"ensure_coin_known" part of the transaction */
|
||||
rmc->coin_is_dirty = true;
|
||||
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 */
|
||||
}
|
||||
|
@ -235,11 +235,13 @@ reserve_history_transaction (void *cls,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct ReserveHistoryContext *rsc = cls;
|
||||
struct TALER_Amount balance;
|
||||
|
||||
(void) connection;
|
||||
(void) mhd_ret;
|
||||
return TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||
&rsc->reserve_pub,
|
||||
&balance,
|
||||
&rsc->rh);
|
||||
}
|
||||
|
||||
|
@ -33,14 +33,6 @@
|
||||
#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
|
||||
* message that we have insufficient funds for the
|
||||
@ -99,21 +91,6 @@ struct WithdrawContext
|
||||
*/
|
||||
struct TALER_WithdrawRequestPS wsrd;
|
||||
|
||||
/**
|
||||
* Value of the coin plus withdraw fee.
|
||||
*/
|
||||
struct TALER_Amount amount_required;
|
||||
|
||||
/**
|
||||
* Hash of the denomination public key.
|
||||
*/
|
||||
struct TALER_DenominationHash denom_pub_hash;
|
||||
|
||||
/**
|
||||
* Signature over the request.
|
||||
*/
|
||||
struct TALER_ReserveSignatureP signature;
|
||||
|
||||
/**
|
||||
* Blinded planchet.
|
||||
*/
|
||||
@ -134,39 +111,9 @@ struct WithdrawContext
|
||||
*/
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
|
||||
/**
|
||||
* Set to true if the operation was denied due to
|
||||
* failing @e kyc checks.
|
||||
*/
|
||||
bool kyc_denied;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function called with another amount that was
|
||||
* already withdrawn. Accumulates all amounts in
|
||||
* @a cls.
|
||||
*
|
||||
* @param[in,out] cls a `struct TALER_Amount`
|
||||
* @param val value to add to @a cls
|
||||
*/
|
||||
static void
|
||||
accumulate_withdraws (void *cls,
|
||||
const struct TALER_Amount *val)
|
||||
{
|
||||
struct TALER_Amount *acc = cls;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_is_valid (acc))
|
||||
return; /* ignore */
|
||||
GNUNET_break (0 <=
|
||||
TALER_amount_add (acc,
|
||||
acc,
|
||||
val));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function implementing withdraw transaction. Runs the
|
||||
* transaction logic; IF it returns a non-error code, the transaction
|
||||
@ -175,15 +122,8 @@ accumulate_withdraws (void *cls,
|
||||
* IF it returns the soft error code, the function MAY be called again
|
||||
* to retry and MUST not queue a MHD response.
|
||||
*
|
||||
* Note that "wc->collectable.sig" may already be set before entering
|
||||
* this function, either because OPTIMISTIC_SIGN was used and we signed
|
||||
* 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.
|
||||
* Note that "wc->collectable.sig" is set before entering this function as we
|
||||
* signed before entering the transaction.
|
||||
*
|
||||
* @param cls a `struct WithdrawContext *`
|
||||
* @param connection MHD request which triggered the transaction
|
||||
@ -197,71 +137,34 @@ withdraw_transaction (void *cls,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct WithdrawContext *wc = cls;
|
||||
struct TALER_EXCHANGEDB_Reserve r;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct TALER_BlindedDenominationSignature denom_sig;
|
||||
bool found = false;
|
||||
bool balance_ok = false;
|
||||
uint64_t reserve_uuid;
|
||||
struct GNUNET_TIME_Absolute now;
|
||||
|
||||
#if OPTIMISTIC_SIGN
|
||||
/* store away optimistic signature to protect
|
||||
it from being overwritten by get_withdraw_info */
|
||||
denom_sig = wc->collectable.sig;
|
||||
memset (&wc->collectable.sig,
|
||||
0,
|
||||
sizeof (wc->collectable.sig));
|
||||
#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;
|
||||
}
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(void) GNUNET_TIME_round_abs (&now);
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Asked to withdraw from %s amount of %s\n",
|
||||
TALER_B2S (&wc->wsrd.reserve_pub),
|
||||
TALER_amount2s (&wc->amount_required));
|
||||
/* Don't sign again if we have already signed the coin */
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
||||
{
|
||||
/* Toss out the optimistic signature, we got another one from the DB;
|
||||
optimization trade-off loses in this case: we unnecessarily computed
|
||||
a signature :-( */
|
||||
#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);
|
||||
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
|
||||
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
||||
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
|
||||
&wc->collectable,
|
||||
now,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&wc->kyc,
|
||||
&reserve_uuid);
|
||||
if (0 > qs)
|
||||
{
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"reserves");
|
||||
"do_withdraw");
|
||||
return qs;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
if (! found)
|
||||
{
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
@ -269,29 +172,29 @@ withdraw_transaction (void *cls,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if (0 < TALER_amount_cmp (&wc->amount_required,
|
||||
&r.balance))
|
||||
if (! balance_ok)
|
||||
{
|
||||
struct TALER_EXCHANGEDB_ReserveHistory *rh;
|
||||
struct TALER_Amount balance;
|
||||
|
||||
TEH_plugin->rollback (TEH_plugin->cls);
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->start (TEH_plugin->cls,
|
||||
"get_reserve_history on insufficient balance"))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
if (NULL != mhd_ret)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_START_FAILED,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
/* The reserve does not have the required amount (actual
|
||||
* amount + withdraw fee) */
|
||||
#if GNUNET_EXTRA_LOGGING
|
||||
{
|
||||
char *amount_required;
|
||||
char *r_balance;
|
||||
|
||||
amount_required = TALER_amount_to_string (&wc->amount_required);
|
||||
r_balance = TALER_amount_to_string (&r.balance);
|
||||
TALER_LOG_DEBUG ("Asked %s over a reserve worth %s\n",
|
||||
amount_required,
|
||||
r_balance);
|
||||
GNUNET_free (amount_required);
|
||||
GNUNET_free (r_balance);
|
||||
}
|
||||
#endif
|
||||
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
|
||||
&wc->wsrd.reserve_pub,
|
||||
&balance,
|
||||
&rh);
|
||||
if (NULL == rh)
|
||||
{
|
||||
@ -303,41 +206,41 @@ withdraw_transaction (void *cls,
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
*mhd_ret = reply_withdraw_insufficient_funds (connection,
|
||||
&r.balance,
|
||||
&balance,
|
||||
rh);
|
||||
TEH_plugin->free_reserve_history (TEH_plugin->cls,
|
||||
rh);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC status is %s for %s\n",
|
||||
wc->kyc.ok ? "ok" : "missing",
|
||||
TALER_B2S (&r.pub));
|
||||
if ( (! wc->kyc.ok) &&
|
||||
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(! wc->kyc.ok) &&
|
||||
(TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
|
||||
{
|
||||
/* Wallet-to-wallet payments _always_ require KYC */
|
||||
wc->kyc_denied = true;
|
||||
return qs;
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if ( (! wc->kyc.ok) &&
|
||||
(TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(! wc->kyc.ok) &&
|
||||
(TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
|
||||
(! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
|
||||
{
|
||||
/* Withdraws require KYC if above threshold */
|
||||
struct TALER_Amount acc;
|
||||
enum GNUNET_DB_QueryStatus qs2;
|
||||
bool below_limit;
|
||||
|
||||
acc = wc->amount_required;
|
||||
qs2 = TEH_plugin->select_withdraw_amounts_by_account (
|
||||
qs2 = TEH_plugin->do_withdraw_limit_check (
|
||||
TEH_plugin->cls,
|
||||
&wc->wsrd.reserve_pub,
|
||||
TEH_kyc_config.withdraw_period,
|
||||
&accumulate_withdraws,
|
||||
&acc);
|
||||
reserve_uuid,
|
||||
GNUNET_TIME_absolute_subtract (now,
|
||||
TEH_kyc_config.withdraw_period),
|
||||
&TEH_kyc_config.withdraw_limit,
|
||||
&below_limit);
|
||||
if (0 > qs2)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
|
||||
@ -345,74 +248,64 @@ withdraw_transaction (void *cls,
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"withdraw details");
|
||||
"do_withdraw_limit_check");
|
||||
return qs2;
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_is_valid (&acc))
|
||||
if (! below_limit)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
*mhd_ret = TALER_MHD_reply_with_ec (connection,
|
||||
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Amount withdrawn so far is %s\n",
|
||||
TALER_amount2s (&acc));
|
||||
if (1 == /* 1: acc > withdraw_limit */
|
||||
TALER_amount_cmp (&acc,
|
||||
&TEH_kyc_config.withdraw_limit))
|
||||
{
|
||||
wc->kyc_denied = true;
|
||||
return qs;
|
||||
}
|
||||
}
|
||||
|
||||
/* Balance is good, 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,
|
||||
wc->blinded_msg,
|
||||
wc->blinded_msg_len,
|
||||
&ec);
|
||||
if (TALER_EC_NONE != ec)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
*mhd_ret = TALER_MHD_reply_with_ec (connection,
|
||||
ec,
|
||||
NULL);
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
|
||||
wc->collectable.amount_with_fee = wc->amount_required;
|
||||
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
|
||||
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
|
||||
wc->collectable.reserve_sig = wc->signature;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Persisting withdraw from %s over %s\n",
|
||||
TALER_B2S (&r.pub),
|
||||
TALER_amount2s (&wc->amount_required));
|
||||
qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
|
||||
&wc->collectable);
|
||||
return qs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the @a rc is replayed and we already have an
|
||||
* answer. If so, replay the existing answer and return the
|
||||
* HTTP response.
|
||||
*
|
||||
* @param rc request context
|
||||
* @param[in,out] wc parsed request data
|
||||
* @param[out] mret HTTP status, set if we return true
|
||||
* @return true if the request is idempotent with an existing request
|
||||
* false if we did not find the request in the DB and did not set @a mret
|
||||
*/
|
||||
static bool
|
||||
check_request_idempotent (struct TEH_RequestContext *rc,
|
||||
struct WithdrawContext *wc,
|
||||
MHD_RESULT *mret)
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
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_STORE_FAILED,
|
||||
"withdraw details");
|
||||
return qs;
|
||||
*mret = TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"get_withdraw_info");
|
||||
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,
|
||||
&wc.blinded_msg_len),
|
||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||
&wc.signature),
|
||||
&wc.collectable.reserve_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
||||
&wc.denom_pub_hash),
|
||||
&wc.collectable.denom_pub_hash),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
enum TALER_ErrorCode ec;
|
||||
@ -463,12 +356,39 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
{
|
||||
MHD_RESULT mret;
|
||||
struct GNUNET_TIME_Absolute now;
|
||||
struct TEH_KeyStateHandle *ksh;
|
||||
|
||||
dk = TEH_keys_denomination_by_hash (&wc.denom_pub_hash,
|
||||
rc->connection,
|
||||
&mret);
|
||||
ksh = TEH_keys_get_state ();
|
||||
if (NULL == ksh)
|
||||
{
|
||||
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 (! 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);
|
||||
return mret;
|
||||
}
|
||||
@ -481,13 +401,20 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(void) GNUNET_TIME_round_abs (&now);
|
||||
/* This denomination is past the expiration time for withdraws */
|
||||
if (! check_request_idempotent (rc,
|
||||
&wc,
|
||||
&mret))
|
||||
{
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
||||
"WITHDRAW");
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
|
||||
"WITHDRAW");
|
||||
return mret;
|
||||
}
|
||||
if (GNUNET_TIME_absolute_is_future (dk->meta.start))
|
||||
{
|
||||
@ -495,11 +422,12 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(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);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.denom_pub_hash,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
|
||||
"WITHDRAW");
|
||||
@ -511,19 +439,26 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(void) GNUNET_TIME_round_abs (&now);
|
||||
/* This denomination has been revoked */
|
||||
if (! check_request_idempotent (rc,
|
||||
&wc,
|
||||
&mret))
|
||||
{
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.collectable.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
|
||||
"WITHDRAW");
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||
rc->connection,
|
||||
&wc.denom_pub_hash,
|
||||
now,
|
||||
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
|
||||
"WITHDRAW");
|
||||
return mret;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (0 >
|
||||
TALER_amount_add (&wc.amount_required,
|
||||
TALER_amount_add (&wc.collectable.amount_with_fee,
|
||||
&dk->meta.value,
|
||||
&dk->meta.fee_withdraw))
|
||||
{
|
||||
@ -534,7 +469,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
NULL);
|
||||
}
|
||||
TALER_amount_hton (&wc.wsrd.amount_with_fee,
|
||||
&wc.amount_required);
|
||||
&wc.collectable.amount_with_fee);
|
||||
}
|
||||
|
||||
/* verify signature! */
|
||||
@ -543,15 +478,16 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
wc.wsrd.purpose.purpose
|
||||
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
||||
wc.wsrd.h_denomination_pub
|
||||
= wc.denom_pub_hash;
|
||||
= wc.collectable.denom_pub_hash;
|
||||
TALER_coin_ev_hash (wc.blinded_msg,
|
||||
wc.blinded_msg_len,
|
||||
&wc.wsrd.h_coin_envelope);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
||||
&wc.wsrd,
|
||||
&wc.signature.eddsa_signature,
|
||||
&wc.wsrd.reserve_pub.eddsa_pub))
|
||||
GNUNET_CRYPTO_eddsa_verify (
|
||||
TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
||||
&wc.wsrd,
|
||||
&wc.collectable.reserve_sig.eddsa_signature,
|
||||
&wc.wsrd.reserve_pub.eddsa_pub))
|
||||
{
|
||||
TALER_LOG_WARNING (
|
||||
"Client supplied invalid signature for withdraw request\n");
|
||||
@ -562,11 +498,10 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#if OPTIMISTIC_SIGN
|
||||
/* Sign before transaction! */
|
||||
ec = TALER_EC_NONE;
|
||||
wc.collectable.sig
|
||||
= TEH_keys_denomination_sign (&wc.denom_pub_hash,
|
||||
= TEH_keys_denomination_sign (&wc.collectable.denom_pub_hash,
|
||||
wc.blinded_msg,
|
||||
wc.blinded_msg_len,
|
||||
&ec);
|
||||
@ -578,10 +513,8 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
ec,
|
||||
NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* run transaction and sign (if not optimistically signed before) */
|
||||
wc.kyc_denied = false;
|
||||
{
|
||||
MHD_RESULT mhd_ret;
|
||||
|
||||
@ -603,16 +536,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
/* Clean up and send back final response */
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
|
||||
if (wc.kyc_denied)
|
||||
{
|
||||
TALER_blinded_denom_sig_free (&wc.collectable.sig);
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
rc->connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc.kyc.payment_target_uuid));
|
||||
}
|
||||
|
||||
{
|
||||
MHD_RESULT ret;
|
||||
|
||||
|
1
src/exchangedb/.gitignore
vendored
1
src/exchangedb/.gitignore
vendored
@ -4,3 +4,4 @@ test-exchangedb-fees
|
||||
test-exchangedb-postgres
|
||||
test-exchangedb-signkeys
|
||||
test-perf-taler-exchangedb
|
||||
bench-db-postgres
|
||||
|
@ -379,7 +379,7 @@ run (void *cls)
|
||||
|
||||
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
TOTAL);
|
||||
if (! bm_select ((0 == f)? conn2 : conn,
|
||||
if (! bm_select ((0 == f) ? conn2 : conn,
|
||||
j))
|
||||
{
|
||||
GNUNET_PQ_disconnect (conn);
|
||||
@ -422,7 +422,7 @@ run (void *cls)
|
||||
|
||||
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
TOTAL);
|
||||
if (! bhm_select ((0 == f)? conn2 : conn,
|
||||
if (! bhm_select ((0 == f) ? conn2 : conn,
|
||||
j))
|
||||
{
|
||||
GNUNET_PQ_disconnect (conn);
|
||||
@ -465,7 +465,7 @@ run (void *cls)
|
||||
|
||||
j = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
TOTAL);
|
||||
if (! bem_select ((0 == f)? conn2 : conn,
|
||||
if (! bem_select ((0 == f) ? conn2 : conn,
|
||||
j))
|
||||
{
|
||||
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 denominations CASCADE;
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw(bigint,integer,bytea,bytea,bytea,bytea,bytea,bigint,bigint) ;
|
||||
|
||||
DROP FUNCTION IF EXISTS exchange_do_withdraw_limit_check(bigint,bigint,bigint,int) ;
|
||||
|
||||
|
||||
-- And we're out of here...
|
||||
COMMIT;
|
||||
|
@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS wire_targets
|
||||
,PRIMARY KEY (h_payto)
|
||||
);
|
||||
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
|
||||
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
|
||||
@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS reserves_in
|
||||
COMMENT ON TABLE reserves_in
|
||||
IS 'list of transfers of funds into the reserves, one per incoming wire transfer';
|
||||
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
|
||||
ON reserves_in
|
||||
(exchange_account_section
|
||||
@ -263,7 +263,7 @@ CREATE TABLE IF NOT EXISTS signkey_revocations
|
||||
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
|
||||
);
|
||||
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
|
||||
@ -301,6 +301,12 @@ CREATE TABLE IF NOT EXISTS refresh_commitments
|
||||
);
|
||||
COMMENT ON TABLE refresh_commitments
|
||||
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
|
||||
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
|
||||
);
|
||||
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
|
||||
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
|
||||
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
|
||||
ON recoup
|
||||
@ -552,10 +562,14 @@ CREATE TABLE IF NOT EXISTS recoup_refresh
|
||||
,timestamp INT8 NOT NULL
|
||||
,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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -596,6 +596,34 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
"lock_withdraw",
|
||||
"LOCK TABLE reserves_out;",
|
||||
0),
|
||||
/* Used in #postgres_do_withdraw() to store
|
||||
the signature of a blinded coin with the blinded coin's
|
||||
details before returning it during /reserve/withdraw. We store
|
||||
the coin's denomination information (public key, signature)
|
||||
and the blinded message as well as the reserve that the coin
|
||||
is being withdrawn from and the signature of the message
|
||||
authorizing the withdrawal. */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"call_withdraw",
|
||||
"SELECT "
|
||||
" reserve_found"
|
||||
",balance_ok"
|
||||
",kycok AS kyc_ok"
|
||||
",ruuid AS reserve_uuid"
|
||||
",account_uuid AS payment_target_uuid"
|
||||
" FROM exchange_do_withdraw"
|
||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
|
||||
9),
|
||||
/* Used in #postgres_do_withdraw_limit_check() to check
|
||||
if the withdrawals remain below the limit under which
|
||||
KYC is not required. */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"call_withdraw_limit_check",
|
||||
"SELECT "
|
||||
" below_limit"
|
||||
" FROM exchange_do_withdraw_limit_check"
|
||||
" ($1,$2,$3,$4);",
|
||||
4),
|
||||
/* Used in #postgres_insert_withdraw_info() to store
|
||||
the signature of a blinded coin with the blinded coin's
|
||||
details before returning it during /reserve/withdraw. We store
|
||||
@ -753,7 +781,8 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
",denom_sig"
|
||||
") SELECT $1, denominations_serial, $3 "
|
||||
" FROM denominations"
|
||||
" WHERE denom_pub_hash=$2;",
|
||||
" WHERE denom_pub_hash=$2"
|
||||
" ON CONFLICT DO NOTHING;",
|
||||
3),
|
||||
|
||||
/* Used in #postgres_insert_melt() to store
|
||||
@ -3378,12 +3407,12 @@ dominations_cb_helper (void *cls,
|
||||
struct TALER_DenominationPublicKey denom_pub;
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
struct TALER_DenominationHash h_denom_pub;
|
||||
uint8_t revoked;
|
||||
bool revoked;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
|
||||
&master_sig),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("revoked",
|
||||
&revoked),
|
||||
GNUNET_PQ_result_spec_bool ("revoked",
|
||||
&revoked),
|
||||
TALER_PQ_result_spec_absolute_time ("valid_from",
|
||||
&meta.start),
|
||||
TALER_PQ_result_spec_absolute_time ("expire_withdraw",
|
||||
@ -3422,7 +3451,7 @@ dominations_cb_helper (void *cls,
|
||||
&h_denom_pub,
|
||||
&meta,
|
||||
&master_sig,
|
||||
(0 != revoked));
|
||||
revoked);
|
||||
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_end
|
||||
};
|
||||
uint8_t ok8;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
|
||||
&reserve->balance),
|
||||
@ -3787,19 +3815,16 @@ postgres_reserves_get (void *cls,
|
||||
&reserve->gc),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"reserves_get_with_kyc",
|
||||
params,
|
||||
rs);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
kyc->ok = (0 != ok8);
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"reserves_get_with_kyc",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -3874,23 +3899,19 @@ postgres_get_kyc_status (void *cls,
|
||||
GNUNET_PQ_query_param_string (payto_uri),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||
kyc->ok = (0 != ok8);
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -3914,24 +3935,20 @@ postgres_select_kyc_status (void *cls,
|
||||
GNUNET_PQ_query_param_uint64 (&payment_target_uuid),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("h_payto",
|
||||
h_payto),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"select_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_UNKNOWN;
|
||||
kyc->ok = (0 != ok8);
|
||||
kyc->payment_target_uuid = payment_target_uuid;
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"select_kyc_status",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -3962,12 +3979,11 @@ inselect_account_kyc_status (
|
||||
GNUNET_PQ_query_param_auto_from_type (&h_payto),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t ok8 = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("wire_target_serial_id",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
@ -3998,10 +4014,6 @@ inselect_account_kyc_status (
|
||||
return GNUNET_DB_STATUS_SOFT_ERROR;
|
||||
kyc->ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
kyc->ok = (0 != ok8);
|
||||
}
|
||||
}
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_BALANCE;
|
||||
return qs;
|
||||
@ -4478,86 +4490,104 @@ postgres_get_withdraw_info (
|
||||
|
||||
|
||||
/**
|
||||
* Store collectable bit coin under the corresponding
|
||||
* hash of the blinded message.
|
||||
* Perform withdraw operation, checking for sufficient balance
|
||||
* and possibly persisting the withdrawal details.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param collectable corresponding collectable coin (blind signature)
|
||||
* if a coin is found
|
||||
* @param now current time (rounded)
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] kyc_ok set to true if the kyc status of the reserve is satisfied
|
||||
* @param[out] reserve_uuid set to the UUID of the reserve
|
||||
* @return query execution status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_insert_withdraw_info (
|
||||
postgres_do_withdraw (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Absolute now,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc,
|
||||
uint64_t *reserve_uuid)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct TALER_EXCHANGEDB_Reserve reserve;
|
||||
struct GNUNET_TIME_Absolute now;
|
||||
struct GNUNET_TIME_Absolute gc;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
|
||||
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->denom_pub_hash),
|
||||
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
|
||||
GNUNET_PQ_query_param_auto_from_type (&collectable->h_coin_envelope),
|
||||
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
||||
TALER_PQ_query_param_absolute_time (&now),
|
||||
TALER_PQ_query_param_amount (&collectable->amount_with_fee),
|
||||
TALER_PQ_query_param_absolute_time (&gc),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_bool ("reserve_found",
|
||||
found),
|
||||
GNUNET_PQ_result_spec_bool ("balance_ok",
|
||||
balance_ok),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_uint64 ("reserve_uuid",
|
||||
reserve_uuid),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
now = GNUNET_TIME_absolute_get ();
|
||||
(void) GNUNET_TIME_round_abs (&now);
|
||||
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"insert_withdraw_info",
|
||||
params);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
return qs;
|
||||
}
|
||||
|
||||
/* update reserve balance */
|
||||
reserve.pub = collectable->reserve_pub;
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
(qs = reserves_get_internal (pg,
|
||||
&reserve)))
|
||||
{
|
||||
/* Should have been checked before we got here... */
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||
return qs;
|
||||
}
|
||||
if (0 >
|
||||
TALER_amount_subtract (&reserve.balance,
|
||||
&reserve.balance,
|
||||
&collectable->amount_with_fee))
|
||||
{
|
||||
/* The reserve history was checked to make sure there is enough of a balance
|
||||
left before we tried this; however, concurrent operations may have changed
|
||||
the situation by now, causing us to fail here. As reserves can no longer
|
||||
be topped up, retrying should not help either. */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Withdrawal from reserve `%s' refused due to insufficient balance.\n",
|
||||
TALER_B2S (&collectable->reserve_pub));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
gc = GNUNET_TIME_absolute_add (now,
|
||||
pg->legal_reserve_expiration_time);
|
||||
reserve.gc = GNUNET_TIME_absolute_max (gc,
|
||||
reserve.gc);
|
||||
(void) GNUNET_TIME_round_abs (&reserve.gc);
|
||||
qs = reserves_update (pg,
|
||||
&reserve);
|
||||
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
return qs;
|
||||
(void) GNUNET_TIME_round_abs (&gc);
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_withdraw",
|
||||
params,
|
||||
rs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that reserve remains below threshold for KYC
|
||||
* checks after withdraw operation.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_uuid reserve to check
|
||||
* @param withdraw_start starting point to accumulate from
|
||||
* @param upper_limit maximum amount allowed
|
||||
* @param[out] below_limit set to true if the limit was not exceeded
|
||||
* @return query execution status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_do_withdraw_limit_check (
|
||||
void *cls,
|
||||
uint64_t reserve_uuid,
|
||||
struct GNUNET_TIME_Absolute withdraw_start,
|
||||
const struct TALER_Amount *upper_limit,
|
||||
bool *below_limit)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_uint64 (&reserve_uuid),
|
||||
TALER_PQ_query_param_absolute_time (&withdraw_start),
|
||||
TALER_PQ_query_param_amount (upper_limit),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_bool ("below_limit",
|
||||
below_limit),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_withdraw_limit_check",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -4587,11 +4617,21 @@ struct ReserveHistoryContext
|
||||
*/
|
||||
struct PostgresClosure *pg;
|
||||
|
||||
/**
|
||||
* Sum of all credit transactions.
|
||||
*/
|
||||
struct TALER_Amount balance_in;
|
||||
|
||||
/**
|
||||
* Sum of all debit transactions.
|
||||
*/
|
||||
struct TALER_Amount balance_out;
|
||||
|
||||
/**
|
||||
* Set to #GNUNET_SYSERR on serious internal errors during
|
||||
* the callbacks.
|
||||
*/
|
||||
int status;
|
||||
enum GNUNET_GenericReturnValue status;
|
||||
};
|
||||
|
||||
|
||||
@ -4667,6 +4707,10 @@ add_bank_to_exchange (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_in,
|
||||
&rhc->balance_in,
|
||||
&bt->amount));
|
||||
bt->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE;
|
||||
@ -4724,6 +4768,10 @@ add_withdraw_coin (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_out,
|
||||
&rhc->balance_out,
|
||||
&cbc->amount_with_fee));
|
||||
cbc->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN;
|
||||
@ -4784,6 +4832,10 @@ add_recoup (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_in,
|
||||
&rhc->balance_in,
|
||||
&recoup->value));
|
||||
recoup->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN;
|
||||
@ -4840,6 +4892,10 @@ add_exchange_to_bank (void *cls,
|
||||
return;
|
||||
}
|
||||
}
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&rhc->balance_out,
|
||||
&rhc->balance_out,
|
||||
&closing->amount));
|
||||
closing->reserve_pub = *rhc->reserve_pub;
|
||||
tail = append_rh (rhc);
|
||||
tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK;
|
||||
@ -4854,12 +4910,14 @@ add_exchange_to_bank (void *cls,
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param[out] balance set to the reserve balance
|
||||
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_get_reserve_history (void *cls,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
struct TALER_Amount *balance,
|
||||
struct TALER_EXCHANGEDB_ReserveHistory **rhp)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
@ -4902,6 +4960,12 @@ postgres_get_reserve_history (void *cls,
|
||||
rhc.rh_tail = NULL;
|
||||
rhc.pg = pg;
|
||||
rhc.status = GNUNET_OK;
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (pg->currency,
|
||||
&rhc.balance_in));
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (pg->currency,
|
||||
&rhc.balance_out));
|
||||
qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */
|
||||
for (unsigned int i = 0; NULL != work[i].cb; i++)
|
||||
{
|
||||
@ -4927,6 +4991,10 @@ postgres_get_reserve_history (void *cls,
|
||||
}
|
||||
}
|
||||
*rhp = rhc.rh;
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_subtract (balance,
|
||||
&rhc.balance_in,
|
||||
&rhc.balance_out));
|
||||
return qs;
|
||||
}
|
||||
|
||||
@ -5308,13 +5376,12 @@ postgres_get_ready_deposit (void *cls,
|
||||
void *deposit_cb_cls)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
uint8_t kyc_override = (kyc_off) ? 1 : 0;
|
||||
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
TALER_PQ_query_param_absolute_time (&now),
|
||||
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
||||
GNUNET_PQ_query_param_uint64 (&end_shard_row),
|
||||
GNUNET_PQ_query_param_auto_from_type (&kyc_override),
|
||||
GNUNET_PQ_query_param_bool (kyc_off),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct TALER_Amount amount_with_fee;
|
||||
@ -5701,16 +5768,24 @@ postgres_ensure_coin_known (void *cls,
|
||||
&denom_pub_hash),
|
||||
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,
|
||||
"lock_known_coins",
|
||||
no_params)))
|
||||
return qs;
|
||||
#endif
|
||||
/* First, try to simply insert it */
|
||||
qs = insert_known_coin (pg,
|
||||
coin);
|
||||
switch (qs)
|
||||
{
|
||||
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 */
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_known_coin_dh",
|
||||
@ -5729,26 +5804,13 @@ postgres_ensure_coin_known (void *cls,
|
||||
GNUNET_break_op (0);
|
||||
return TALER_EXCHANGEDB_CKS_CONFLICT;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
break;
|
||||
}
|
||||
|
||||
/* if not known, insert it */
|
||||
qs = insert_known_coin (pg,
|
||||
coin);
|
||||
switch (qs)
|
||||
{
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
/* should be impossible */
|
||||
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:
|
||||
GNUNET_break (0);
|
||||
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||
break;
|
||||
}
|
||||
return TALER_EXCHANGEDB_CKS_ADDED;
|
||||
/* we should never get here */
|
||||
GNUNET_break (0);
|
||||
return TALER_EXCHANGEDB_CKS_HARD_FAIL;
|
||||
}
|
||||
|
||||
|
||||
@ -6609,7 +6671,6 @@ add_coin_deposit (void *cls,
|
||||
chc->have_deposit_or_melt = true;
|
||||
deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
|
||||
{
|
||||
uint8_t done = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
&deposit->amount_with_fee),
|
||||
@ -6636,7 +6697,7 @@ add_coin_deposit (void *cls,
|
||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||
&serial_id),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||
&done),
|
||||
&deposit->done),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
@ -6650,7 +6711,6 @@ add_coin_deposit (void *cls,
|
||||
chc->failed = true;
|
||||
return;
|
||||
}
|
||||
deposit->done = (0 != done);
|
||||
}
|
||||
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
|
||||
tl->next = chc->head;
|
||||
@ -7340,7 +7400,6 @@ postgres_lookup_transfer_by_deposit (
|
||||
/* Check if transaction exists in deposits, so that we just
|
||||
do not have a WTID yet. In that case, return without wtid
|
||||
(by setting 'pending' true). */
|
||||
uint8_t ok8 = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs2[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
||||
&wire_salt),
|
||||
@ -7349,7 +7408,7 @@ postgres_lookup_transfer_by_deposit (
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&ok8),
|
||||
&kyc->ok),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
amount_with_fee),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
||||
@ -7377,7 +7436,6 @@ postgres_lookup_transfer_by_deposit (
|
||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||
}
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||
kyc->ok = (0 != ok8);
|
||||
return qs;
|
||||
}
|
||||
}
|
||||
@ -8164,7 +8222,7 @@ deposit_serial_helper_cb (void *cls,
|
||||
struct TALER_EXCHANGEDB_Deposit deposit;
|
||||
struct GNUNET_TIME_Absolute exchange_timestamp;
|
||||
struct TALER_DenominationPublicKey denom_pub;
|
||||
uint8_t done = 0;
|
||||
bool done;
|
||||
uint64_t rowid;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
@ -8191,8 +8249,8 @@ deposit_serial_helper_cb (void *cls,
|
||||
&deposit.wire_salt),
|
||||
GNUNET_PQ_result_spec_string ("receiver_wire_account",
|
||||
&deposit.receiver_wire_account),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_bool ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||
&rowid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -8213,7 +8271,7 @@ deposit_serial_helper_cb (void *cls,
|
||||
exchange_timestamp,
|
||||
&deposit,
|
||||
&denom_pub,
|
||||
(0 != done) ? true : false);
|
||||
done);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
if (GNUNET_OK != ret)
|
||||
break;
|
||||
@ -9783,8 +9841,8 @@ missing_wire_cb (void *cls,
|
||||
struct TALER_Amount amount;
|
||||
char *payto_uri;
|
||||
struct GNUNET_TIME_Absolute deadline;
|
||||
uint8_t tiny;
|
||||
uint8_t done;
|
||||
bool tiny;
|
||||
bool done;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
|
||||
&rowid),
|
||||
@ -9796,10 +9854,10 @@ missing_wire_cb (void *cls,
|
||||
&payto_uri),
|
||||
TALER_PQ_result_spec_absolute_time ("wire_deadline",
|
||||
&deadline),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("tiny",
|
||||
&tiny),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_bool ("tiny",
|
||||
&tiny),
|
||||
GNUNET_PQ_result_spec_bool ("done",
|
||||
&done),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
@ -9923,22 +9981,18 @@ postgres_lookup_auditor_status (
|
||||
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint8_t enabled8 = 0;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_string ("auditor_url",
|
||||
auditor_url),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("is_active",
|
||||
&enabled8),
|
||||
GNUNET_PQ_result_spec_bool ("is_active",
|
||||
enabled),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"lookup_auditor_status",
|
||||
params,
|
||||
rs);
|
||||
*enabled = (0 != enabled8);
|
||||
return qs;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"lookup_auditor_status",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
@ -9996,12 +10050,11 @@ postgres_update_auditor (void *cls,
|
||||
bool enabled)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
uint8_t enabled8 = enabled ? 1 : 0;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (auditor_pub),
|
||||
GNUNET_PQ_query_param_string (auditor_url),
|
||||
GNUNET_PQ_query_param_string (auditor_name),
|
||||
GNUNET_PQ_query_param_auto_from_type (&enabled8),
|
||||
GNUNET_PQ_query_param_bool (enabled),
|
||||
GNUNET_PQ_query_param_absolute_time (&change_date),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
@ -10091,10 +10144,9 @@ postgres_update_wire (void *cls,
|
||||
bool enabled)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
uint8_t enabled8 = enabled ? 1 : 0;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_string (payto_uri),
|
||||
GNUNET_PQ_query_param_auto_from_type (&enabled8),
|
||||
GNUNET_PQ_query_param_bool (enabled),
|
||||
GNUNET_PQ_query_param_absolute_time (&change_date),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
@ -11767,7 +11819,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
||||
plugin->get_latest_reserve_in_reference =
|
||||
&postgres_get_latest_reserve_in_reference;
|
||||
plugin->get_withdraw_info = &postgres_get_withdraw_info;
|
||||
plugin->insert_withdraw_info = &postgres_insert_withdraw_info;
|
||||
// plugin->insert_withdraw_info = &postgres_insert_withdraw_info;
|
||||
plugin->do_withdraw = &postgres_do_withdraw;
|
||||
plugin->do_withdraw_limit_check = &postgres_do_withdraw_limit_check;
|
||||
plugin->get_reserve_history = &postgres_get_reserve_history;
|
||||
plugin->select_withdraw_amounts_by_account
|
||||
= &postgres_select_withdraw_amounts_by_account;
|
||||
|
@ -1659,10 +1659,26 @@ run (void *cls)
|
||||
cbc.reserve_pub = reserve_pub;
|
||||
cbc.amount_with_fee = value;
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (CURRENCY, &cbc.withdraw_fee));
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->insert_withdraw_info (plugin->cls,
|
||||
&cbc));
|
||||
TALER_amount_set_zero (CURRENCY,
|
||||
&cbc.withdraw_fee));
|
||||
{
|
||||
bool found;
|
||||
bool balance_ok;
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
uint64_t ruuid;
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->do_withdraw (plugin->cls,
|
||||
&cbc,
|
||||
now,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&kyc,
|
||||
&ruuid));
|
||||
GNUNET_assert (found);
|
||||
GNUNET_assert (balance_ok);
|
||||
GNUNET_assert (! kyc.ok);
|
||||
}
|
||||
FAILIF (GNUNET_OK !=
|
||||
check_reserve (&reserve_pub,
|
||||
value.value,
|
||||
@ -1780,9 +1796,14 @@ run (void *cls)
|
||||
value.currency));
|
||||
|
||||
result = 7;
|
||||
qs = plugin->get_reserve_history (plugin->cls,
|
||||
&reserve_pub,
|
||||
&rh);
|
||||
{
|
||||
struct TALER_Amount balance;
|
||||
|
||||
qs = plugin->get_reserve_history (plugin->cls,
|
||||
&reserve_pub,
|
||||
&balance,
|
||||
&rh);
|
||||
}
|
||||
FAILIF (0 > qs);
|
||||
FAILIF (NULL == rh);
|
||||
rh_head = rh;
|
||||
|
@ -496,7 +496,7 @@ struct TALER_EXCHANGEDB_ClosingTransfer
|
||||
struct TALER_ReservePublicKeyP reserve_pub;
|
||||
|
||||
/**
|
||||
* Amount that was transferred to the exchange.
|
||||
* Amount that was transferred from the exchange.
|
||||
*/
|
||||
struct TALER_Amount amount;
|
||||
|
||||
@ -2512,23 +2512,70 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @return statement execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*insert_withdraw_info)(
|
||||
(*insert_withdraw_infoXX)(
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
|
||||
|
||||
|
||||
/**
|
||||
* Perform withdraw operation, checking for sufficient balance
|
||||
* and possibly persisting the withdrawal details.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param collectable corresponding collectable coin (blind signature)
|
||||
* if a coin is found
|
||||
* @param now current time (rounded)
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] kyc set to the KYC status of the reserve
|
||||
* @param[out] reserve_uuid set to the UUID of the reserve
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*do_withdraw)(
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Absolute now,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc_ok,
|
||||
uint64_t *reserve_uuid);
|
||||
|
||||
|
||||
/**
|
||||
* Check that reserve remains below threshold for KYC
|
||||
* checks after withdraw operation.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_uuid reserve to check
|
||||
* @param withdraw_start starting point to accumulate from
|
||||
* @param upper_limit maximum amount allowed
|
||||
* @param[out] below_limit set to true if the limit was not exceeded
|
||||
* @return query execution status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*do_withdraw_limit_check)(
|
||||
void *cls,
|
||||
uint64_t reserve_uuid,
|
||||
struct GNUNET_TIME_Absolute withdraw_start,
|
||||
const struct TALER_Amount *upper_limit,
|
||||
bool *below_limit);
|
||||
|
||||
|
||||
/**
|
||||
* Get all of the transaction history associated with the specified
|
||||
* reserve.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param[out] balance set to the reserve balance
|
||||
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
|
||||
* @return transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*get_reserve_history)(void *cls,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
struct TALER_Amount *balance,
|
||||
struct TALER_EXCHANGEDB_ReserveHistory **rhp);
|
||||
|
||||
|
||||
|
@ -46,8 +46,9 @@ enum TALER_EXTENSION_ReturnValue
|
||||
* The default age mask represents the age groups
|
||||
* 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 << \
|
||||
14 | 1 << 16 | 1 << 18 | 1 << 21)
|
||||
#define TALER_EXTENSION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 \
|
||||
<< 14 | 1 << 16 | 1 << 18 | 1 \
|
||||
<< 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,
|
||||
"RECOUP-REFRESH"))
|
||||
{
|
||||
/* This is the coin that was subjected to a recoup,
|
||||
the value being credited to the old coin. */
|
||||
struct TALER_RecoupRefreshConfirmationPS pc = {
|
||||
.purpose.size = htonl (sizeof (pc)),
|
||||
.purpose.purpose = htonl (
|
||||
@ -836,6 +838,8 @@ TALER_EXCHANGE_verify_coin_history (
|
||||
else if (0 == strcasecmp (type,
|
||||
"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 = {
|
||||
.purpose.size = htonl (sizeof (pc)),
|
||||
.purpose.purpose = htonl (
|
||||
@ -878,7 +882,7 @@ TALER_EXCHANGE_verify_coin_history (
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
add = GNUNET_YES;
|
||||
add = GNUNET_NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -911,7 +915,8 @@ TALER_EXCHANGE_verify_coin_history (
|
||||
However, for the implementation, we first *add* up all of
|
||||
these negative amounts, as we might get refunds before
|
||||
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 >
|
||||
TALER_amount_add (&rtotal,
|
||||
&rtotal,
|
||||
|
@ -629,8 +629,8 @@ TALER_EXCHANGE_deposit (
|
||||
&h_wire,
|
||||
h_contract_terms,
|
||||
(NULL != extension_details)
|
||||
? &ech
|
||||
: NULL,
|
||||
? &ech
|
||||
: NULL,
|
||||
coin_pub,
|
||||
denom_sig,
|
||||
denom_pub,
|
||||
|
@ -83,6 +83,8 @@ try_connect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
|
||||
{
|
||||
if (-1 != dh->sock)
|
||||
return GNUNET_OK;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Establishing connection!\n");
|
||||
dh->sock = socket (AF_UNIX,
|
||||
SOCK_STREAM,
|
||||
0);
|
||||
@ -103,6 +105,7 @@ try_connect (struct TALER_CRYPTO_RsaDenominationHelper *dh)
|
||||
do_disconnect (dh);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
TALER_CRYPTO_helper_rsa_poll (dh);
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
@ -153,7 +156,6 @@ TALER_CRYPTO_helper_rsa_connect (
|
||||
TALER_CRYPTO_helper_rsa_disconnect (dh);
|
||||
return NULL;
|
||||
}
|
||||
TALER_CRYPTO_helper_rsa_poll (dh);
|
||||
return dh;
|
||||
}
|
||||
|
||||
@ -397,6 +399,8 @@ TALER_CRYPTO_helper_rsa_sign (
|
||||
.cipher = TALER_DENOMINATION_INVALID
|
||||
};
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Starting signature process\n");
|
||||
if (GNUNET_OK !=
|
||||
try_connect (dh))
|
||||
{
|
||||
@ -406,6 +410,8 @@ TALER_CRYPTO_helper_rsa_sign (
|
||||
return ds;
|
||||
}
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Requesting signature\n");
|
||||
{
|
||||
char buf[sizeof (struct TALER_CRYPTO_SignRequest) + msg_size];
|
||||
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];
|
||||
size_t off = 0;
|
||||
@ -512,6 +520,8 @@ more:
|
||||
*ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
|
||||
goto end;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Received signature\n");
|
||||
*ec = TALER_EC_NONE;
|
||||
finished = true;
|
||||
ds.cipher = TALER_DENOMINATION_RSA;
|
||||
@ -531,10 +541,14 @@ more:
|
||||
(const struct TALER_CRYPTO_SignFailure *) buf;
|
||||
|
||||
*ec = (enum TALER_ErrorCode) ntohl (sf->ec);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Signing failed!\n");
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
case TALER_HELPER_RSA_MT_AVAIL:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Received new key!\n");
|
||||
if (GNUNET_OK !=
|
||||
handle_mt_avail (dh,
|
||||
hdr))
|
||||
@ -546,6 +560,8 @@ more:
|
||||
}
|
||||
break; /* while(1) loop ensures we recvfrom() again */
|
||||
case TALER_HELPER_RSA_MT_PURGE:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Received revocation!\n");
|
||||
if (GNUNET_OK !=
|
||||
handle_mt_purge (dh,
|
||||
hdr))
|
||||
|
@ -78,7 +78,7 @@ TES_transmit_raw (int sock,
|
||||
size_t end,
|
||||
const void *pos)
|
||||
{
|
||||
ssize_t off = 0;
|
||||
size_t off = 0;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Sending message of length %u\n",
|
||||
|
@ -388,6 +388,11 @@ handle_sign_request (struct TES_Client *client,
|
||||
GNUNET_YES));
|
||||
ret = TES_transmit (client->csock,
|
||||
&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);
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user