refactoring /deposit towards new transaction style (#5010)

This commit is contained in:
Christian Grothoff 2017-06-18 21:47:05 +02:00
parent d2c7ef54a7
commit 2ec1b055a0
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
9 changed files with 421 additions and 340 deletions

View File

@ -112,6 +112,79 @@ transaction_start_label: /* we will use goto for retries */ \
} while (0) } while (0)
/**
* Run a database transaction for @a connection.
* Starts a transaction and calls @a cb. Upon success,
* attempts to commit the transaction. Upon soft failures,
* retries @a cb a few times. Upon hard or persistent soft
* errors, generates an error message for @a connection.
*
* @param connection MHD connection to run @a cb for
* @param[out] set to MHD response code, if transaction failed
* @param cb callback implementing transaction logic
* @param cb_cls closure for @a cb, must be read-only!
* @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/
int
TEH_DB_run_transaction (struct MHD_Connection *connection,
int *mhd_ret,
TEH_DB_TransactionCallback cb,
void *cb_cls)
{
struct TALER_EXCHANGEDB_Session *session;
*mhd_ret = -1; /* invalid value */
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_SETUP_FAILED);
return GNUNET_SYSERR;
}
for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES; retries++)
{
enum GNUNET_DB_QueryStatus qs;
if (GNUNET_OK !=
TEH_plugin->start (TEH_plugin->cls,
session))
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_START_FAILED);
return GNUNET_SYSERR;
}
qs = cb (cb_cls,
connection,
session,
mhd_ret);
if (0 > qs)
TEH_plugin->rollback (TEH_plugin->cls,
session);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
return GNUNET_SYSERR;
if (0 <= qs)
qs = TEH_plugin->commit (TEH_plugin->cls,
session);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
*mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
TALER_EC_DB_COMMIT_FAILED_HARD);
return GNUNET_SYSERR;
}
/* make sure callback did not violate invariants! */
GNUNET_assert (-1 == *mhd_ret);
if (0 <= qs)
return GNUNET_OK;
}
TALER_LOG_WARNING ("Transaction commit failed %u times\n",
MAX_TRANSACTION_COMMIT_RETRIES);
*mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
return GNUNET_SYSERR;
}
/** /**
* Calculate the total value of all transactions performed. * Calculate the total value of all transactions performed.
* Stores @a off plus the cost of all transactions in @a tl * Stores @a off plus the cost of all transactions in @a tl
@ -119,13 +192,13 @@ transaction_start_label: /* we will use goto for retries */ \
* *
* @param tl transaction list to process * @param tl transaction list to process
* @param off offset to use as the starting value * @param off offset to use as the starting value
* @param ret where the resulting total is to be stored * @param[out] ret where the resulting total is to be stored
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/ */
static int int
calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl, TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
const struct TALER_Amount *off, const struct TALER_Amount *off,
struct TALER_Amount *ret) struct TALER_Amount *ret)
{ {
struct TALER_Amount spent = *off; struct TALER_Amount spent = *off;
struct TALER_EXCHANGEDB_TransactionList *pos; struct TALER_EXCHANGEDB_TransactionList *pos;
@ -206,146 +279,6 @@ calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
} }
/**
* Execute a deposit. The validity of the coin and signature
* have already been checked. The database must now check that
* the coin is not (double or over) spent, and execute the
* transaction (record details, generate success or failure response).
*
* @param connection the MHD connection to handle
* @param deposit information about the deposit
* @return MHD result code
*/
int
TEH_DB_execute_deposit (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Deposit *deposit)
{
struct TALER_EXCHANGEDB_Session *session;
struct TALER_EXCHANGEDB_TransactionList *tl;
struct TALER_Amount spent;
struct TALER_Amount value;
struct TALER_Amount amount_without_fee;
struct TEH_KS_StateHandle *mks;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
int ret;
enum GNUNET_DB_QueryStatus qs;
unsigned int retries = 0;
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
{
GNUNET_break (0);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_SETUP_FAILED);
}
again:
if (GNUNET_YES ==
TEH_plugin->have_deposit (TEH_plugin->cls,
session,
deposit))
{
GNUNET_assert (GNUNET_OK ==
TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee,
&deposit->deposit_fee));
return TEH_RESPONSE_reply_deposit_success (connection,
&deposit->coin.coin_pub,
&deposit->h_wire,
&deposit->h_contract_terms,
deposit->timestamp,
deposit->refund_deadline,
&deposit->merchant_pub,
&amount_without_fee);
}
/* FIXME: move the 'mks'-logic outside of _db.c? */
mks = TEH_KS_acquire ();
dki = TEH_KS_denomination_key_lookup (mks,
&deposit->coin.denom_pub,
TEH_KS_DKU_DEPOSIT);
if (NULL == dki)
{
TEH_KS_release (mks);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
}
TALER_amount_ntoh (&value,
&dki->issue.properties.value);
TEH_KS_release (mks);
START_TRANSACTION (session, connection);
/* fee for THIS transaction */
spent = deposit->amount_with_fee;
/* add cost of all previous transactions */
tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
session,
&deposit->coin.coin_pub);
if (GNUNET_OK !=
calculate_transaction_list_totals (tl,
&spent,
&spent))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
}
/* Check that cost of all transactions is smaller than
the value of the coin. */
if (0 < TALER_amount_cmp (&spent,
&value))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
tl);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return ret;
}
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
session,
deposit);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_STORE_DB_ERROR);
}
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
retries++;
TEH_plugin->rollback (TEH_plugin->cls,
session);
if (retries > 5)
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_STORE_DB_ERROR);
goto again;
}
COMMIT_TRANSACTION(session, connection);
GNUNET_assert (GNUNET_SYSERR !=
TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee,
&deposit->deposit_fee));
return TEH_RESPONSE_reply_deposit_success (connection,
&deposit->coin.coin_pub,
&deposit->h_wire,
&deposit->h_contract_terms,
deposit->timestamp,
deposit->refund_deadline,
&deposit->merchant_pub,
&amount_without_fee);
}
/** /**
* Execute a "/refund". Returns a confirmation that the refund * Execute a "/refund". Returns a confirmation that the refund
* was successful, or a failure if we are not aware of a matching * was successful, or a failure if we are not aware of a matching
@ -367,6 +300,7 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
struct TEH_KS_StateHandle *mks; struct TEH_KS_StateHandle *mks;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TALER_Amount expect_fee; struct TALER_Amount expect_fee;
enum GNUNET_DB_QueryStatus qs;
int ret; int ret;
int deposit_found; int deposit_found;
int refund_found; int refund_found;
@ -595,10 +529,10 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
tl); tl);
/* Finally, store new refund data */ /* Finally, store new refund data */
if (GNUNET_OK != qs = TEH_plugin->insert_refund (TEH_plugin->cls,
TEH_plugin->insert_refund (TEH_plugin->cls, session,
session, refund);
refund)) if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{ {
TALER_LOG_WARNING ("Failed to store /refund information in database\n"); TALER_LOG_WARNING ("Failed to store /refund information in database\n");
TEH_plugin->rollback (TEH_plugin->cls, TEH_plugin->rollback (TEH_plugin->cls,
@ -606,6 +540,11 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
return TEH_RESPONSE_reply_internal_db_error (connection, return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFUND_STORE_DB_ERROR); TALER_EC_REFUND_STORE_DB_ERROR);
} }
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
/* FIXME: #5010: retry! */
}
COMMIT_TRANSACTION (session, connection); COMMIT_TRANSACTION (session, connection);
return TEH_RESPONSE_reply_refund_success (connection, return TEH_RESPONSE_reply_refund_success (connection,
@ -1043,9 +982,9 @@ refresh_check_melt (struct MHD_Connection *connection,
session, session,
&coin_details->coin_info.coin_pub); &coin_details->coin_info.coin_pub);
if (GNUNET_OK != if (GNUNET_OK !=
calculate_transaction_list_totals (tl, TEH_DB_calculate_transaction_list_totals (tl,
&spent, &spent,
&spent)) &spent))
{ {
GNUNET_break (0); GNUNET_break (0);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
@ -2403,9 +2342,9 @@ TEH_DB_execute_payback (struct MHD_Connection *connection,
TALER_amount_get_zero (value->currency, TALER_amount_get_zero (value->currency,
&spent); &spent);
if (GNUNET_OK != if (GNUNET_OK !=
calculate_transaction_list_totals (tl, TEH_DB_calculate_transaction_list_totals (tl,
&spent, &spent,
&spent)) &spent))
{ {
GNUNET_break (0); GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls, TEH_plugin->rollback (TEH_plugin->cls,

View File

@ -24,20 +24,63 @@
#include <microhttpd.h> #include <microhttpd.h>
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
/**
* Function implementing a database transaction. 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 closure
* @param connection MHD request which triggered the transaction
* @param session database session to use
* @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!)
* @return transaction status
*/
typedef enum GNUNET_DB_QueryStatus
(*TEH_DB_TransactionCallback)(void *cls,
struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret);
/** /**
* Execute a "/deposit". The validity of the coin and signature * Run a database transaction for @a connection.
* have already been checked. The database must now check that * Starts a transaction and calls @a cb. Upon success,
* the coin is not (double or over) spent, and execute the * attempts to commit the transaction. Upon soft failures,
* transaction (record details, generate success or failure response). * retries @a cb a few times. Upon hard or persistent soft
* * errors, generates an error message for @a connection.
* @param connection the MHD connection to handle *
* @param deposit information about the deposit * @param connection MHD connection to run @a cb for
* @return MHD result code * @param[out] set to MHD response code, if transaction failed
* @param cb callback implementing transaction logic
* @param cb_cls closure for @a cb, must be read-only!
* @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/ */
int int
TEH_DB_execute_deposit (struct MHD_Connection *connection, TEH_DB_run_transaction (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Deposit *deposit); int *mhd_ret,
TEH_DB_TransactionCallback cb,
void *cb_cls);
/**
* Calculate the total value of all transactions performed.
* Stores @a off plus the cost of all transactions in @a tl
* in @a ret.
*
* @param tl transaction list to process
* @param off offset to use as the starting value
* @param[out] ret where the resulting total is to be stored
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
// FIXME: maybe move to another module?
int
TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
const struct TALER_Amount *off,
struct TALER_Amount *ret);
/** /**

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V. Copyright (C) 2014-2017 Inria and GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -36,6 +36,174 @@
#include "taler-exchange-httpd_validation.h" #include "taler-exchange-httpd_validation.h"
/**
* Send confirmation of deposit success to client. This function
* will create a signed message affirming the given information
* and return it to the client. By this, the exchange affirms that
* the coin had sufficient (residual) value for the specified
* transaction and that it will execute the requested deposit
* operation with the given wiring details.
*
* @param connection connection to the client
* @param coin_pub public key of the coin
* @param h_wire hash of wire details
* @param h_contract_terms hash of contract details
* @param timestamp client's timestamp
* @param refund_deadline until when this deposit be refunded
* @param merchant merchant public key
* @param amount_without_fee fraction of coin value to deposit, without the fee
* @return MHD result code
*/
static int
reply_deposit_success (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
const struct TALER_MerchantPublicKeyP *merchant,
const struct TALER_Amount *amount_without_fee)
{
struct TALER_DepositConfirmationPS dc;
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
dc.h_contract_terms = *h_contract_terms;
dc.h_wire = *h_wire;
dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
TALER_amount_hton (&dc.amount_without_fee,
amount_without_fee);
dc.coin_pub = *coin_pub;
dc.merchant = *merchant;
TEH_KS_sign (&dc.purpose,
&pub,
&sig);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s, s:o, s:o}",
"status", "DEPOSIT_OK",
"sig", GNUNET_JSON_from_data_auto (&sig),
"pub", GNUNET_JSON_from_data_auto (&pub));
}
/**
* Closure for #deposit_transaction.
*/
struct DepositContext
{
/**
* Information about the deposit request.
*/
const struct TALER_EXCHANGEDB_Deposit *deposit;
/**
* Value of the coin.
*/
struct TALER_Amount value;
};
/**
* 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 session database session and transaction to use
* @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,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{
struct DepositContext *dc = cls;
const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
struct TALER_EXCHANGEDB_TransactionList *tl;
struct TALER_Amount spent;
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
session,
deposit);
if (qs < 0)
return qs;
if (1 == qs)
{
struct TALER_Amount amount_without_fee;
GNUNET_assert (GNUNET_OK ==
TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee,
&deposit->deposit_fee));
*mhd_ret = reply_deposit_success (connection,
&deposit->coin.coin_pub,
&deposit->h_wire,
&deposit->h_contract_terms,
deposit->timestamp,
deposit->refund_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;
}
/* Start with fee for THIS transaction */
spent = deposit->amount_with_fee;
/* add cost of all previous transactions */
tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
session,
&deposit->coin.coin_pub);
if (GNUNET_OK !=
TEH_DB_calculate_transaction_list_totals (tl,
&spent,
&spent))
{
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* Check that cost of all transactions is smaller than
the value of the coin. */
if (0 < TALER_amount_cmp (&spent,
&dc->value))
{
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
tl);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return GNUNET_DB_STATUS_HARD_ERROR;
}
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
session,
deposit);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_STORE_DB_ERROR);
}
return qs;
}
/** /**
* We have parsed the JSON information about the deposit, do some * We have parsed the JSON information about the deposit, do some
* basic sanity checks (especially that the signature on the coin is * basic sanity checks (especially that the signature on the coin is
@ -51,7 +219,13 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Deposit *deposit) const struct TALER_EXCHANGEDB_Deposit *deposit)
{ {
struct TALER_DepositRequestPS dr; struct TALER_DepositRequestPS dr;
int mhd_ret;
struct TALER_Amount amount_without_fee;
struct DepositContext dc;
struct TEH_KS_StateHandle *mks;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
/* check signature */
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
dr.h_contract_terms = deposit->h_contract_terms; dr.h_contract_terms = deposit->h_contract_terms;
@ -76,8 +250,43 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
"coin_sig"); "coin_sig");
} }
return TEH_DB_execute_deposit (connection, /* check denomination */
deposit); mks = TEH_KS_acquire ();
dki = TEH_KS_denomination_key_lookup (mks,
&deposit->coin.denom_pub,
TEH_KS_DKU_DEPOSIT);
if (NULL == dki)
{
TEH_KS_release (mks);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
}
TALER_amount_ntoh (&dc.value,
&dki->issue.properties.value);
TEH_KS_release (mks);
/* execute transaction */
dc.deposit = deposit;
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
&mhd_ret,
&deposit_transaction,
&dc))
return mhd_ret;
/* generate regular response */
GNUNET_assert (GNUNET_SYSERR !=
TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee,
&deposit->deposit_fee));
return reply_deposit_success (connection,
&deposit->coin.coin_pub,
&deposit->h_wire,
&deposit->h_contract_terms,
deposit->timestamp,
deposit->refund_deadline,
&deposit->merchant_pub,
&amount_without_fee);
} }

View File

@ -461,60 +461,6 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection)
} }
/**
* Send confirmation of deposit success to client. This function
* will create a signed message affirming the given information
* and return it to the client. By this, the exchange affirms that
* the coin had sufficient (residual) value for the specified
* transaction and that it will execute the requested deposit
* operation with the given wiring details.
*
* @param connection connection to the client
* @param coin_pub public key of the coin
* @param h_wire hash of wire details
* @param h_contract_terms hash of contract details
* @param timestamp client's timestamp
* @param refund_deadline until when this deposit be refunded
* @param merchant merchant public key
* @param amount_without_fee fraction of coin value to deposit, without the fee
* @return MHD result code
*/
int
TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
const struct TALER_MerchantPublicKeyP *merchant,
const struct TALER_Amount *amount_without_fee)
{
struct TALER_DepositConfirmationPS dc;
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
dc.h_contract_terms = *h_contract_terms;
dc.h_wire = *h_wire;
dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
TALER_amount_hton (&dc.amount_without_fee,
amount_without_fee);
dc.coin_pub = *coin_pub;
dc.merchant = *merchant;
TEH_KS_sign (&dc.purpose,
&pub,
&sig);
return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s, s:o, s:o}",
"status", "DEPOSIT_OK",
"sig", GNUNET_JSON_from_data_auto (&sig),
"pub", GNUNET_JSON_from_data_auto (&pub));
}
/** /**
* Compile the transaction history of a coin into a JSON object. * Compile the transaction history of a coin into a JSON object.
* *

View File

@ -243,35 +243,6 @@ int
TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connectionx); TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connectionx);
/**
* Send confirmation of deposit success to client. This function
* will create a signed message affirming the given information
* and return it to the client. By this, the exchange affirms that
* the coin had sufficient (residual) value for the specified
* transaction and that it will execute the requested deposit
* operation with the given wiring details.
*
* @param connection connection to the client
* @param coin_pub public key of the coin
* @param h_wire hash of wire details
* @param h_contract_terms hash of proposal data
* @param timestamp client's timestamp
* @param refund_deadline until when this deposit be refunded
* @param merchant merchant public key
* @param amount_without_fee fraction of coin value to deposit (without fee)
* @return MHD result code
*/
int
TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
const struct TALER_MerchantPublicKeyP *merchant,
const struct TALER_Amount *amount_without_fee);
/** /**
* Send proof that a request is invalid to client because of * Send proof that a request is invalid to client because of
* insufficient funds. This function will create a message with all * insufficient funds. This function will create a message with all

View File

@ -1350,7 +1350,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_GET_DEPOSIT: case PERF_TALER_EXCHANGEDB_CMD_GET_DEPOSIT:
{ {
unsigned int source_index; unsigned int source_index;
int ret; enum GNUNET_DB_QueryStatus ret;
struct PERF_TALER_EXCHANGEDB_Data *data; struct PERF_TALER_EXCHANGEDB_Data *data;
source_index = state->cmd[state->i].details.get_deposit.index_deposit; source_index = state->cmd[state->i].details.get_deposit.index_deposit;
@ -1358,7 +1358,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
ret = state->plugin->have_deposit (state->plugin->cls, ret = state->plugin->have_deposit (state->plugin->cls,
state->session, state->session,
data->data.deposit); data->data.deposit);
GNUNET_assert (GNUNET_SYSERR != ret); GNUNET_assert (0 >= ret);
} }
break; break;

View File

@ -2698,11 +2698,11 @@ postgres_get_reserve_history (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state * @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection * @param session database connection
* @param deposit deposit to search for * @param deposit deposit to search for
* @return #GNUNET_YES if we know this operation, * @return 1 if we know this operation,
* #GNUNET_NO if this exact deposit is unknown to us * 0 if this exact deposit is unknown to us,
* #GNUNET_SYSERR on DB error * otherwise transaction error status
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_have_deposit (void *cls, postgres_have_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit) const struct TALER_EXCHANGEDB_Deposit *deposit)
@ -2713,75 +2713,52 @@ postgres_have_deposit (void *cls,
GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub), GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
PGresult *result; struct TALER_EXCHANGEDB_Deposit deposit2;
struct GNUNET_PQ_ResultSpec rs[] = {
result = GNUNET_PQ_exec_prepared (session->conn, TALER_PQ_result_spec_amount ("amount_with_fee",
"get_deposit", &deposit2.amount_with_fee),
params); GNUNET_PQ_result_spec_absolute_time ("timestamp",
if (PGRES_TUPLES_OK != &deposit2.timestamp),
PQresultStatus (result)) GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
{ &deposit2.refund_deadline),
BREAK_DB_ERR (result, session->conn); GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
PQclear (result); &deposit2.wire_deadline),
return GNUNET_SYSERR; GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
} &deposit2.h_contract_terms),
if (0 == PQntuples (result)) GNUNET_PQ_result_spec_auto_from_type ("h_wire",
{ &deposit2.h_wire),
PQclear (result); GNUNET_PQ_result_spec_end
return GNUNET_NO; };
} enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_deposit",
params,
rs);
if (0 >= qs)
return qs;
/* Now we check that the other information in @a deposit /* Now we check that the other information in @a deposit
also matches, and if not report inconsistencies. */ also matches, and if not report inconsistencies. */
if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
&deposit2.amount_with_fee)) ||
(deposit->timestamp.abs_value_us !=
deposit2.timestamp.abs_value_us) ||
(deposit->refund_deadline.abs_value_us !=
deposit2.refund_deadline.abs_value_us) ||
(0 != memcmp (&deposit->h_contract_terms,
&deposit2.h_contract_terms,
sizeof (struct GNUNET_HashCode))) ||
(0 != memcmp (&deposit->h_wire,
&deposit2.h_wire,
sizeof (struct GNUNET_HashCode))) )
{ {
struct TALER_EXCHANGEDB_Deposit deposit2; /* Inconsistencies detected! Does not match! (We might want to
struct GNUNET_PQ_ResultSpec rs[] = { expand the API with a 'get_deposit' function to return the
TALER_PQ_result_spec_amount ("amount_with_fee", original transaction details to be used for an error message
&deposit2.amount_with_fee), in the future!) #3838 */
GNUNET_PQ_result_spec_absolute_time ("timestamp", return 0; /* Counts as if the transaction was not there */
&deposit2.timestamp),
GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
&deposit2.refund_deadline),
GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
&deposit2.wire_deadline),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&deposit2.h_contract_terms),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&deposit2.h_wire),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
0))
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
&deposit2.amount_with_fee)) ||
(deposit->timestamp.abs_value_us !=
deposit2.timestamp.abs_value_us) ||
(deposit->refund_deadline.abs_value_us !=
deposit2.refund_deadline.abs_value_us) ||
(0 != memcmp (&deposit->h_contract_terms,
&deposit2.h_contract_terms,
sizeof (struct GNUNET_HashCode))) ||
(0 != memcmp (&deposit->h_wire,
&deposit2.h_wire,
sizeof (struct GNUNET_HashCode))) )
{
/* Inconsistencies detected! Does not match! (We might want to
expand the API with a 'get_deposit' function to return the
original transaction details to be used for an error message
in the future!) #3838 */
PQclear (result);
return GNUNET_NO;
}
} }
PQclear (result); return 1;
return GNUNET_YES;
} }
@ -3267,11 +3244,9 @@ postgres_insert_deposit (void *cls,
* @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 session connection to the database * @param session connection to the database
* @param refund refund information to store * @param refund refund information to store
* @return #GNUNET_OK on success * @return query result status
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on error
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_insert_refund (void *cls, postgres_insert_refund (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund) const struct TALER_EXCHANGEDB_Refund *refund)
@ -3289,9 +3264,9 @@ postgres_insert_refund (void *cls,
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
TALER_amount_cmp_currency (&refund->refund_amount, TALER_amount_cmp_currency (&refund->refund_amount,
&refund->refund_fee)); &refund->refund_fee));
return execute_prepared_non_select (session, return GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_refund", "insert_refund",
params); params);
} }

View File

@ -1772,7 +1772,7 @@ run (void *cls)
plugin->insert_deposit (plugin->cls, plugin->insert_deposit (plugin->cls,
session, session,
&deposit)); &deposit));
FAILIF (GNUNET_YES != FAILIF (1 !=
plugin->have_deposit (plugin->cls, plugin->have_deposit (plugin->cls,
session, session,
&deposit)); &deposit));
@ -1839,13 +1839,13 @@ run (void *cls)
result = 10; result = 10;
deposit2 = deposit; deposit2 = deposit;
RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */ RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
FAILIF (GNUNET_NO != FAILIF (0 !=
plugin->have_deposit (plugin->cls, plugin->have_deposit (plugin->cls,
session, session,
&deposit2)); &deposit2));
deposit2.merchant_pub = deposit.merchant_pub; deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */ RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
FAILIF (GNUNET_NO != FAILIF (0 !=
plugin->have_deposit (plugin->cls, plugin->have_deposit (plugin->cls,
session, session,
&deposit2)); &deposit2));
@ -1860,7 +1860,7 @@ run (void *cls)
refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
refund.refund_amount = deposit.amount_with_fee; refund.refund_amount = deposit.amount_with_fee;
refund.refund_fee = fee_refund; refund.refund_fee = fee_refund;
FAILIF (GNUNET_OK != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refund (plugin->cls, plugin->insert_refund (plugin->cls,
session, session,
&refund)); &refund));

View File

@ -1293,11 +1293,11 @@ struct TALER_EXCHANGEDB_Plugin
* @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 session database connection * @param session database connection
* @param deposit deposit to search for * @param deposit deposit to search for
* @return #GNUNET_YES if we know this operation, * @return 1 if we know this operation,
* #GNUNET_NO if this exact deposit is unknown to us, * 0 if this exact deposit is unknown to us,
* #GNUNET_SYSERR on DB error * otherwise transaction error status
*/ */
int enum GNUNET_DB_QueryStatus
(*have_deposit) (void *cls, (*have_deposit) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit); const struct TALER_EXCHANGEDB_Deposit *deposit);
@ -1323,11 +1323,9 @@ struct TALER_EXCHANGEDB_Plugin
* @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 session connection to the database * @param session connection to the database
* @param refund refund information to store * @param refund refund information to store
* @return #GNUNET_OK on success, * @return query result status
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on error
*/ */
int enum GNUNET_DB_QueryStatus
(*insert_refund) (void *cls, (*insert_refund) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund); const struct TALER_EXCHANGEDB_Refund *refund);