Merge branch 'master' into age_restriction

This commit is contained in:
Özgür Kesim 2021-12-07 13:20:32 +01:00
commit 83ce1b7c3d
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
20 changed files with 792 additions and 524 deletions

View File

@ -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,

View File

@ -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 ();

View File

@ -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;

View File

@ -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 */
} }

View File

@ -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);
} }

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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"

View File

@ -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,

View File

@ -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))

View File

@ -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",

View File

@ -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;
} }