first rough-cut implementation of POST /reserves//status

This commit is contained in:
Christian Grothoff 2022-03-21 02:39:36 +01:00
parent b560527ee3
commit f5f15e6531
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
14 changed files with 441 additions and 219 deletions

@ -1 +1 @@
Subproject commit baeb820366b88befd6f5aa2a551e2827ef406daf Subproject commit 4cfefdf374de55fe9be3f0f039c7a13f496ab970

View File

@ -108,6 +108,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \ taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \
taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \ taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \ taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \
taler-exchange-httpd_reserves_status.c taler-exchange-httpd_reserves_status.h \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \ taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \ taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \ taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \

View File

@ -48,6 +48,7 @@
#include "taler-exchange-httpd_refreshes_reveal.h" #include "taler-exchange-httpd_refreshes_reveal.h"
#include "taler-exchange-httpd_refund.h" #include "taler-exchange-httpd_refund.h"
#include "taler-exchange-httpd_reserves_get.h" #include "taler-exchange-httpd_reserves_get.h"
#include "taler-exchange-httpd_reserves_status.h"
#include "taler-exchange-httpd_terms.h" #include "taler-exchange-httpd_terms.h"
#include "taler-exchange-httpd_transfers_get.h" #include "taler-exchange-httpd_transfers_get.h"
#include "taler-exchange-httpd_wire.h" #include "taler-exchange-httpd_wire.h"
@ -212,6 +213,19 @@ typedef MHD_RESULT
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const json_t *root); const json_t *root);
/**
* Signature of functions that handle operations on reserves.
*
* @param rc request context
* @param reserve_pub the public key of the reserve
* @param root uploaded JSON data
* @return MHD result code
*/
typedef MHD_RESULT
(*ReserveOpHandler)(struct TEH_RequestContext *rc,
const struct TALER_ReservePublicKeyP *reserve_pub,
const json_t *root);
/** /**
* Generate a 404 "not found" reply on @a connection with * Generate a 404 "not found" reply on @a connection with
@ -237,8 +251,7 @@ r404 (struct MHD_Connection *connection,
* *
* @param rc request context * @param rc request context
* @param root uploaded JSON data * @param root uploaded JSON data
* @param args array of additional options (first must be the * @param args array of additional options
* reserve public key, the second one should be "withdraw")
* @return MHD result code * @return MHD result code
*/ */
static MHD_RESULT static MHD_RESULT
@ -309,6 +322,71 @@ handle_post_coins (struct TEH_RequestContext *rc,
} }
/**
* Handle a "/reserves/$RESERVE_PUB/$OP" POST request. Parses the "reserve_pub"
* EdDSA key of the reserve and demultiplexes based on $OP.
*
* @param rc request context
* @param root uploaded JSON data
* @param args array of additional options
* @return MHD result code
*/
static MHD_RESULT
handle_post_reserves (struct TEH_RequestContext *rc,
const json_t *root,
const char *const args[2])
{
struct TALER_ReservePublicKeyP reserve_pub;
static const struct
{
/**
* Name of the operation (args[1])
*/
const char *op;
/**
* Function to call to perform the operation.
*/
ReserveOpHandler handler;
} h[] = {
{
.op = "withdraw",
.handler = &TEH_handler_withdraw
},
{
.op = "status",
.handler = &TEH_handler_reserves_status
},
{
.op = NULL,
.handler = NULL
},
};
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&reserve_pub,
sizeof (reserve_pub)))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
for (unsigned int i = 0; NULL != h[i].op; i++)
if (0 == strcmp (h[i].op,
args[1]))
return h[i].handler (rc,
&reserve_pub,
root);
return r404 (rc->connection,
args[1]);
}
/** /**
* Increments our request counter and checks if this * Increments our request counter and checks if this
* process should commit suicide. * process should commit suicide.
@ -947,7 +1025,7 @@ handle_mhd_request (void *cls,
{ {
.url = "reserves", .url = "reserves",
.method = MHD_HTTP_METHOD_POST, .method = MHD_HTTP_METHOD_POST,
.handler.post = &TEH_handler_withdraw, .handler.post = &handle_post_reserves,
.nargs = 2 .nargs = 2
}, },
/* coins */ /* coins */

View File

@ -205,18 +205,6 @@ deposit_transaction (void *cls,
} }
/**
* Handle a "/coins/$COIN_PUB/deposit" request. Parses the JSON, and, if
* successful, passes the JSON data to #deposit_transaction() to
* further check the details of the operation specified. If everything checks
* out, this will ultimately lead to the "/deposit" being executed, or
* rejected.
*
* @param connection the MHD connection to handle
* @param coin_pub public key of the coin
* @param root uploaded JSON data
* @return MHD result code
*/
MHD_RESULT MHD_RESULT
TEH_handler_deposit (struct MHD_Connection *connection, TEH_handler_deposit (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2020 Taler Systems SA Copyright (C) 2014-2022 Taler Systems SA
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

View File

@ -164,37 +164,6 @@ db_event_cb (void *cls,
} }
/**
* Send reserve history to client.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
static MHD_RESULT
reply_reserve_history_success (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{
json_t *json_history;
struct TALER_Amount balance;
json_history = TEH_RESPONSE_compile_reserve_history (rh,
&balance);
if (NULL == json_history)
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
NULL);
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
TALER_JSON_pack_amount ("balance",
&balance),
GNUNET_JSON_pack_array_steal ("history",
json_history));
}
/** /**
* Closure for #reserve_history_transaction. * Closure for #reserve_history_transaction.
*/ */
@ -205,10 +174,18 @@ struct ReserveHistoryContext
*/ */
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
#ifndef MBOSS_DONE
/** /**
* History of the reserve, set in the callback. * History of the reserve, set in the callback.
* FIXME: get rid of this once benchmarking is done!
*/ */
struct TALER_EXCHANGEDB_ReserveHistory *rh; struct TALER_EXCHANGEDB_ReserveHistory *rh;
#endif
/**
* Balance of the reserve, set in the callback.
*/
struct TALER_Amount balance;
}; };
@ -226,23 +203,37 @@ struct ReserveHistoryContext
* @param cls a `struct ReserveHistoryContext *` * @param cls a `struct ReserveHistoryContext *`
* @param connection MHD request which triggered the transaction * @param connection MHD request which triggered the transaction
* @param[out] mhd_ret set to MHD response status for @a connection, * @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!); unused * if transaction failed (!)
* @return transaction status * @return transaction status
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
reserve_history_transaction (void *cls, reserve_balance_transaction (void *cls,
struct MHD_Connection *connection, struct MHD_Connection *connection,
MHD_RESULT *mhd_ret) MHD_RESULT *mhd_ret)
{ {
struct ReserveHistoryContext *rsc = cls; struct ReserveHistoryContext *rsc = cls;
struct TALER_Amount balance; enum GNUNET_DB_QueryStatus qs;
(void) connection; #ifdef MBOSS_DONE
(void) mhd_ret; qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
return TEH_plugin->get_reserve_history (TEH_plugin->cls, &rsc->reserve_pub,
&rsc->reserve_pub, &rsc->balance);
&balance, #else
&rsc->rh); qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
&rsc->reserve_pub,
&rsc->balance,
&rsc->rh);
#endif
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
*mhd_ret
= TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_reserve_balance");
}
return qs;
} }
@ -314,10 +305,10 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
rsc.rh = NULL; rsc.rh = NULL;
if (GNUNET_OK != if (GNUNET_OK !=
TEH_DB_run_transaction (rc->connection, TEH_DB_run_transaction (rc->connection,
"get reserve history", "get reserve balance",
TEH_MT_REQUEST_OTHER, TEH_MT_REQUEST_OTHER,
&mhd_ret, &mhd_ret,
&reserve_history_transaction, &reserve_balance_transaction,
&rsc)) &rsc))
{ {
if (NULL != eh) if (NULL != eh)
@ -335,7 +326,7 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
{ {
return TALER_MHD_reply_with_error (rc->connection, return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_RESERVES_GET_STATUS_UNKNOWN, TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN,
args[0]); args[0]);
} }
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -358,10 +349,15 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
if (NULL != eh) if (NULL != eh)
TEH_plugin->event_listen_cancel (TEH_plugin->cls, TEH_plugin->event_listen_cancel (TEH_plugin->cls,
eh); eh);
mhd_ret = reply_reserve_history_success (rc->connection, mhd_ret = TALER_MHD_REPLY_JSON_PACK (
rsc.rh); rc->connection,
MHD_HTTP_OK,
TALER_JSON_pack_amount ("balance",
&rsc.balance));
#ifndef MBOSS_DONE
TEH_plugin->free_reserve_history (TEH_plugin->cls, TEH_plugin->free_reserve_history (TEH_plugin->cls,
rsc.rh); rsc.rh);
#endif
return mhd_ret; return mhd_ret;
} }

View File

@ -0,0 +1,199 @@
/*
This file is part of TALER
Copyright (C) 2014-2022 Taler Systems SA
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
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-exchange-httpd_reserves_status.c
* @brief Handle /reserves/$RESERVE_PUB STATUS requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include "taler_mhd_lib.h"
#include "taler_json_lib.h"
#include "taler_dbevents.h"
#include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_reserves_status.h"
#include "taler-exchange-httpd_responses.h"
/**
* Closure for #reserve_status_transaction.
*/
struct ReserveStatusContext
{
/**
* Public key of the reserve the inquiry is about.
*/
const struct TALER_ReservePublicKeyP *reserve_pub;
/**
* History of the reserve, set in the callback.
*/
struct TALER_EXCHANGEDB_ReserveHistory *rh;
/**
* Current reserve balance.
*/
struct TALER_Amount balance;
};
/**
* Send reserve status to client.
*
* @param connection connection to the client
* @param rh reserve history to return
* @return MHD result code
*/
static MHD_RESULT
reply_reserve_status_success (struct MHD_Connection *connection,
const struct ReserveStatusContext *rhc)
{
const struct TALER_EXCHANGEDB_ReserveHistory *rh = rhc->rh;
json_t *json_history;
json_history = TEH_RESPONSE_compile_reserve_history (rh);
if (NULL == json_history)
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
NULL);
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
TALER_JSON_pack_amount ("balance",
&rhc->balance),
GNUNET_JSON_pack_array_steal ("history",
json_history));
}
/**
* Function implementing /reserves/ STATUS transaction.
* Execute a /reserves/ STATUS. Given the public key of a reserve,
* return the associated transaction history. 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 ReserveStatusContext *`
* @param connection MHD request which triggered the transaction
* @param[out] mhd_ret set to MHD response status for @a connection,
* if transaction failed (!); unused
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
reserve_status_transaction (void *cls,
struct MHD_Connection *connection,
MHD_RESULT *mhd_ret)
{
struct ReserveStatusContext *rsc = cls;
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
rsc->reserve_pub,
&rsc->balance,
&rsc->rh);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
*mhd_ret
= TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"get_reserve_status");
}
return qs;
}
MHD_RESULT
TEH_handler_reserves_status (struct TEH_RequestContext *rc,
const struct TALER_ReservePublicKeyP *reserve_pub,
const json_t *root)
{
struct ReserveStatusContext rsc;
MHD_RESULT mhd_ret;
struct GNUNET_TIME_Timestamp timestamp;
struct TALER_ReserveSignatureP reserve_sig;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_timestamp ("request_timestamp",
&timestamp),
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
&reserve_sig),
GNUNET_JSON_spec_end ()
};
rsc.reserve_pub = reserve_pub;
{
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (rc->connection,
root,
spec);
if (GNUNET_SYSERR == res)
{
GNUNET_break (0);
return MHD_NO; /* hard failure */
}
if (GNUNET_NO == res)
{
GNUNET_break_op (0);
return MHD_YES; /* failure */
}
}
if (GNUNET_OK !=
TALER_wallet_reserve_status_verify (timestamp,
reserve_pub,
&reserve_sig))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_RESERVES_STATUS_BAD_SIGNATURE,
NULL);
}
rsc.rh = NULL;
if (GNUNET_OK !=
TEH_DB_run_transaction (rc->connection,
"get reserve status",
TEH_MT_REQUEST_OTHER,
&mhd_ret,
&reserve_status_transaction,
&rsc))
{
return mhd_ret;
}
if (NULL == rsc.rh)
{
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_EXCHANGE_RESERVES_STATUS_UNKNOWN,
NULL);
}
mhd_ret = reply_reserve_status_success (rc->connection,
&rsc);
TEH_plugin->free_reserve_history (TEH_plugin->cls,
rsc.rh);
return mhd_ret;
}
/* end of taler-exchange-httpd_reserves_status.c */

View File

@ -0,0 +1,43 @@
/*
This file is part of TALER
Copyright (C) 2014-2020 Taler Systems SA
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
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-exchange-httpd_reserves_status.h
* @brief Handle /reserves/$RESERVE_PUB STATUS requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
#ifndef TALER_EXCHANGE_HTTPD_RESERVES_STATUS_H
#define TALER_EXCHANGE_HTTPD_RESERVES_STATUS_H
#include <microhttpd.h>
#include "taler-exchange-httpd.h"
/**
* Handle a POST "/reserves/$RID/status" request.
*
* @param rc request context
* @param reserve_pub public key of the reserve
* @param root uploaded body from the client
* @return MHD result code
*/
MHD_RESULT
TEH_handler_reserves_status (struct TEH_RequestContext *rc,
const struct TALER_ReservePublicKeyP *reserve_pub,
const json_t *root);
#endif

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2021 Taler Systems SA Copyright (C) 2014-2022 Taler Systems SA
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
@ -605,31 +605,11 @@ TEH_RESPONSE_reply_coin_insufficient_funds (
} }
/**
* Compile the history of a reserve into a JSON object
* and calculate the total balance.
*
* @param rh reserve history to JSON-ify
* @param[out] balance set to current reserve balance
* @return json representation of the @a rh, NULL on error
*/
json_t * json_t *
TEH_RESPONSE_compile_reserve_history ( TEH_RESPONSE_compile_reserve_history (
const struct TALER_EXCHANGEDB_ReserveHistory *rh, const struct TALER_EXCHANGEDB_ReserveHistory *rh)
struct TALER_Amount *balance)
{ {
struct TALER_Amount credit_total;
struct TALER_Amount withdraw_total;
json_t *json_history; json_t *json_history;
enum InitAmounts
{
/** Nothing initialized */
IA_NONE = 0,
/** credit_total initialized */
IA_CREDIT = 1,
/** withdraw_total initialized */
IA_WITHDRAW = 2
} init = IA_NONE;
json_history = json_array (); json_history = json_array ();
for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh; for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
@ -642,20 +622,7 @@ TEH_RESPONSE_compile_reserve_history (
{ {
const struct TALER_EXCHANGEDB_BankTransfer *bank = const struct TALER_EXCHANGEDB_BankTransfer *bank =
pos->details.bank; pos->details.bank;
if (0 == (IA_CREDIT & init))
{
credit_total = bank->amount;
init |= IA_CREDIT;
}
else if (0 >
TALER_amount_add (&credit_total,
&credit_total,
&bank->amount))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
if (0 != if (0 !=
json_array_append_new ( json_array_append_new (
json_history, json_history,
@ -681,26 +648,7 @@ TEH_RESPONSE_compile_reserve_history (
{ {
const struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw const struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw
= pos->details.withdraw; = pos->details.withdraw;
struct TALER_Amount value;
value = withdraw->amount_with_fee;
if (0 == (IA_WITHDRAW & init))
{
withdraw_total = value;
init |= IA_WITHDRAW;
}
else
{
if (0 >
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
}
if (0 != if (0 !=
json_array_append_new ( json_array_append_new (
json_history, json_history,
@ -716,7 +664,7 @@ TEH_RESPONSE_compile_reserve_history (
TALER_JSON_pack_amount ("withdraw_fee", TALER_JSON_pack_amount ("withdraw_fee",
&withdraw->withdraw_fee), &withdraw->withdraw_fee),
TALER_JSON_pack_amount ("amount", TALER_JSON_pack_amount ("amount",
&value)))) &withdraw->amount_with_fee))))
{ {
GNUNET_break (0); GNUNET_break (0);
json_decref (json_history); json_decref (json_history);
@ -731,20 +679,6 @@ TEH_RESPONSE_compile_reserve_history (
struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig; struct TALER_ExchangeSignatureP sig;
if (0 == (IA_CREDIT & init))
{
credit_total = recoup->value;
init |= IA_CREDIT;
}
else if (0 >
TALER_amount_add (&credit_total,
&credit_total,
&recoup->value))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
{ {
struct TALER_RecoupConfirmationPS pc = { struct TALER_RecoupConfirmationPS pc = {
.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP), .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
@ -796,26 +730,7 @@ TEH_RESPONSE_compile_reserve_history (
pos->details.closing; pos->details.closing;
struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig; struct TALER_ExchangeSignatureP sig;
struct TALER_Amount value;
value = closing->amount;
if (0 == (IA_WITHDRAW & init))
{
withdraw_total = value;
init |= IA_WITHDRAW;
}
else
{
if (0 >
TALER_amount_add (&withdraw_total,
&withdraw_total,
&value))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
}
{ {
struct TALER_ReserveCloseConfirmationPS rcc = { struct TALER_ReserveCloseConfirmationPS rcc = {
.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED), .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED),
@ -826,7 +741,7 @@ TEH_RESPONSE_compile_reserve_history (
}; };
TALER_amount_hton (&rcc.closing_amount, TALER_amount_hton (&rcc.closing_amount,
&value); &closing->amount);
TALER_amount_hton (&rcc.closing_fee, TALER_amount_hton (&rcc.closing_fee,
&closing->closing_fee); &closing->closing_fee);
TALER_payto_hash (closing->receiver_account_details, TALER_payto_hash (closing->receiver_account_details,
@ -858,7 +773,7 @@ TEH_RESPONSE_compile_reserve_history (
GNUNET_JSON_pack_timestamp ("timestamp", GNUNET_JSON_pack_timestamp ("timestamp",
closing->execution_date), closing->execution_date),
TALER_JSON_pack_amount ("amount", TALER_JSON_pack_amount ("amount",
&value), &closing->amount),
TALER_JSON_pack_amount ("closing_fee", TALER_JSON_pack_amount ("closing_fee",
&closing->closing_fee)))) &closing->closing_fee))))
{ {
@ -871,31 +786,6 @@ TEH_RESPONSE_compile_reserve_history (
} }
} }
if (0 == (IA_CREDIT & init))
{
/* We should not have gotten here, without credits no reserve
should exist! */
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
if (0 == (IA_WITHDRAW & init))
{
/* did not encounter any withdraw operations, set withdraw_total to zero */
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (credit_total.currency,
&withdraw_total));
}
if (0 >
TALER_amount_subtract (balance,
&credit_total,
&withdraw_total))
{
GNUNET_break (0);
json_decref (json_history);
return NULL;
}
return json_history; return json_history;
} }

View File

@ -34,17 +34,14 @@
/** /**
* Compile the history of a reserve into a JSON object * Compile the history of a reserve into a JSON object.
* and calculate the total balance.
* *
* @param rh reserve history to JSON-ify * @param rh reserve history to JSON-ify
* @param[out] balance set to current reserve balance
* @return json representation of the @a rh, NULL on error * @return json representation of the @a rh, NULL on error
*/ */
json_t * json_t *
TEH_RESPONSE_compile_reserve_history ( TEH_RESPONSE_compile_reserve_history (
const struct TALER_EXCHANGEDB_ReserveHistory *rh, const struct TALER_EXCHANGEDB_ReserveHistory *rh);
struct TALER_Amount *balance);
/** /**

View File

@ -52,32 +52,19 @@ reply_withdraw_insufficient_funds (
const struct TALER_EXCHANGEDB_ReserveHistory *rh) const struct TALER_EXCHANGEDB_ReserveHistory *rh)
{ {
json_t *json_history; json_t *json_history;
struct TALER_Amount balance;
json_history = TEH_RESPONSE_compile_reserve_history (rh, json_history = TEH_RESPONSE_compile_reserve_history (rh);
&balance);
if (NULL == json_history) if (NULL == json_history)
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS, TALER_EC_EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS,
NULL); NULL);
if (0 !=
TALER_amount_cmp (&balance,
ebalance))
{
GNUNET_break (0);
json_decref (json_history);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
"reserve balance corrupt");
}
return TALER_MHD_REPLY_JSON_PACK ( return TALER_MHD_REPLY_JSON_PACK (
connection, connection,
MHD_HTTP_CONFLICT, MHD_HTTP_CONFLICT,
TALER_JSON_pack_ec (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS), TALER_JSON_pack_ec (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS),
TALER_JSON_pack_amount ("balance", TALER_JSON_pack_amount ("balance",
&balance), ebalance),
TALER_JSON_pack_amount ("requested_amount", TALER_JSON_pack_amount ("requested_amount",
withdraw_amount), withdraw_amount),
GNUNET_JSON_pack_array_steal ("history", GNUNET_JSON_pack_array_steal ("history",
@ -105,7 +92,6 @@ struct WithdrawContext
*/ */
struct TALER_Amount amount_with_fee; struct TALER_Amount amount_with_fee;
/** /**
* Blinded planchet. * Blinded planchet.
*/ */
@ -329,8 +315,8 @@ check_request_idempotent (struct TEH_RequestContext *rc,
MHD_RESULT MHD_RESULT
TEH_handler_withdraw (struct TEH_RequestContext *rc, TEH_handler_withdraw (struct TEH_RequestContext *rc,
const json_t *root, const struct TALER_ReservePublicKeyP *reserve_pub,
const char *const args[2]) const json_t *root)
{ {
struct WithdrawContext wc; struct WithdrawContext wc;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
@ -348,18 +334,7 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
memset (&wc, memset (&wc,
0, 0,
sizeof (wc)); sizeof (wc));
if (GNUNET_OK != wc.collectable.reserve_pub = *reserve_pub;
GNUNET_STRINGS_string_to_data (args[0],
strlen (args[0]),
&wc.collectable.reserve_pub,
sizeof (wc.collectable.reserve_pub)))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_MERCHANT_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
{ {
enum GNUNET_GenericReturnValue res; enum GNUNET_GenericReturnValue res;

View File

@ -28,8 +28,7 @@
/** /**
* Handle a "/reserves/$RESERVE_PUB/withdraw" request. Parses the * Handle a "/reserves/$RESERVE_PUB/withdraw" request. Parses the the requested "denom_pub" which
* "reserve_pub" EdDSA key of the reserve and the requested "denom_pub" which
* specifies the key/value of the coin to be withdrawn, and checks that the * specifies the key/value of the coin to be withdrawn, and checks that the
* signature "reserve_sig" makes this a valid withdrawal request from the * signature "reserve_sig" makes this a valid withdrawal request from the
* specified reserve. If so, the envelope with the blinded coin "coin_ev" is * specified reserve. If so, the envelope with the blinded coin "coin_ev" is
@ -37,13 +36,12 @@
* *
* @param rc request context * @param rc request context
* @param root uploaded JSON data * @param root uploaded JSON data
* @param args array of additional options (first must be the * @param reserve_pub public key of the reserve
* reserve public key, the second one should be "withdraw")
* @return MHD result code * @return MHD result code
*/ */
MHD_RESULT MHD_RESULT
TEH_handler_withdraw (struct TEH_RequestContext *rc, TEH_handler_withdraw (struct TEH_RequestContext *rc,
const json_t *root, const struct TALER_ReservePublicKeyP *reserve_pub,
const char *const args[2]); const json_t *root);
#endif #endif

View File

@ -732,6 +732,17 @@ prepare_statements (struct PostgresClosure *pg)
" WHERE res.reserve_pub=$1;", " WHERE res.reserve_pub=$1;",
1), 1),
/* Used in #postgres_select_withdrawals_above_serial_id() */ /* Used in #postgres_select_withdrawals_above_serial_id() */
GNUNET_PQ_make_prepare (
"get_reserve_balance",
"SELECT"
" current_balance_val"
",current_balance_frac"
" FROM reserves"
" WHERE reserve_pub=$1;",
1),
/* Fetch deposits with rowid '\geq' the given parameter */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"audit_get_reserves_out_incr", "audit_get_reserves_out_incr",
"SELECT" "SELECT"
@ -5533,6 +5544,37 @@ postgres_get_reserve_history (void *cls,
} }
/**
* Get the balance of the specified reserve.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param reserve_pub public key of the reserve
* @param[out] balance set to the reserve balance
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_get_reserve_balance (void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct TALER_Amount *balance)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
balance),
GNUNET_PQ_result_spec_end
};
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"get_reserve_balance",
params,
rs);
}
/** /**
* Check if we have the specified deposit already in the database. * Check if we have the specified deposit already in the database.
* *
@ -12503,6 +12545,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->do_refund = &postgres_do_refund; plugin->do_refund = &postgres_do_refund;
plugin->do_recoup = &postgres_do_recoup; plugin->do_recoup = &postgres_do_recoup;
plugin->do_recoup_refresh = &postgres_do_recoup_refresh; plugin->do_recoup_refresh = &postgres_do_recoup_refresh;
plugin->get_reserve_balance = &postgres_get_reserve_balance;
plugin->get_reserve_history = &postgres_get_reserve_history; plugin->get_reserve_history = &postgres_get_reserve_history;
plugin->free_reserve_history = &common_free_reserve_history; plugin->free_reserve_history = &common_free_reserve_history;
plugin->count_known_coins = &postgres_count_known_coins; plugin->count_known_coins = &postgres_count_known_coins;

View File

@ -2846,6 +2846,20 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_EXCHANGEDB_ReserveHistory **rhp); struct TALER_EXCHANGEDB_ReserveHistory **rhp);
/**
* The current reserve balance of the specified reserve.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the reserve
* @param[out] balance set to the reserve balance
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
(*get_reserve_balance)(void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct TALER_Amount *balance);
/** /**
* Free memory associated with the given reserve history. * Free memory associated with the given reserve history.
* *