fix #5010 for /track/transfer

This commit is contained in:
Christian Grothoff 2017-06-19 20:12:00 +02:00
parent beea8eb383
commit a0c66e7919
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 234 additions and 194 deletions

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015, 2016 GNUnet e.V. Copyright (C) 2014-2017 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
@ -81,14 +81,14 @@ struct TEH_TrackTransferDetail
* @param wdd_head linked list with details about the combined deposits * @param wdd_head linked list with details about the combined deposits
* @return MHD result code * @return MHD result code
*/ */
int static int
TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection, reply_track_transfer_details (struct MHD_Connection *connection,
const struct TALER_Amount *total, const struct TALER_Amount *total,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *wire_fee, const struct TALER_Amount *wire_fee,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct TEH_TrackTransferDetail *wdd_head) const struct TEH_TrackTransferDetail *wdd_head)
{ {
const struct TEH_TrackTransferDetail *wdd_pos; const struct TEH_TrackTransferDetail *wdd_pos;
json_t *deposits; json_t *deposits;
@ -154,6 +154,11 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
struct WtidTransactionContext struct WtidTransactionContext
{ {
/**
* Identifier of the wire transfer to track.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/** /**
* Total amount of the wire transfer, as calculated by * Total amount of the wire transfer, as calculated by
* summing up the individual amounts. To be rounded down * summing up the individual amounts. To be rounded down
@ -179,6 +184,11 @@ struct WtidTransactionContext
*/ */
struct GNUNET_HashCode h_wire; struct GNUNET_HashCode h_wire;
/**
* Wire fee applicable at @e exec_time.
*/
struct TALER_Amount wire_fee;
/** /**
* Execution time of the wire transfer * Execution time of the wire transfer
*/ */
@ -309,102 +319,116 @@ handle_transaction_data (void *cls,
/** /**
* Execute a "/track/transfer". Returns the transaction information * Execute a "/track/transfer". Returns the transaction information
* associated with the given wire transfer identifier. * associated with the given wire transfer identifier.
*
* 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 connection the MHD connection to handle * @param cls closure
* @param wtid wire transfer identifier to resolve * @param connection MHD request which triggered the transaction
* @return MHD result code * @param session database session to use
* @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!)
* @return transaction status
*/ */
int static enum GNUNET_DB_QueryStatus
TEH_DB_execute_track_transfer (struct MHD_Connection *connection, track_transfer_transaction (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid) struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{ {
int ret; struct WtidTransactionContext *ctx = cls;
struct WtidTransactionContext ctx; enum GNUNET_DB_QueryStatus qs;
struct TALER_EXCHANGEDB_Session *session;
struct TEH_TrackTransferDetail *wdd;
struct GNUNET_TIME_Absolute wire_fee_start_date; struct GNUNET_TIME_Absolute wire_fee_start_date;
struct GNUNET_TIME_Absolute wire_fee_end_date; struct GNUNET_TIME_Absolute wire_fee_end_date;
struct TALER_Amount wire_fee;
struct TALER_MasterSignatureP wire_fee_master_sig; struct TALER_MasterSignatureP wire_fee_master_sig;
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) ctx->is_valid = GNUNET_NO;
ctx->wdd_head = NULL;
ctx->wdd_tail = NULL;
ctx->wire_method = NULL;
qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
session,
&ctx->wtid,
&handle_transaction_data,
ctx);
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
}
return qs;
}
if (GNUNET_SYSERR == ctx->is_valid)
{ {
GNUNET_break (0); GNUNET_break (0);
return TEH_RESPONSE_reply_internal_db_error (connection, *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_SETUP_FAILED); TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
return GNUNET_DB_STATUS_HARD_ERROR;
} }
ctx.is_valid = GNUNET_NO; if (GNUNET_NO == ctx->is_valid)
ctx.wdd_head = NULL;
ctx.wdd_tail = NULL;
ctx.wire_method = NULL;
ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
session,
wtid,
&handle_transaction_data,
&ctx);
if (GNUNET_SYSERR == ret)
{ {
GNUNET_break (0); *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
ret = TEH_RESPONSE_reply_internal_db_error (connection, TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED); "wtid");
goto cleanup; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if (GNUNET_SYSERR == ctx.is_valid) qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
session,
ctx->wire_method,
ctx->exec_time,
&wire_fee_start_date,
&wire_fee_end_date,
&ctx->wire_fee,
&wire_fee_master_sig);
if (0 >= qs)
{ {
GNUNET_break (0); if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
ret = TEH_RESPONSE_reply_internal_db_error (connection, (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS) )
TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT); {
goto cleanup; GNUNET_break (0);
} *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
if (GNUNET_NO == ctx.is_valid) TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
{ }
ret = TEH_RESPONSE_reply_arg_unknown (connection, return qs;
TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
"wtid");
goto cleanup;
} }
if (GNUNET_OK != if (GNUNET_OK !=
TEH_plugin->get_wire_fee (TEH_plugin->cls, TALER_amount_subtract (&ctx->total,
session, &ctx->total,
ctx.wire_method, &ctx->wire_fee))
ctx.exec_time,
&wire_fee_start_date,
&wire_fee_end_date,
&wire_fee,
&wire_fee_master_sig))
{ {
GNUNET_break (0); GNUNET_break (0);
ret = TEH_RESPONSE_reply_internal_db_error (connection, *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND); TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
goto cleanup; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if (GNUNET_OK != return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
TALER_amount_subtract (&ctx.total, }
&ctx.total,
&wire_fee))
/**
* Free data structure reachable from @a ctx, but not @a ctx itself.
*
* @param ctx context to free
*/
static void
free_ctx (struct WtidTransactionContext *ctx)
{
struct TEH_TrackTransferDetail *wdd;
while (NULL != (wdd = ctx->wdd_head))
{ {
GNUNET_break (0); GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
ret = TEH_RESPONSE_reply_internal_db_error (connection, ctx->wdd_tail,
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
goto cleanup;
}
ret = TEH_RESPONSE_reply_track_transfer_details (connection,
&ctx.total,
&ctx.merchant_pub,
&ctx.h_wire,
&wire_fee,
ctx.exec_time,
ctx.wdd_head);
cleanup:
while (NULL != (wdd = ctx.wdd_head))
{
GNUNET_CONTAINER_DLL_remove (ctx.wdd_head,
ctx.wdd_tail,
wdd); wdd);
GNUNET_free (wdd); GNUNET_free (wdd);
} }
GNUNET_free_non_null (ctx.wire_method); GNUNET_free_non_null (ctx->wire_method);
return ret;
} }
@ -425,19 +449,37 @@ TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
const char *upload_data, const char *upload_data,
size_t *upload_data_size) size_t *upload_data_size)
{ {
struct TALER_WireTransferIdentifierRawP wtid; struct WtidTransactionContext ctx;
int res; int res;
int mhd_ret;
memset (&ctx, 0, sizeof (ctx));
res = TEH_PARSE_mhd_request_arg_data (connection, res = TEH_PARSE_mhd_request_arg_data (connection,
"wtid", "wtid",
&wtid, &ctx.wtid,
sizeof (struct TALER_WireTransferIdentifierRawP)); sizeof (struct TALER_WireTransferIdentifierRawP));
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
return MHD_NO; /* internal error */ return MHD_NO; /* internal error */
if (GNUNET_NO == res) if (GNUNET_NO == res)
return MHD_YES; /* parse error */ return MHD_YES; /* parse error */
return TEH_DB_execute_track_transfer (connection, if (GNUNET_OK !=
&wtid); TEH_DB_run_transaction (connection,
&mhd_ret,
&track_transfer_transaction,
&ctx))
{
free_ctx (&ctx);
return mhd_ret;
}
mhd_ret = reply_track_transfer_details (connection,
&ctx.total,
&ctx.merchant_pub,
&ctx.h_wire,
&ctx.wire_fee,
ctx.exec_time,
ctx.wdd_head);
free_ctx (&ctx);
return mhd_ret;
} }

View File

@ -4430,50 +4430,44 @@ postgres_get_coin_transactions (void *cls,
/** /**
* Lookup the list of Taler transactions that were aggregated * Closure for #handle_wt_result.
* into a wire transfer by the respective @a wtid.
*
* @param cls closure
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
* #GNUNET_NO if we found no results
*/ */
static int struct WireTransferResultContext
postgres_lookup_wire_transfer (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_EXCHANGEDB_WireTransferDataCallback cb,
void *cb_cls)
{ {
PGresult *result; /**
struct GNUNET_PQ_QueryParam params[] = { * Function to call on each result.
GNUNET_PQ_query_param_auto_from_type (wtid), */
GNUNET_PQ_query_param_end TALER_EXCHANGEDB_WireTransferDataCallback cb;
};
int nrows;
/* check if the melt record exists and get it */ /**
result = GNUNET_PQ_exec_prepared (session->conn, * Closure for @e cb.
"lookup_transactions", */
params); void *cb_cls;
if (PGRES_TUPLES_OK != PQresultStatus (result))
{ /**
BREAK_DB_ERR (result, session->conn); * Set to #GNUNET_SYSERR on serious errors.
PQclear (result); */
return GNUNET_SYSERR; int status;
} };
nrows = PQntuples (result);
if (0 == nrows)
{ /**
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, * Function to be called with the results of a SELECT statement
"lookup_wire_transfer() returned 0 matching rows\n"); * that has returned @a num_results results. Helper function
PQclear (result); * for #postgres_lookup_wire_transfer().
return GNUNET_NO; *
} * @param cls closure of type `struct WireTransferResultContext *`
for (int i=0;i<nrows;i++) * @param result the postgres result
* @param num_result the number of results in @a result
*/
static void
handle_wt_result (void *cls,
PGresult *result,
unsigned int num_results)
{
struct WireTransferResultContext *ctx = cls;
for (unsigned int i=0;i<num_results;i++)
{ {
uint64_t rowid; uint64_t rowid;
struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_contract_terms;
@ -4505,37 +4499,75 @@ postgres_lookup_wire_transfer (void *cls,
i)) i))
{ {
GNUNET_break (0); GNUNET_break (0);
PQclear (result); ctx->status = GNUNET_SYSERR;
return GNUNET_SYSERR; return;
} }
t = json_object_get (wire, "type"); t = json_object_get (wire, "type");
if (NULL == t) if (NULL == t)
{ {
GNUNET_break (0); GNUNET_break (0);
PQclear (result); ctx->status = GNUNET_SYSERR;
return GNUNET_SYSERR; return;
} }
wire_method = json_string_value (t); wire_method = json_string_value (t);
if (NULL == wire_method) if (NULL == wire_method)
{ {
GNUNET_break (0); GNUNET_break (0);
PQclear (result); ctx->status = GNUNET_SYSERR;
return GNUNET_SYSERR; return;
} }
cb (cb_cls, ctx->cb (ctx->cb_cls,
rowid, rowid,
&merchant_pub, &merchant_pub,
wire_method, wire_method,
&h_wire, &h_wire,
exec_time, exec_time,
&h_contract_terms, &h_contract_terms,
&coin_pub, &coin_pub,
&amount_with_fee, &amount_with_fee,
&deposit_fee); &deposit_fee);
GNUNET_PQ_cleanup_result (rs); GNUNET_PQ_cleanup_result (rs);
} }
PQclear (result); }
return GNUNET_OK;
/**
* Lookup the list of Taler transactions that were aggregated
* into a wire transfer by the respective @a wtid.
*
* @param cls closure
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found
* @param cb_cls closure for @a cb
* @return query status of the transaction
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_wire_transfer (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_EXCHANGEDB_WireTransferDataCallback cb,
void *cb_cls)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_end
};
struct WireTransferResultContext ctx;
enum GNUNET_DB_QueryStatus qs;
ctx.cb = cb;
ctx.cb_cls = cb_cls;
ctx.status = GNUNET_OK;
/* check if the melt record exists and get it */
qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
"lookup_transactions",
params,
&handle_wt_result,
&ctx);
if (GNUNET_OK != ctx.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
} }
@ -4733,10 +4765,9 @@ postgres_insert_aggregation_tracking (void *cls,
* @param[out] end_date when does the fee end being valid * @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee * @param[out] wire_fee how high is the wire transfer fee
* @param[out] master_sig signature over the above by the exchange master key * @param[out] master_sig signature over the above by the exchange master key
* @return #GNUNET_OK on success, #GNUNET_NO if no fee is known * @return status of the transaction
* #GNUNET_SYSERR on failure
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_get_wire_fee (void *cls, postgres_get_wire_fee (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const char *type, const char *type,
@ -4758,42 +4789,11 @@ postgres_get_wire_fee (void *cls,
GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig), GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
PGresult *result;
int nrows;
result = GNUNET_PQ_exec_prepared (session->conn, return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_wire_fee", "get_wire_fee",
params); params,
if (PGRES_TUPLES_OK != rs);
PQresultStatus (result))
{
BREAK_DB_ERR (result, session->conn);
PQclear (result);
return GNUNET_SYSERR;
}
nrows = PQntuples (result);
if (0 == nrows)
{
/* no matches found */
PQclear (result);
return GNUNET_NO;
}
if (1 != nrows)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
0))
{
PQclear (result);
GNUNET_break (0);
return GNUNET_SYSERR;
}
PQclear (result);
return GNUNET_OK;
} }

View File

@ -1185,7 +1185,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
} }
/* This must fail as 'end_date' is NOT in the /* This must fail as 'end_date' is NOT in the
half-open interval [start_date,end_date) */ half-open interval [start_date,end_date) */
if (GNUNET_OK == if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->get_wire_fee (plugin->cls, plugin->get_wire_fee (plugin->cls,
session, session,
"wire-method", "wire-method",
@ -1198,7 +1198,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
GNUNET_break (0); GNUNET_break (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_OK != if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_wire_fee (plugin->cls, plugin->get_wire_fee (plugin->cls,
session, session,
"wire-method", "wire-method",
@ -1302,7 +1302,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
TALER_amount_subtract (&transfer_value_wt, TALER_amount_subtract (&transfer_value_wt,
&coin_value_wt, &coin_value_wt,
&coin_fee_wt)); &coin_fee_wt));
FAILIF (GNUNET_NO != FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->lookup_wire_transfer (plugin->cls, plugin->lookup_wire_transfer (plugin->cls,
session, session,
&wire_out_wtid, &wire_out_wtid,
@ -1344,7 +1344,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
plugin->commit (plugin->cls, plugin->commit (plugin->cls,
session)); session));
FAILIF (GNUNET_OK != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->lookup_wire_transfer (plugin->cls, plugin->lookup_wire_transfer (plugin->cls,
session, session,
&wire_out_wtid, &wire_out_wtid,

View File

@ -1731,10 +1731,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param wtid the raw wire transfer identifier we used * @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found * @param cb function to call on each transaction found
* @param cb_cls closure for @a cb * @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors, * @return query status of the transaction
* #GNUNET_NO if we found no results
*/ */
int enum GNUNET_DB_QueryStatus
(*lookup_wire_transfer) (void *cls, (*lookup_wire_transfer) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_WireTransferIdentifierRawP *wtid,
@ -1822,10 +1821,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param[out] end_date when does the fee end being valid * @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee * @param[out] wire_fee how high is the wire transfer fee
* @param[out] master_sig signature over the above by the exchange master key * @param[out] master_sig signature over the above by the exchange master key
* @return #GNUNET_OK on success, #GNUNET_NO if no fee is known * @return query status of the transaction
* #GNUNET_SYSERR on failure
*/ */
int enum GNUNET_DB_QueryStatus
(*get_wire_fee) (void *cls, (*get_wire_fee) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const char *type, const char *type,