big rename fest related to #6067 API renaming
This commit is contained in:
parent
de9ab28ab9
commit
0a2b049864
@ -3763,7 +3763,7 @@ refresh_session_cb (void *cls,
|
|||||||
NULL);
|
NULL);
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("refresh_melt",
|
report_row_inconsistency ("melt",
|
||||||
rowid,
|
rowid,
|
||||||
"denomination key not found");
|
"denomination key not found");
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
|
@ -374,8 +374,8 @@ run (void *cls,
|
|||||||
i,
|
i,
|
||||||
j);
|
j);
|
||||||
unit[2] =
|
unit[2] =
|
||||||
TALER_TESTING_cmd_refresh_melt_with_retry
|
TALER_TESTING_cmd_melt_with_retry
|
||||||
(TALER_TESTING_cmd_refresh_melt
|
(TALER_TESTING_cmd_melt
|
||||||
(add_label (melt_label),
|
(add_label (melt_label),
|
||||||
withdraw_label,
|
withdraw_label,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
|
@ -51,11 +51,11 @@ taler_exchange_httpd_SOURCES = \
|
|||||||
taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
|
taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
|
||||||
taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
|
taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
|
||||||
taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \
|
taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \
|
||||||
|
taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
|
||||||
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
|
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
|
||||||
taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
|
taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
|
||||||
taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
|
taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \
|
||||||
taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
|
taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \
|
||||||
taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_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_responses.c taler-exchange-httpd_responses.h \
|
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
|
||||||
|
@ -33,9 +33,9 @@
|
|||||||
#include "taler-exchange-httpd_reserves_get.h"
|
#include "taler-exchange-httpd_reserves_get.h"
|
||||||
#include "taler-exchange-httpd_withdraw.h"
|
#include "taler-exchange-httpd_withdraw.h"
|
||||||
#include "taler-exchange-httpd_recoup.h"
|
#include "taler-exchange-httpd_recoup.h"
|
||||||
#include "taler-exchange-httpd_refresh_link.h"
|
#include "taler-exchange-httpd_link.h"
|
||||||
#include "taler-exchange-httpd_refresh_melt.h"
|
#include "taler-exchange-httpd_melt.h"
|
||||||
#include "taler-exchange-httpd_refresh_reveal.h"
|
#include "taler-exchange-httpd_refreshes_reveal.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_deposits_get.h"
|
#include "taler-exchange-httpd_deposits_get.h"
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_mhd.h"
|
#include "taler-exchange-httpd_mhd.h"
|
||||||
#include "taler-exchange-httpd_refresh_link.h"
|
#include "taler-exchange-httpd_link.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
#include "taler-exchange-httpd_keystate.h"
|
#include "taler-exchange-httpd_keystate.h"
|
||||||
|
|
@ -14,7 +14,7 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file taler-exchange-httpd_refresh_melt.c
|
* @file taler-exchange-httpd_melt.c
|
||||||
* @brief Handle /refresh/melt requests
|
* @brief Handle /refresh/melt requests
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
* @author Benedikt Mueller
|
* @author Benedikt Mueller
|
||||||
@ -27,7 +27,7 @@
|
|||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_mhd.h"
|
#include "taler-exchange-httpd_mhd.h"
|
||||||
#include "taler-exchange-httpd_refresh_melt.h"
|
#include "taler-exchange-httpd_melt.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
#include "taler-exchange-httpd_keystate.h"
|
#include "taler-exchange-httpd_keystate.h"
|
||||||
|
|
||||||
@ -48,14 +48,14 @@
|
|||||||
* @return a MHD result code
|
* @return a MHD result code
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
|
reply_melt_insufficient_funds (struct MHD_Connection *connection,
|
||||||
const struct
|
const struct
|
||||||
TALER_CoinSpendPublicKeyP *coin_pub,
|
TALER_CoinSpendPublicKeyP *coin_pub,
|
||||||
const struct TALER_Amount *coin_value,
|
const struct TALER_Amount *coin_value,
|
||||||
struct TALER_EXCHANGEDB_TransactionList *
|
struct TALER_EXCHANGEDB_TransactionList *
|
||||||
tl,
|
tl,
|
||||||
const struct TALER_Amount *requested,
|
const struct TALER_Amount *requested,
|
||||||
const struct TALER_Amount *residual)
|
const struct TALER_Amount *residual)
|
||||||
{
|
{
|
||||||
json_t *history;
|
json_t *history;
|
||||||
|
|
||||||
@ -96,9 +96,9 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
|
|||||||
* @return a MHD status code
|
* @return a MHD status code
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
reply_refresh_melt_success (struct MHD_Connection *connection,
|
reply_melt_success (struct MHD_Connection *connection,
|
||||||
const struct TALER_RefreshCommitmentP *rc,
|
const struct TALER_RefreshCommitmentP *rc,
|
||||||
uint32_t noreveal_index)
|
uint32_t noreveal_index)
|
||||||
{
|
{
|
||||||
struct TALER_RefreshMeltConfirmationPS body;
|
struct TALER_RefreshMeltConfirmationPS body;
|
||||||
struct TALER_ExchangePublicKeyP pub;
|
struct TALER_ExchangePublicKeyP pub;
|
||||||
@ -139,7 +139,7 @@ struct RefreshMeltContext
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* noreveal_index is only initialized during
|
* noreveal_index is only initialized during
|
||||||
* #refresh_melt_transaction().
|
* #melt_transaction().
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
|
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
|
||||||
|
|
||||||
@ -253,14 +253,14 @@ refresh_check_melt (struct MHD_Connection *connection,
|
|||||||
TALER_amount_subtract (&coin_residual,
|
TALER_amount_subtract (&coin_residual,
|
||||||
&spent,
|
&spent,
|
||||||
&rmc->refresh_session.amount_with_fee));
|
&rmc->refresh_session.amount_with_fee));
|
||||||
*mhd_ret = reply_refresh_melt_insufficient_funds (connection,
|
*mhd_ret = reply_melt_insufficient_funds (connection,
|
||||||
&rmc->refresh_session.coin
|
&rmc->refresh_session.coin
|
||||||
.coin_pub,
|
.coin_pub,
|
||||||
&rmc->coin_value,
|
&rmc->coin_value,
|
||||||
tl,
|
tl,
|
||||||
&rmc->refresh_session.
|
&rmc->refresh_session.
|
||||||
amount_with_fee,
|
amount_with_fee,
|
||||||
&coin_residual);
|
&coin_residual);
|
||||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||||
tl);
|
tl);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
@ -294,10 +294,10 @@ refresh_check_melt (struct MHD_Connection *connection,
|
|||||||
* @return transaction status
|
* @return transaction status
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_DB_QueryStatus
|
static enum GNUNET_DB_QueryStatus
|
||||||
refresh_melt_transaction (void *cls,
|
melt_transaction (void *cls,
|
||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
struct TALER_EXCHANGEDB_Session *session,
|
struct TALER_EXCHANGEDB_Session *session,
|
||||||
int *mhd_ret)
|
int *mhd_ret)
|
||||||
{
|
{
|
||||||
struct RefreshMeltContext *rmc = cls;
|
struct RefreshMeltContext *rmc = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
@ -311,9 +311,9 @@ refresh_melt_transaction (void *cls,
|
|||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
||||||
{
|
{
|
||||||
TALER_LOG_DEBUG ("Found already-melted coin\n");
|
TALER_LOG_DEBUG ("Found already-melted coin\n");
|
||||||
*mhd_ret = reply_refresh_melt_success (connection,
|
*mhd_ret = reply_melt_success (connection,
|
||||||
&rmc->refresh_session.rc,
|
&rmc->refresh_session.rc,
|
||||||
noreveal_index);
|
noreveal_index);
|
||||||
/* Note: we return "hard error" to ensure the wrapper
|
/* Note: we return "hard error" to ensure the wrapper
|
||||||
does not retry the transaction, and to also not generate
|
does not retry the transaction, and to also not generate
|
||||||
a "fresh" response (as we would on "success") */
|
a "fresh" response (as we would on "success") */
|
||||||
@ -366,15 +366,15 @@ refresh_melt_transaction (void *cls,
|
|||||||
* happened. We now need to validate the coins being melted and the
|
* happened. We now need to validate the coins being melted and the
|
||||||
* session signature and then hand things of to execute the melt
|
* session signature and then hand things of to execute the melt
|
||||||
* operation. This function parses the JSON arrays and then passes
|
* operation. This function parses the JSON arrays and then passes
|
||||||
* processing on to #refresh_melt_transaction().
|
* processing on to #melt_transaction().
|
||||||
*
|
*
|
||||||
* @param connection the MHD connection to handle
|
* @param connection the MHD connection to handle
|
||||||
* @param[in,out] rmc details about the melt request
|
* @param[in,out] rmc details about the melt request
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
handle_refresh_melt (struct MHD_Connection *connection,
|
handle_melt (struct MHD_Connection *connection,
|
||||||
struct RefreshMeltContext *rmc)
|
struct RefreshMeltContext *rmc)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* verify signature of coin for melt operation */
|
/* verify signature of coin for melt operation */
|
||||||
@ -415,15 +415,15 @@ handle_refresh_melt (struct MHD_Connection *connection,
|
|||||||
TEH_DB_run_transaction (connection,
|
TEH_DB_run_transaction (connection,
|
||||||
"run melt",
|
"run melt",
|
||||||
&mhd_ret,
|
&mhd_ret,
|
||||||
&refresh_melt_transaction,
|
&melt_transaction,
|
||||||
rmc))
|
rmc))
|
||||||
return mhd_ret;
|
return mhd_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* generate ordinary response */
|
/* generate ordinary response */
|
||||||
return reply_refresh_melt_success (connection,
|
return reply_melt_success (connection,
|
||||||
&rmc->refresh_session.rc,
|
&rmc->refresh_session.rc,
|
||||||
rmc->refresh_session.noreveal_index);
|
rmc->refresh_session.noreveal_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -571,8 +571,8 @@ check_for_denomination_key (struct MHD_Connection *connection,
|
|||||||
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
|
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
|
||||||
"melt amount smaller than melting fee");
|
"melt amount smaller than melting fee");
|
||||||
}
|
}
|
||||||
return handle_refresh_melt (connection,
|
return handle_melt (connection,
|
||||||
rmc);
|
rmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -580,7 +580,7 @@ check_for_denomination_key (struct MHD_Connection *connection,
|
|||||||
* Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON
|
* Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON
|
||||||
* components and then hands things of to #check_for_denomination_key() to
|
* components and then hands things of to #check_for_denomination_key() to
|
||||||
* validate the melted coins, the signature and execute the melt using
|
* validate the melted coins, the signature and execute the melt using
|
||||||
* handle_refresh_melt().
|
* handle_melt().
|
||||||
|
|
||||||
* @param connection the MHD connection to handle
|
* @param connection the MHD connection to handle
|
||||||
* @param coin_pub public key of the coin
|
* @param coin_pub public key of the coin
|
||||||
@ -625,4 +625,4 @@ TEH_REFRESH_handler_melt (struct MHD_Connection *connection,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_refresh_melt.c */
|
/* end of taler-exchange-httpd_melt.c */
|
@ -14,7 +14,7 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file taler-exchange-httpd_refresh_melt.h
|
* @file taler-exchange-httpd_melt.h
|
||||||
* @brief Handle /refresh/melt requests
|
* @brief Handle /refresh/melt requests
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
* @author Benedikt Mueller
|
* @author Benedikt Mueller
|
||||||
@ -32,7 +32,7 @@
|
|||||||
* Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON
|
* Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON
|
||||||
* components and then hands things of to #check_for_denomination_key() to
|
* components and then hands things of to #check_for_denomination_key() to
|
||||||
* validate the melted coins, the signature and execute the melt using
|
* validate the melted coins, the signature and execute the melt using
|
||||||
* handle_refresh_melt().
|
* handle_melt().
|
||||||
|
|
||||||
* @param connection the MHD connection to handle
|
* @param connection the MHD connection to handle
|
||||||
* @param coin_pub public key of the coin
|
* @param coin_pub public key of the coin
|
@ -14,8 +14,8 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file taler-exchange-httpd_refresh_reveal.c
|
* @file taler-exchange-httpd_refreshes_reveal.c
|
||||||
* @brief Handle /refresh/reveal requests
|
* @brief Handle /refreshes/$RCH/reveal requests
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
* @author Benedikt Mueller
|
* @author Benedikt Mueller
|
||||||
* @author Christian Grothoff
|
* @author Christian Grothoff
|
||||||
@ -26,7 +26,7 @@
|
|||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_mhd.h"
|
#include "taler-exchange-httpd_mhd.h"
|
||||||
#include "taler-exchange-httpd_refresh_reveal.h"
|
#include "taler-exchange-httpd_refreshes_reveal.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
#include "taler-exchange-httpd_keystate.h"
|
#include "taler-exchange-httpd_keystate.h"
|
||||||
|
|
||||||
@ -299,7 +299,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
int *mhd_ret)
|
int *mhd_ret)
|
||||||
{
|
{
|
||||||
struct RevealContext *rctx = cls;
|
struct RevealContext *rctx = cls;
|
||||||
struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
|
struct TALER_EXCHANGEDB_RefreshMelt melt;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
/* Obtain basic information about the refresh operation and what
|
/* Obtain basic information about the refresh operation and what
|
||||||
@ -307,7 +307,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
qs = TEH_plugin->get_melt (TEH_plugin->cls,
|
qs = TEH_plugin->get_melt (TEH_plugin->cls,
|
||||||
session,
|
session,
|
||||||
&rctx->rc,
|
&rctx->rc,
|
||||||
&refresh_melt);
|
&melt);
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
{
|
{
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
@ -319,7 +319,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||||
return qs;
|
return qs;
|
||||||
if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
|
if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
|
||||||
(refresh_melt.session.noreveal_index >= TALER_CNC_KAPPA) )
|
(melt.session.noreveal_index >= TALER_CNC_KAPPA) )
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
@ -331,7 +331,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
|
|
||||||
/* Verify commitment */
|
/* Verify commitment */
|
||||||
{
|
{
|
||||||
/* Note that the contents of rcs[refresh_melt.session.noreveal_index]
|
/* Note that the contents of rcs[melt.session.noreveal_index]
|
||||||
will be aliased and are *not* allocated (or deallocated) in
|
will be aliased and are *not* allocated (or deallocated) in
|
||||||
this function -- in contrast to the other offsets! */
|
this function -- in contrast to the other offsets! */
|
||||||
struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
|
struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
|
||||||
@ -343,7 +343,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
{
|
{
|
||||||
struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
|
struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
|
||||||
|
|
||||||
if (i == refresh_melt.session.noreveal_index)
|
if (i == melt.session.noreveal_index)
|
||||||
{
|
{
|
||||||
/* Take these coin envelopes from the client */
|
/* Take these coin envelopes from the client */
|
||||||
rce->transfer_pub = rctx->gamma_tp;
|
rce->transfer_pub = rctx->gamma_tp;
|
||||||
@ -360,7 +360,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
|
GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
|
||||||
&rce->transfer_pub.ecdhe_pub);
|
&rce->transfer_pub.ecdhe_pub);
|
||||||
TALER_link_reveal_transfer_secret (tpriv,
|
TALER_link_reveal_transfer_secret (tpriv,
|
||||||
&refresh_melt.session.coin.coin_pub,
|
&melt.session.coin.coin_pub,
|
||||||
&ts);
|
&ts);
|
||||||
rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
|
rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
|
||||||
struct TALER_RefreshCoinData);
|
struct TALER_RefreshCoinData);
|
||||||
@ -387,15 +387,15 @@ refresh_reveal_transaction (void *cls,
|
|||||||
TALER_CNC_KAPPA,
|
TALER_CNC_KAPPA,
|
||||||
rctx->num_fresh_coins,
|
rctx->num_fresh_coins,
|
||||||
rcs,
|
rcs,
|
||||||
&refresh_melt.session.coin.coin_pub,
|
&melt.session.coin.coin_pub,
|
||||||
&refresh_melt.session.amount_with_fee);
|
&melt.session.amount_with_fee);
|
||||||
|
|
||||||
/* Free resources allocated above */
|
/* Free resources allocated above */
|
||||||
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
{
|
{
|
||||||
struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
|
struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
|
||||||
|
|
||||||
if (i == refresh_melt.session.noreveal_index)
|
if (i == melt.session.noreveal_index)
|
||||||
continue; /* This offset is special... */
|
continue; /* This offset is special... */
|
||||||
for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
|
for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
|
||||||
{
|
{
|
||||||
@ -421,7 +421,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
{
|
{
|
||||||
struct TALER_Amount refresh_cost;
|
struct TALER_Amount refresh_cost;
|
||||||
|
|
||||||
refresh_cost = refresh_melt.melt_fee;
|
refresh_cost = melt.melt_fee;
|
||||||
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
||||||
{
|
{
|
||||||
struct TALER_Amount fee_withdraw;
|
struct TALER_Amount fee_withdraw;
|
||||||
@ -450,7 +450,7 @@ refresh_reveal_transaction (void *cls,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (0 < TALER_amount_cmp (&refresh_cost,
|
if (0 < TALER_amount_cmp (&refresh_cost,
|
||||||
&refresh_melt.session.amount_with_fee))
|
&melt.session.amount_with_fee))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
@ -542,7 +542,7 @@ resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state,
|
|||||||
struct GNUNET_HashCode dki_h[num_fresh_coins];
|
struct GNUNET_HashCode dki_h[num_fresh_coins];
|
||||||
struct TALER_RefreshCoinData rcds[num_fresh_coins];
|
struct TALER_RefreshCoinData rcds[num_fresh_coins];
|
||||||
struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
|
struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
|
||||||
struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
|
struct TALER_EXCHANGEDB_RefreshMelt melt;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
/* Parse denomination key hashes */
|
/* Parse denomination key hashes */
|
||||||
@ -613,7 +613,7 @@ resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state,
|
|||||||
(qs = TEH_plugin->get_melt (TEH_plugin->cls,
|
(qs = TEH_plugin->get_melt (TEH_plugin->cls,
|
||||||
NULL,
|
NULL,
|
||||||
&rctx->rc,
|
&rctx->rc,
|
||||||
&refresh_melt)))
|
&melt)))
|
||||||
{
|
{
|
||||||
switch (qs)
|
switch (qs)
|
||||||
{
|
{
|
||||||
@ -663,7 +663,7 @@ resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state,
|
|||||||
ldp.purpose.size = htonl (sizeof (ldp));
|
ldp.purpose.size = htonl (sizeof (ldp));
|
||||||
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
|
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
|
||||||
ldp.h_denom_pub = dki_h[i];
|
ldp.h_denom_pub = dki_h[i];
|
||||||
ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
|
ldp.old_coin_pub = melt.session.coin.coin_pub;
|
||||||
ldp.transfer_pub = rctx->gamma_tp;
|
ldp.transfer_pub = rctx->gamma_tp;
|
||||||
GNUNET_CRYPTO_hash (rcds[i].coin_ev,
|
GNUNET_CRYPTO_hash (rcds[i].coin_ev,
|
||||||
rcds[i].coin_ev_size,
|
rcds[i].coin_ev_size,
|
||||||
@ -672,7 +672,7 @@ resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state,
|
|||||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
|
||||||
&ldp.purpose,
|
&ldp.purpose,
|
||||||
&link_sigs[i].eddsa_signature,
|
&link_sigs[i].eddsa_signature,
|
||||||
&refresh_melt.session.coin.coin_pub.
|
&melt.session.coin.coin_pub.
|
||||||
eddsa_pub))
|
eddsa_pub))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
@ -3389,8 +3389,8 @@ postgres_select_refunds_by_coin (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 handle to use, NULL if not run in any transaction
|
* @param session database handle to use, NULL if not run in any transaction
|
||||||
* @param rc commitment hash to use to locate the operation
|
* @param rc commitment hash to use to locate the operation
|
||||||
* @param[out] refresh_melt where to store the result; note that
|
* @param[out] melt where to store the result; note that
|
||||||
* refresh_melt->session.coin.denom_sig will be set to NULL
|
* melt->session.coin.denom_sig will be set to NULL
|
||||||
* and is not fetched by this routine (as it is not needed by the client)
|
* and is not fetched by this routine (as it is not needed by the client)
|
||||||
* @return transaction status
|
* @return transaction status
|
||||||
*/
|
*/
|
||||||
@ -3398,7 +3398,7 @@ static enum GNUNET_DB_QueryStatus
|
|||||||
postgres_get_melt (void *cls,
|
postgres_get_melt (void *cls,
|
||||||
struct TALER_EXCHANGEDB_Session *session,
|
struct TALER_EXCHANGEDB_Session *session,
|
||||||
const struct TALER_RefreshCommitmentP *rc,
|
const struct TALER_RefreshCommitmentP *rc,
|
||||||
struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
|
struct TALER_EXCHANGEDB_RefreshMelt *melt)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
@ -3407,30 +3407,30 @@ postgres_get_melt (void *cls,
|
|||||||
};
|
};
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
|
||||||
&refresh_melt->session.coin.
|
&melt->session.coin.
|
||||||
denom_pub_hash),
|
denom_pub_hash),
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
|
||||||
&refresh_melt->melt_fee),
|
&melt->melt_fee),
|
||||||
GNUNET_PQ_result_spec_uint32 ("noreveal_index",
|
GNUNET_PQ_result_spec_uint32 ("noreveal_index",
|
||||||
&refresh_melt->session.noreveal_index),
|
&melt->session.noreveal_index),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
|
GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
|
||||||
&refresh_melt->session.coin.coin_pub),
|
&melt->session.coin.coin_pub),
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
|
GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
|
||||||
&refresh_melt->session.coin_sig),
|
&melt->session.coin_sig),
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||||
&refresh_melt->session.amount_with_fee),
|
&melt->session.amount_with_fee),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
refresh_melt->session.coin.denom_sig.rsa_signature = NULL;
|
melt->session.coin.denom_sig.rsa_signature = NULL;
|
||||||
if (NULL == session)
|
if (NULL == session)
|
||||||
session = postgres_get_session (pg);
|
session = postgres_get_session (pg);
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
|
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
|
||||||
"get_melt",
|
"get_melt",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
refresh_melt->session.rc = *rc;
|
melt->session.rc = *rc;
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,7 +697,7 @@ void
|
|||||||
TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh);
|
TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /deposit *********************** */
|
/* ********************* /coins/$COIN_PUB/deposit *********************** */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -799,7 +799,7 @@ void
|
|||||||
TALER_EXCHANGE_deposit_cancel (struct TALER_EXCHANGE_DepositHandle *deposit);
|
TALER_EXCHANGE_deposit_cancel (struct TALER_EXCHANGE_DepositHandle *deposit);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /refund *********************** */
|
/* ********************* /coins/$COIN_PUB/refund *********************** */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A Refund Handle
|
* @brief A Refund Handle
|
||||||
@ -926,7 +926,7 @@ void
|
|||||||
TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund);
|
TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /reserve/status *********************** */
|
/* ********************* GET /reserves/$RESERVE_PUB *********************** */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1147,14 +1147,14 @@ TALER_EXCHANGE_reserves_get (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_reserves_get_cancel (struct
|
TALER_EXCHANGE_reserves_get_cancel (struct
|
||||||
TALER_EXCHANGE_ReservesGetHandle *rsh);
|
TALER_EXCHANGE_ReservesGetHandle *rhh);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /reserve/withdraw *********************** */
|
/* ********************* POST /reserves/$RESERVE_PUB/withdraw *********************** */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A /reserve/withdraw Handle
|
* @brief A /reserves/$RESERVE_PUB/withdraw Handle
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_WithdrawHandle;
|
struct TALER_EXCHANGE_WithdrawHandle;
|
||||||
|
|
||||||
@ -1175,13 +1175,12 @@ typedef void
|
|||||||
unsigned int http_status,
|
unsigned int http_status,
|
||||||
enum TALER_ErrorCode ec,
|
enum TALER_ErrorCode ec,
|
||||||
const struct
|
const struct
|
||||||
TALER_DenominationSignature *
|
TALER_DenominationSignature *sig,
|
||||||
sig,
|
|
||||||
const json_t *full_response);
|
const json_t *full_response);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Withdraw a coin from the exchange using a /reserve/withdraw
|
* Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
|
||||||
* request. This API is typically used by a wallet to withdraw from a
|
* request. This API is typically used by a wallet to withdraw from a
|
||||||
* reserve.
|
* reserve.
|
||||||
*
|
*
|
||||||
@ -1213,7 +1212,7 @@ TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Withdraw a coin from the exchange using a /reserve/withdraw
|
* Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
|
||||||
* request. This API is typically used by a wallet to withdraw a tip
|
* request. This API is typically used by a wallet to withdraw a tip
|
||||||
* where the reserve's signature was created by the merchant already.
|
* where the reserve's signature was created by the merchant already.
|
||||||
*
|
*
|
||||||
@ -1252,12 +1251,10 @@ TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
* Cancel a withdraw status request. This function cannot be used
|
* Cancel a withdraw status request. This function cannot be used
|
||||||
* on a request handle if a response is already served for it.
|
* on a request handle if a response is already served for it.
|
||||||
*
|
*
|
||||||
* @param sign the withdraw sign request handle
|
* @param wh the withdraw handle
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_withdraw_cancel (struct
|
TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh);
|
||||||
TALER_EXCHANGE_WithdrawHandle *
|
|
||||||
sign);
|
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /refresh/melt+reveal ***************************** */
|
/* ********************* /refresh/melt+reveal ***************************** */
|
||||||
@ -1273,7 +1270,7 @@ TALER_EXCHANGE_withdraw_cancel (struct
|
|||||||
* no money is lost in case of hardware failures, is operation does
|
* no money is lost in case of hardware failures, is operation does
|
||||||
* not actually initiate the request. Instead, it generates a buffer
|
* not actually initiate the request. Instead, it generates a buffer
|
||||||
* which the caller must store before proceeding with the actual call
|
* which the caller must store before proceeding with the actual call
|
||||||
* to #TALER_EXCHANGE_refresh_melt() that will generate the request.
|
* to #TALER_EXCHANGE_melt() that will generate the request.
|
||||||
*
|
*
|
||||||
* This function does verify that the given request data is internally
|
* This function does verify that the given request data is internally
|
||||||
* consistent. However, the @a melts_sigs are NOT verified.
|
* consistent. However, the @a melts_sigs are NOT verified.
|
||||||
@ -1298,7 +1295,7 @@ TALER_EXCHANGE_withdraw_cancel (struct
|
|||||||
* @return NULL
|
* @return NULL
|
||||||
* if the inputs are invalid (i.e. denomination key not with this exchange).
|
* if the inputs are invalid (i.e. denomination key not with this exchange).
|
||||||
* Otherwise, pointer to a buffer of @a res_size to store persistently
|
* Otherwise, pointer to a buffer of @a res_size to store persistently
|
||||||
* before proceeding to #TALER_EXCHANGE_refresh_melt().
|
* before proceeding to #TALER_EXCHANGE_melt().
|
||||||
* Non-null results should be freed using GNUNET_free().
|
* Non-null results should be freed using GNUNET_free().
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
@ -1315,18 +1312,19 @@ TALER_EXCHANGE_refresh_prepare (const struct
|
|||||||
size_t *res_size);
|
size_t *res_size);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /refresh/melt ***************************** */
|
/* ********************* /coins/$COIN_PUB/melt ***************************** */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A /refresh/melt Handle
|
* @brief A /coins/$COIN_PUB/melt Handle
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshMeltHandle;
|
struct TALER_EXCHANGE_MeltHandle;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callbacks of this type are used to notify the application about the
|
* Callbacks of this type are used to notify the application about the result
|
||||||
* result of the /refresh/melt stage. If successful, the @a noreveal_index
|
* of the /coins/$COIN_PUB/melt stage. If successful, the @a noreveal_index
|
||||||
* should be committed to disk prior to proceeding #TALER_EXCHANGE_refresh_reveal().
|
* should be committed to disk prior to proceeding
|
||||||
|
* #TALER_EXCHANGE_refreshes_reveal().
|
||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for successful intermediate response this callback is skipped.
|
* @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for successful intermediate response this callback is skipped.
|
||||||
@ -1338,17 +1336,16 @@ struct TALER_EXCHANGE_RefreshMeltHandle;
|
|||||||
* @param full_response full response from the exchange (for logging, in case of errors)
|
* @param full_response full response from the exchange (for logging, in case of errors)
|
||||||
*/
|
*/
|
||||||
typedef void
|
typedef void
|
||||||
(*TALER_EXCHANGE_RefreshMeltCallback) (void *cls,
|
(*TALER_EXCHANGE_MeltCallback) (void *cls,
|
||||||
unsigned int http_status,
|
unsigned int http_status,
|
||||||
enum TALER_ErrorCode ec,
|
enum TALER_ErrorCode ec,
|
||||||
uint32_t noreveal_index,
|
uint32_t noreveal_index,
|
||||||
const struct
|
const struct TALER_ExchangePublicKeyP *sign_key,
|
||||||
TALER_ExchangePublicKeyP *sign_key,
|
const json_t *full_response);
|
||||||
const json_t *full_response);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit a refresh melt request to the exchange and get the exchange's
|
* Submit a melt request to the exchange and get the exchange's
|
||||||
* response.
|
* response.
|
||||||
*
|
*
|
||||||
* This API is typically used by a wallet. Note that to ensure that
|
* This API is typically used by a wallet. Note that to ensure that
|
||||||
@ -1367,26 +1364,25 @@ typedef void
|
|||||||
* @return a handle for this request; NULL if the argument was invalid.
|
* @return a handle for this request; NULL if the argument was invalid.
|
||||||
* In this case, neither callback will be called.
|
* In this case, neither callback will be called.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshMeltHandle *
|
struct TALER_EXCHANGE_MeltHandle *
|
||||||
TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
size_t refresh_data_length,
|
size_t refresh_data_length,
|
||||||
const char *refresh_data,
|
const char *refresh_data,
|
||||||
TALER_EXCHANGE_RefreshMeltCallback melt_cb,
|
TALER_EXCHANGE_MeltCallback melt_cb,
|
||||||
void *melt_cb_cls);
|
void *melt_cb_cls);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel a refresh melt request. This function cannot be used
|
* Cancel a melt request. This function cannot be used
|
||||||
* on a request handle if the callback was already invoked.
|
* on a request handle if the callback was already invoked.
|
||||||
*
|
*
|
||||||
* @param rmh the refresh handle
|
* @param mh the melt handle
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_refresh_melt_cancel (struct
|
TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh);
|
||||||
TALER_EXCHANGE_RefreshMeltHandle *rmh);
|
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /refresh/reveal ***************************** */
|
/* ********************* /refreshes/$RCH/reveal ***************************** */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1407,25 +1403,25 @@ TALER_EXCHANGE_refresh_melt_cancel (struct
|
|||||||
* @param full_response full response from the exchange (for logging, in case of errors)
|
* @param full_response full response from the exchange (for logging, in case of errors)
|
||||||
*/
|
*/
|
||||||
typedef void
|
typedef void
|
||||||
(*TALER_EXCHANGE_RefreshRevealCallback) (void *cls,
|
(*TALER_EXCHANGE_RefreshesRevealCallback)(void *cls,
|
||||||
unsigned int http_status,
|
unsigned int http_status,
|
||||||
enum TALER_ErrorCode ec,
|
enum TALER_ErrorCode ec,
|
||||||
unsigned int num_coins,
|
unsigned int num_coins,
|
||||||
const struct
|
const struct
|
||||||
TALER_PlanchetSecretsP *coin_privs,
|
TALER_PlanchetSecretsP *coin_privs,
|
||||||
const struct
|
const struct
|
||||||
TALER_DenominationSignature *sigs,
|
TALER_DenominationSignature *sigs,
|
||||||
const json_t *full_response);
|
const json_t *full_response);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A /refresh/reveal Handle
|
* @brief A /refreshes/$RCH/reveal Handle
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshRevealHandle;
|
struct TALER_EXCHANGE_RefreshesRevealHandle;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit a /refresh/reval request to the exchange and get the exchange's
|
* Submit a /refreshes/$RCH/reval request to the exchange and get the exchange's
|
||||||
* response.
|
* response.
|
||||||
*
|
*
|
||||||
* This API is typically used by a wallet. Note that to ensure that
|
* This API is typically used by a wallet. Note that to ensure that
|
||||||
@ -1439,20 +1435,21 @@ struct TALER_EXCHANGE_RefreshRevealHandle;
|
|||||||
* @param refresh_data the refresh data as returned from
|
* @param refresh_data the refresh data as returned from
|
||||||
#TALER_EXCHANGE_refresh_prepare())
|
#TALER_EXCHANGE_refresh_prepare())
|
||||||
* @param noreveal_index response from the exchange to the
|
* @param noreveal_index response from the exchange to the
|
||||||
* #TALER_EXCHANGE_refresh_melt() invocation
|
* #TALER_EXCHANGE_melt() invocation
|
||||||
* @param reveal_cb the callback to call with the final result of the
|
* @param reveal_cb the callback to call with the final result of the
|
||||||
* refresh operation
|
* refresh operation
|
||||||
* @param reveal_cb_cls closure for the above callback
|
* @param reveal_cb_cls closure for the above callback
|
||||||
* @return a handle for this request; NULL if the argument was invalid.
|
* @return a handle for this request; NULL if the argument was invalid.
|
||||||
* In this case, neither callback will be called.
|
* In this case, neither callback will be called.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshRevealHandle *
|
struct TALER_EXCHANGE_RefreshesRevealHandle *
|
||||||
TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_refreshes_reveal (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
size_t refresh_data_length,
|
size_t refresh_data_length,
|
||||||
const char *refresh_data,
|
const char *refresh_data,
|
||||||
uint32_t noreveal_index,
|
uint32_t noreveal_index,
|
||||||
TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
|
TALER_EXCHANGE_RefreshesRevealCallback
|
||||||
void *reveal_cb_cls);
|
reveal_cb,
|
||||||
|
void *reveal_cb_cls);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1462,24 +1459,25 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
* @param rrh the refresh reval handle
|
* @param rrh the refresh reval handle
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_refresh_reveal_cancel (struct
|
TALER_EXCHANGE_refreshes_reveal_cancel (struct
|
||||||
TALER_EXCHANGE_RefreshRevealHandle *rrh);
|
TALER_EXCHANGE_RefreshesRevealHandle *
|
||||||
|
rrh);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /refresh/link ***************************** */
|
/* ********************* /coins/$COIN_PUB/link ***************************** */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A /refresh/link Handle
|
* @brief A /coins/$COIN_PUB/link Handle
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle;
|
struct TALER_EXCHANGE_LinkHandle;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callbacks of this type are used to return the final result of
|
* Callbacks of this type are used to return the final result of submitting a
|
||||||
* submitting a /refresh/link request to a exchange. If the operation was
|
* /coins/$COIN_PUB/link request to a exchange. If the operation was
|
||||||
* successful, this function returns the signatures over the coins
|
* successful, this function returns the signatures over the coins that were
|
||||||
* that were created when the original coin was melted.
|
* created when the original coin was melted.
|
||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
|
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
|
||||||
@ -1492,26 +1490,24 @@ struct TALER_EXCHANGE_RefreshLinkHandle;
|
|||||||
* @param full_response full response from the exchange (for logging, in case of errors)
|
* @param full_response full response from the exchange (for logging, in case of errors)
|
||||||
*/
|
*/
|
||||||
typedef void
|
typedef void
|
||||||
(*TALER_EXCHANGE_RefreshLinkCallback) (void *cls,
|
(*TALER_EXCHANGE_LinkCallback) (void *cls,
|
||||||
unsigned int http_status,
|
unsigned int http_status,
|
||||||
enum TALER_ErrorCode ec,
|
enum TALER_ErrorCode ec,
|
||||||
unsigned int num_coins,
|
unsigned int num_coins,
|
||||||
const struct
|
const struct
|
||||||
TALER_CoinSpendPrivateKeyP *coin_privs,
|
TALER_CoinSpendPrivateKeyP *coin_privs,
|
||||||
const struct
|
const struct
|
||||||
TALER_DenominationSignature *sigs,
|
TALER_DenominationSignature *sigs,
|
||||||
const struct
|
const struct
|
||||||
TALER_DenominationPublicKey *pubs,
|
TALER_DenominationPublicKey *pubs,
|
||||||
const json_t *full_response);
|
const json_t *full_response);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit a refresh link request to the exchange and get the
|
* Submit a link request to the exchange and get the exchange's response.
|
||||||
* exchange's response.
|
|
||||||
*
|
*
|
||||||
* This API is typically not used by anyone, it is more a threat
|
* This API is typically not used by anyone, it is more a threat against those
|
||||||
* against those trying to receive a funds transfer by abusing the
|
* trying to receive a funds transfer by abusing the refresh protocol.
|
||||||
* /refresh protocol.
|
|
||||||
*
|
*
|
||||||
* @param exchange the exchange handle; the exchange must be ready to operate
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
* @param coin_priv private key to request link data for
|
* @param coin_priv private key to request link data for
|
||||||
@ -1520,22 +1516,21 @@ typedef void
|
|||||||
* @param link_cb_cls closure for @a link_cb
|
* @param link_cb_cls closure for @a link_cb
|
||||||
* @return a handle for this request
|
* @return a handle for this request
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle *
|
struct TALER_EXCHANGE_LinkHandle *
|
||||||
TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
|
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
|
||||||
TALER_EXCHANGE_RefreshLinkCallback link_cb,
|
TALER_EXCHANGE_LinkCallback link_cb,
|
||||||
void *link_cb_cls);
|
void *link_cb_cls);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel a refresh link request. This function cannot be used
|
* Cancel a link request. This function cannot be used
|
||||||
* on a request handle if the callback was already invoked.
|
* on a request handle if the callback was already invoked.
|
||||||
*
|
*
|
||||||
* @param rlh the refresh link handle
|
* @param lh the link handle
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_refresh_link_cancel (struct
|
TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh);
|
||||||
TALER_EXCHANGE_RefreshLinkHandle *rlh);
|
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /transfers/$WTID *********************** */
|
/* ********************* /transfers/$WTID *********************** */
|
||||||
@ -1709,6 +1704,46 @@ TALER_EXCHANGE_verify_coin_history (const struct
|
|||||||
struct TALER_Amount *total);
|
struct TALER_Amount *total);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse history given in JSON format and return it in binary
|
||||||
|
* format.
|
||||||
|
*
|
||||||
|
* @param exchange connection to the exchange we can use
|
||||||
|
* @param history JSON array with the history
|
||||||
|
* @param reserve_pub public key of the reserve to inspect
|
||||||
|
* @param currency currency we expect the balance to be in
|
||||||
|
* @param[out] balance final balance
|
||||||
|
* @param history_length number of entries in @a history
|
||||||
|
* @param[out] rhistory array of length @a history_length, set to the
|
||||||
|
* parsed history entries
|
||||||
|
* @return #GNUNET_OK if history was valid and @a rhistory and @a balance
|
||||||
|
* were set,
|
||||||
|
* #GNUNET_SYSERR if there was a protocol violation in @a history
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
TALER_EXCHANGE_parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
const json_t *history,
|
||||||
|
const struct
|
||||||
|
TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
const char *currency,
|
||||||
|
struct TALER_Amount *balance,
|
||||||
|
unsigned int history_length,
|
||||||
|
struct TALER_EXCHANGE_ReserveHistory *
|
||||||
|
rhistory);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free memory (potentially) allocated by #TALER_EXCHANGE_parse_reserve_history().
|
||||||
|
*
|
||||||
|
* @param rhistory result to free
|
||||||
|
* @param len number of entries in @a rhistory
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_free_reserve_history (struct
|
||||||
|
TALER_EXCHANGE_ReserveHistory *rhistory,
|
||||||
|
unsigned int len);
|
||||||
|
|
||||||
|
|
||||||
/* ********************* /recoup *********************** */
|
/* ********************* /recoup *********************** */
|
||||||
|
|
||||||
|
|
||||||
|
@ -2087,8 +2087,8 @@ 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 handle to use
|
* @param session database handle to use
|
||||||
* @param rc commitment to use for the lookup
|
* @param rc commitment to use for the lookup
|
||||||
* @param[out] refresh_melt where to store the result; note that
|
* @param[out] melt where to store the result; note that
|
||||||
* refresh_melt->session.coin.denom_sig will be set to NULL
|
* melt->session.coin.denom_sig will be set to NULL
|
||||||
* and is not fetched by this routine (as it is not needed by the client)
|
* and is not fetched by this routine (as it is not needed by the client)
|
||||||
* @return transaction status
|
* @return transaction status
|
||||||
*/
|
*/
|
||||||
@ -2096,7 +2096,7 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
(*get_melt)(void *cls,
|
(*get_melt)(void *cls,
|
||||||
struct TALER_EXCHANGEDB_Session *session,
|
struct TALER_EXCHANGEDB_Session *session,
|
||||||
const struct TALER_RefreshCommitmentP *rc,
|
const struct TALER_RefreshCommitmentP *rc,
|
||||||
struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt);
|
struct TALER_EXCHANGEDB_RefreshMelt *melt);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1307,10 +1307,10 @@ TALER_TESTING_cmd_deposit_with_retry (struct TALER_TESTING_Command cmd);
|
|||||||
* @return the command.
|
* @return the command.
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_refresh_melt (const char *label,
|
TALER_TESTING_cmd_melt (const char *label,
|
||||||
const char *coin_reference,
|
const char *coin_reference,
|
||||||
unsigned int expected_response_code,
|
unsigned int expected_response_code,
|
||||||
...);
|
...);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1326,10 +1326,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
|
|||||||
* @return the command.
|
* @return the command.
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_refresh_melt_double (const char *label,
|
TALER_TESTING_cmd_melt_double (const char *label,
|
||||||
const char *coin_reference,
|
const char *coin_reference,
|
||||||
unsigned int expected_response_code,
|
unsigned int expected_response_code,
|
||||||
...);
|
...);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1339,7 +1339,7 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
|
|||||||
* @return modified command.
|
* @return modified command.
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd);
|
TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,13 +25,16 @@ libtalerexchange_la_SOURCES = \
|
|||||||
exchange_api_common.c \
|
exchange_api_common.c \
|
||||||
exchange_api_handle.c exchange_api_handle.h \
|
exchange_api_handle.c exchange_api_handle.h \
|
||||||
exchange_api_deposit.c \
|
exchange_api_deposit.c \
|
||||||
|
exchange_api_deposits_get.c \
|
||||||
|
exchange_api_link.c \
|
||||||
|
exchange_api_melt.c \
|
||||||
exchange_api_recoup.c \
|
exchange_api_recoup.c \
|
||||||
exchange_api_refresh.c \
|
exchange_api_refresh_common.c exchange_api_refresh_common.h \
|
||||||
exchange_api_refresh_link.c \
|
exchange_api_refreshes_reveal.c \
|
||||||
exchange_api_refund.c \
|
exchange_api_refund.c \
|
||||||
exchange_api_reserve.c \
|
exchange_api_reserves_get.c \
|
||||||
exchange_api_track_transaction.c \
|
exchange_api_transfers_get.c \
|
||||||
exchange_api_track_transfer.c \
|
exchange_api_withdraw.c \
|
||||||
exchange_api_wire.c
|
exchange_api_wire.c
|
||||||
libtalerexchange_la_LIBADD = \
|
libtalerexchange_la_LIBADD = \
|
||||||
libtalerauditor.la \
|
libtalerauditor.la \
|
||||||
|
@ -26,6 +26,422 @@
|
|||||||
#include "taler_signatures.h"
|
#include "taler_signatures.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse history given in JSON format and return it in binary
|
||||||
|
* format.
|
||||||
|
*
|
||||||
|
* @param exchange connection to the exchange we can use
|
||||||
|
* @param history JSON array with the history
|
||||||
|
* @param reserve_pub public key of the reserve to inspect
|
||||||
|
* @param currency currency we expect the balance to be in
|
||||||
|
* @param[out] balance final balance
|
||||||
|
* @param history_length number of entries in @a history
|
||||||
|
* @param[out] rhistory array of length @a history_length, set to the
|
||||||
|
* parsed history entries
|
||||||
|
* @return #GNUNET_OK if history was valid and @a rhistory and @a balance
|
||||||
|
* were set,
|
||||||
|
* #GNUNET_SYSERR if there was a protocol violation in @a history
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
TALER_EXCHANGE_parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
const json_t *history,
|
||||||
|
const struct
|
||||||
|
TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
const char *currency,
|
||||||
|
struct TALER_Amount *balance,
|
||||||
|
unsigned int history_length,
|
||||||
|
struct TALER_EXCHANGE_ReserveHistory *
|
||||||
|
rhistory)
|
||||||
|
{
|
||||||
|
struct GNUNET_HashCode uuid[history_length];
|
||||||
|
unsigned int uuid_off;
|
||||||
|
struct TALER_Amount total_in;
|
||||||
|
struct TALER_Amount total_out;
|
||||||
|
size_t off;
|
||||||
|
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_in));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_out));
|
||||||
|
uuid_off = 0;
|
||||||
|
for (off = 0; off<history_length; off++)
|
||||||
|
{
|
||||||
|
json_t *transaction;
|
||||||
|
struct TALER_Amount amount;
|
||||||
|
const char *type;
|
||||||
|
struct GNUNET_JSON_Specification hist_spec[] = {
|
||||||
|
GNUNET_JSON_spec_string ("type", &type),
|
||||||
|
TALER_JSON_spec_amount ("amount",
|
||||||
|
&amount),
|
||||||
|
/* 'wire' and 'signature' are optional depending on 'type'! */
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
transaction = json_array_get (history,
|
||||||
|
off);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (transaction,
|
||||||
|
hist_spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
rhistory[off].amount = amount;
|
||||||
|
|
||||||
|
if (0 == strcasecmp (type,
|
||||||
|
"DEPOSIT"))
|
||||||
|
{
|
||||||
|
const char *wire_url;
|
||||||
|
void *wire_reference;
|
||||||
|
size_t wire_reference_size;
|
||||||
|
struct GNUNET_TIME_Absolute timestamp;
|
||||||
|
|
||||||
|
struct GNUNET_JSON_Specification withdraw_spec[] = {
|
||||||
|
GNUNET_JSON_spec_varsize ("wire_reference",
|
||||||
|
&wire_reference,
|
||||||
|
&wire_reference_size),
|
||||||
|
GNUNET_JSON_spec_absolute_time ("timestamp",
|
||||||
|
×tamp),
|
||||||
|
GNUNET_JSON_spec_string ("sender_account_url",
|
||||||
|
&wire_url),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total_in,
|
||||||
|
&total_in,
|
||||||
|
&amount))
|
||||||
|
{
|
||||||
|
/* overflow in history already!? inconceivable! Bad exchange! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (transaction,
|
||||||
|
withdraw_spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url);
|
||||||
|
rhistory[off].details.in_details.wire_reference = wire_reference;
|
||||||
|
rhistory[off].details.in_details.wire_reference_size =
|
||||||
|
wire_reference_size;
|
||||||
|
rhistory[off].details.in_details.timestamp = timestamp;
|
||||||
|
/* end type==DEPOSIT */
|
||||||
|
}
|
||||||
|
else if (0 == strcasecmp (type,
|
||||||
|
"WITHDRAW"))
|
||||||
|
{
|
||||||
|
struct TALER_ReserveSignatureP sig;
|
||||||
|
struct TALER_WithdrawRequestPS withdraw_purpose;
|
||||||
|
struct GNUNET_JSON_Specification withdraw_spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||||
|
&sig),
|
||||||
|
TALER_JSON_spec_amount_nbo ("withdraw_fee",
|
||||||
|
&withdraw_purpose.withdraw_fee),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
|
||||||
|
&withdraw_purpose.h_denomination_pub),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
|
||||||
|
&withdraw_purpose.h_coin_envelope),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
rhistory[off].type = TALER_EXCHANGE_RTT_WITHDRAWAL;
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (transaction,
|
||||||
|
withdraw_spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
withdraw_purpose.purpose.size
|
||||||
|
= htonl (sizeof (withdraw_purpose));
|
||||||
|
withdraw_purpose.purpose.purpose
|
||||||
|
= htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
||||||
|
withdraw_purpose.reserve_pub = *reserve_pub;
|
||||||
|
TALER_amount_hton (&withdraw_purpose.amount_with_fee,
|
||||||
|
&amount);
|
||||||
|
/* Check that the signature is a valid withdraw request */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
|
||||||
|
&withdraw_purpose.purpose,
|
||||||
|
&sig.eddsa_signature,
|
||||||
|
&reserve_pub->eddsa_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (withdraw_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
/* check that withdraw fee matches expectations! */
|
||||||
|
{
|
||||||
|
const struct TALER_EXCHANGE_Keys *key_state;
|
||||||
|
const struct TALER_EXCHANGE_DenomPublicKey *dki;
|
||||||
|
struct TALER_Amount fee;
|
||||||
|
|
||||||
|
key_state = TALER_EXCHANGE_get_keys (exchange);
|
||||||
|
dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
|
||||||
|
&withdraw_purpose.
|
||||||
|
h_denomination_pub);
|
||||||
|
TALER_amount_ntoh (&fee,
|
||||||
|
&withdraw_purpose.withdraw_fee);
|
||||||
|
if ( (GNUNET_YES !=
|
||||||
|
TALER_amount_cmp_currency (&fee,
|
||||||
|
&dki->fee_withdraw)) ||
|
||||||
|
(0 !=
|
||||||
|
TALER_amount_cmp (&fee,
|
||||||
|
&dki->fee_withdraw)) )
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (withdraw_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rhistory[off].details.out_authorization_sig
|
||||||
|
= json_object_get (transaction,
|
||||||
|
"signature");
|
||||||
|
/* Check check that the same withdraw transaction
|
||||||
|
isn't listed twice by the exchange. We use the
|
||||||
|
"uuid" array to remember the hashes of all
|
||||||
|
purposes, and compare the hashes to find
|
||||||
|
duplicates. *///
|
||||||
|
GNUNET_CRYPTO_hash (&withdraw_purpose,
|
||||||
|
ntohl (withdraw_purpose.purpose.size),
|
||||||
|
&uuid[uuid_off]);
|
||||||
|
for (unsigned int i = 0; i<uuid_off; i++)
|
||||||
|
{
|
||||||
|
if (0 == GNUNET_memcmp (&uuid[uuid_off],
|
||||||
|
&uuid[i]))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (withdraw_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uuid_off++;
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total_out,
|
||||||
|
&total_out,
|
||||||
|
&amount))
|
||||||
|
{
|
||||||
|
/* overflow in history already!? inconceivable! Bad exchange! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (withdraw_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
/* end type==WITHDRAW */
|
||||||
|
}
|
||||||
|
else if (0 == strcasecmp (type,
|
||||||
|
"RECOUP"))
|
||||||
|
{
|
||||||
|
struct TALER_RecoupConfirmationPS pc;
|
||||||
|
struct GNUNET_TIME_Absolute timestamp;
|
||||||
|
const struct TALER_EXCHANGE_Keys *key_state;
|
||||||
|
struct GNUNET_JSON_Specification recoup_spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("coin_pub",
|
||||||
|
&pc.coin_pub),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
|
||||||
|
&rhistory[off].details.recoup_details.
|
||||||
|
exchange_sig),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
|
||||||
|
&rhistory[off].details.recoup_details.
|
||||||
|
exchange_pub),
|
||||||
|
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
|
||||||
|
&pc.timestamp),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
rhistory[off].type = TALER_EXCHANGE_RTT_RECOUP;
|
||||||
|
rhistory[off].amount = amount;
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (transaction,
|
||||||
|
recoup_spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
rhistory[off].details.recoup_details.coin_pub = pc.coin_pub;
|
||||||
|
TALER_amount_hton (&pc.recoup_amount,
|
||||||
|
&amount);
|
||||||
|
pc.purpose.size = htonl (sizeof (pc));
|
||||||
|
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP);
|
||||||
|
pc.reserve_pub = *reserve_pub;
|
||||||
|
timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
|
||||||
|
rhistory[off].details.recoup_details.timestamp = timestamp;
|
||||||
|
|
||||||
|
key_state = TALER_EXCHANGE_get_keys (exchange);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_EXCHANGE_test_signing_key (key_state,
|
||||||
|
&rhistory[off].details.
|
||||||
|
recoup_details.exchange_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
|
||||||
|
&pc.purpose,
|
||||||
|
&rhistory[off].details.recoup_details.
|
||||||
|
exchange_sig.eddsa_signature,
|
||||||
|
&rhistory[off].details.recoup_details.
|
||||||
|
exchange_pub.eddsa_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total_in,
|
||||||
|
&total_in,
|
||||||
|
&rhistory[off].amount))
|
||||||
|
{
|
||||||
|
/* overflow in history already!? inconceivable! Bad exchange! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
/* end type==RECOUP */
|
||||||
|
}
|
||||||
|
else if (0 == strcasecmp (type,
|
||||||
|
"CLOSING"))
|
||||||
|
{
|
||||||
|
const struct TALER_EXCHANGE_Keys *key_state;
|
||||||
|
struct TALER_ReserveCloseConfirmationPS rcc;
|
||||||
|
struct GNUNET_TIME_Absolute timestamp;
|
||||||
|
struct GNUNET_JSON_Specification closing_spec[] = {
|
||||||
|
GNUNET_JSON_spec_string ("receiver_account_details",
|
||||||
|
&rhistory[off].details.close_details.
|
||||||
|
receiver_account_details),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("wtid",
|
||||||
|
&rhistory[off].details.close_details.wtid),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
|
||||||
|
&rhistory[off].details.close_details.
|
||||||
|
exchange_sig),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
|
||||||
|
&rhistory[off].details.close_details.
|
||||||
|
exchange_pub),
|
||||||
|
TALER_JSON_spec_amount_nbo ("closing_fee",
|
||||||
|
&rcc.closing_fee),
|
||||||
|
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
|
||||||
|
&rcc.timestamp),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
rhistory[off].type = TALER_EXCHANGE_RTT_CLOSE;
|
||||||
|
rhistory[off].amount = amount;
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (transaction,
|
||||||
|
closing_spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
TALER_amount_hton (&rcc.closing_amount,
|
||||||
|
&amount);
|
||||||
|
GNUNET_CRYPTO_hash (
|
||||||
|
rhistory[off].details.close_details.receiver_account_details,
|
||||||
|
strlen (
|
||||||
|
rhistory[off].details.close_details.receiver_account_details) + 1,
|
||||||
|
&rcc.h_wire);
|
||||||
|
rcc.wtid = rhistory[off].details.close_details.wtid;
|
||||||
|
rcc.purpose.size = htonl (sizeof (rcc));
|
||||||
|
rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
|
||||||
|
rcc.reserve_pub = *reserve_pub;
|
||||||
|
timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
|
||||||
|
rhistory[off].details.close_details.timestamp = timestamp;
|
||||||
|
|
||||||
|
key_state = TALER_EXCHANGE_get_keys (exchange);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_EXCHANGE_test_signing_key (key_state,
|
||||||
|
&rhistory[off].details.close_details.
|
||||||
|
exchange_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
|
||||||
|
&rcc.purpose,
|
||||||
|
&rhistory[off].details.close_details.
|
||||||
|
exchange_sig.eddsa_signature,
|
||||||
|
&rhistory[off].details.close_details.
|
||||||
|
exchange_pub.eddsa_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total_out,
|
||||||
|
&total_out,
|
||||||
|
&rhistory[off].amount))
|
||||||
|
{
|
||||||
|
/* overflow in history already!? inconceivable! Bad exchange! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
/* end type==CLOSING */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* unexpected 'type', protocol incompatibility, complain! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check balance = total_in - total_out < withdraw-amount */
|
||||||
|
if (GNUNET_SYSERR ==
|
||||||
|
TALER_amount_subtract (balance,
|
||||||
|
&total_in,
|
||||||
|
&total_out))
|
||||||
|
{
|
||||||
|
/* total_in < total_out, why did the exchange ever allow this!? */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free memory (potentially) allocated by #TALER_EXCHANGE_parse_reserve_history().
|
||||||
|
*
|
||||||
|
* @param rhistory result to free
|
||||||
|
* @param len number of entries in @a rhistory
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_free_reserve_history (struct
|
||||||
|
TALER_EXCHANGE_ReserveHistory *rhistory,
|
||||||
|
unsigned int len)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i<len; i++)
|
||||||
|
{
|
||||||
|
switch (rhistory[i].type)
|
||||||
|
{
|
||||||
|
case TALER_EXCHANGE_RTT_DEPOSIT:
|
||||||
|
GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
|
||||||
|
GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
|
||||||
|
break;
|
||||||
|
case TALER_EXCHANGE_RTT_WITHDRAWAL:
|
||||||
|
break;
|
||||||
|
case TALER_EXCHANGE_RTT_RECOUP:
|
||||||
|
break;
|
||||||
|
case TALER_EXCHANGE_RTT_CLOSE:
|
||||||
|
/* FIXME: should we free "receiver_account_details" ? */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GNUNET_free (rhistory);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a coins transaction history as returned by the exchange.
|
* Verify a coins transaction history as returned by the exchange.
|
||||||
*
|
*
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
<http://www.gnu.org/licenses/>
|
<http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file lib/exchange_api_track_transaction.c
|
* @file lib/exchange_api_deposits_get.c
|
||||||
* @brief Implementation of the /track/transaction request of the exchange's HTTP API
|
* @brief Implementation of the /deposits/ GET request
|
||||||
* @author Christian Grothoff
|
* @author Christian Grothoff
|
||||||
*/
|
*/
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
@ -378,15 +378,13 @@ TALER_EXCHANGE_deposits_get (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel deposit wtid request. This function cannot be used on a request
|
* Cancel /deposits/$WTID request. This function cannot be used on a request
|
||||||
* handle if a response is already served for it.
|
* handle if a response is already served for it.
|
||||||
*
|
*
|
||||||
* @param dwh the wire deposits request handle
|
* @param dwh the wire deposits request handle
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_deposits_get_cancel (struct
|
TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh)
|
||||||
TALER_EXCHANGE_DepositGetHandle *
|
|
||||||
dwh)
|
|
||||||
{
|
{
|
||||||
if (NULL != dwh->job)
|
if (NULL != dwh->job)
|
||||||
{
|
{
|
||||||
@ -399,4 +397,4 @@ TALER_EXCHANGE_deposits_get_cancel (struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* end of exchange_api_track_transaction.c */
|
/* end of exchange_api_deposits_get.c */
|
@ -15,8 +15,8 @@
|
|||||||
<http://www.gnu.org/licenses/>
|
<http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file lib/exchange_api_refresh_link.c
|
* @file lib/exchange_api_link.c
|
||||||
* @brief Implementation of the /refresh/link request of the exchange's HTTP API
|
* @brief Implementation of the /coins/$COIN_PUB/link request
|
||||||
* @author Christian Grothoff
|
* @author Christian Grothoff
|
||||||
*/
|
*/
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
@ -31,9 +31,9 @@
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A /refresh/link Handle
|
* @brief A /coins/$COIN_PUB/link Handle
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle
|
struct TALER_EXCHANGE_LinkHandle
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +54,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
|
|||||||
/**
|
/**
|
||||||
* Function to call with the result.
|
* Function to call with the result.
|
||||||
*/
|
*/
|
||||||
TALER_EXCHANGE_RefreshLinkCallback link_cb;
|
TALER_EXCHANGE_LinkCallback link_cb;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closure for @e cb.
|
* Closure for @e cb.
|
||||||
@ -73,7 +73,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
|
|||||||
* Parse the provided linkage data from the "200 OK" response
|
* Parse the provided linkage data from the "200 OK" response
|
||||||
* for one of the coins.
|
* for one of the coins.
|
||||||
*
|
*
|
||||||
* @param rlh refresh link handle
|
* @param lh link handle
|
||||||
* @param json json reply with the data for one coin
|
* @param json json reply with the data for one coin
|
||||||
* @param coin_num number of the coin to decode
|
* @param coin_num number of the coin to decode
|
||||||
* @param trans_pub our transfer public key
|
* @param trans_pub our transfer public key
|
||||||
@ -83,13 +83,13 @@ struct TALER_EXCHANGE_RefreshLinkHandle
|
|||||||
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
|
||||||
const json_t *json,
|
const json_t *json,
|
||||||
unsigned int coin_num,
|
unsigned int coin_num,
|
||||||
const struct TALER_TransferPublicKeyP *trans_pub,
|
const struct TALER_TransferPublicKeyP *trans_pub,
|
||||||
struct TALER_CoinSpendPrivateKeyP *coin_priv,
|
struct TALER_CoinSpendPrivateKeyP *coin_priv,
|
||||||
struct TALER_DenominationSignature *sig,
|
struct TALER_DenominationSignature *sig,
|
||||||
struct TALER_DenominationPublicKey *pub)
|
struct TALER_DenominationPublicKey *pub)
|
||||||
{
|
{
|
||||||
struct GNUNET_CRYPTO_RsaSignature *bsig;
|
struct GNUNET_CRYPTO_RsaSignature *bsig;
|
||||||
struct GNUNET_CRYPTO_RsaPublicKey *rpub;
|
struct GNUNET_CRYPTO_RsaPublicKey *rpub;
|
||||||
@ -114,7 +114,7 @@ parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TALER_link_recover_transfer_secret (trans_pub,
|
TALER_link_recover_transfer_secret (trans_pub,
|
||||||
&rlh->coin_priv,
|
&lh->coin_priv,
|
||||||
&secret);
|
&secret);
|
||||||
TALER_planchet_setup_refresh (&secret,
|
TALER_planchet_setup_refresh (&secret,
|
||||||
coin_num,
|
coin_num,
|
||||||
@ -133,7 +133,7 @@ parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
|||||||
|
|
||||||
ldp.purpose.size = htonl (sizeof (ldp));
|
ldp.purpose.size = htonl (sizeof (ldp));
|
||||||
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
|
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
|
||||||
GNUNET_CRYPTO_eddsa_key_get_public (&rlh->coin_priv.eddsa_priv,
|
GNUNET_CRYPTO_eddsa_key_get_public (&lh->coin_priv.eddsa_priv,
|
||||||
&ldp.old_coin_pub.eddsa_pub);
|
&ldp.old_coin_pub.eddsa_pub);
|
||||||
ldp.transfer_pub = *trans_pub;
|
ldp.transfer_pub = *trans_pub;
|
||||||
pub->rsa_public_key = rpub;
|
pub->rsa_public_key = rpub;
|
||||||
@ -175,13 +175,13 @@ parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
|||||||
* Parse the provided linkage data from the "200 OK" response
|
* Parse the provided linkage data from the "200 OK" response
|
||||||
* for one of the coins.
|
* for one of the coins.
|
||||||
*
|
*
|
||||||
* @param[in,out] rlh refresh link handle (callback may be zero'ed out)
|
* @param[in,out] lh link handle (callback may be zero'ed out)
|
||||||
* @param json json reply with the data for one coin
|
* @param json json reply with the data for one coin
|
||||||
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
|
||||||
const json_t *json)
|
const json_t *json)
|
||||||
{
|
{
|
||||||
unsigned int session;
|
unsigned int session;
|
||||||
unsigned int num_coins;
|
unsigned int num_coins;
|
||||||
@ -277,14 +277,14 @@ parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
|||||||
{
|
{
|
||||||
GNUNET_assert (i + off_coin < num_coins);
|
GNUNET_assert (i + off_coin < num_coins);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
parse_refresh_link_coin (rlh,
|
parse_link_coin (lh,
|
||||||
json_array_get (jsona,
|
json_array_get (jsona,
|
||||||
i),
|
i),
|
||||||
i,
|
i,
|
||||||
&trans_pub,
|
&trans_pub,
|
||||||
&coin_privs[i + off_coin],
|
&coin_privs[i + off_coin],
|
||||||
&sigs[i + off_coin],
|
&sigs[i + off_coin],
|
||||||
&pubs[i + off_coin]))
|
&pubs[i + off_coin]))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
break;
|
break;
|
||||||
@ -304,15 +304,15 @@ parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
|||||||
|
|
||||||
if (off_coin == num_coins)
|
if (off_coin == num_coins)
|
||||||
{
|
{
|
||||||
rlh->link_cb (rlh->link_cb_cls,
|
lh->link_cb (lh->link_cb_cls,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
TALER_EC_NONE,
|
TALER_EC_NONE,
|
||||||
num_coins,
|
num_coins,
|
||||||
coin_privs,
|
coin_privs,
|
||||||
sigs,
|
sigs,
|
||||||
pubs,
|
pubs,
|
||||||
json);
|
json);
|
||||||
rlh->link_cb = NULL;
|
lh->link_cb = NULL;
|
||||||
ret = GNUNET_OK;
|
ret = GNUNET_OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -337,29 +337,29 @@ parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called when we're done processing the
|
* Function called when we're done processing the
|
||||||
* HTTP /refresh/link request.
|
* HTTP /coins/$COIN_PUB/link request.
|
||||||
*
|
*
|
||||||
* @param cls the `struct TALER_EXCHANGE_RefreshLinkHandle`
|
* @param cls the `struct TALER_EXCHANGE_LinkHandle`
|
||||||
* @param response_code HTTP response code, 0 on error
|
* @param response_code HTTP response code, 0 on error
|
||||||
* @param response parsed JSON result, NULL on error
|
* @param response parsed JSON result, NULL on error
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
handle_refresh_link_finished (void *cls,
|
handle_link_finished (void *cls,
|
||||||
long response_code,
|
long response_code,
|
||||||
const void *response)
|
const void *response)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle *rlh = cls;
|
struct TALER_EXCHANGE_LinkHandle *lh = cls;
|
||||||
const json_t *j = response;
|
const json_t *j = response;
|
||||||
|
|
||||||
rlh->job = NULL;
|
lh->job = NULL;
|
||||||
switch (response_code)
|
switch (response_code)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
parse_refresh_link_ok (rlh,
|
parse_link_ok (lh,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
response_code = 0;
|
response_code = 0;
|
||||||
@ -386,25 +386,24 @@ handle_refresh_link_finished (void *cls,
|
|||||||
response_code = 0;
|
response_code = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL != rlh->link_cb)
|
if (NULL != lh->link_cb)
|
||||||
rlh->link_cb (rlh->link_cb_cls,
|
lh->link_cb (lh->link_cb_cls,
|
||||||
response_code,
|
response_code,
|
||||||
TALER_JSON_get_error_code (j),
|
TALER_JSON_get_error_code (j),
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
j);
|
j);
|
||||||
TALER_EXCHANGE_refresh_link_cancel (rlh);
|
TALER_EXCHANGE_link_cancel (lh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit a link request to the exchange and get the exchange's response.
|
* Submit a link request to the exchange and get the exchange's response.
|
||||||
*
|
*
|
||||||
* This API is typically not used by anyone, it is more a threat
|
* This API is typically not used by anyone, it is more a threat against those
|
||||||
* against those trying to receive a funds transfer by abusing the
|
* trying to receive a funds transfer by abusing the refresh protocol.
|
||||||
* /refresh protocol.
|
|
||||||
*
|
*
|
||||||
* @param exchange the exchange handle; the exchange must be ready to operate
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
* @param coin_priv private key to request link data for
|
* @param coin_priv private key to request link data for
|
||||||
@ -413,13 +412,13 @@ handle_refresh_link_finished (void *cls,
|
|||||||
* @param link_cb_cls closure for @a link_cb
|
* @param link_cb_cls closure for @a link_cb
|
||||||
* @return a handle for this request
|
* @return a handle for this request
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle *
|
struct TALER_EXCHANGE_LinkHandle *
|
||||||
TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
|
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
|
||||||
TALER_EXCHANGE_RefreshLinkCallback link_cb,
|
TALER_EXCHANGE_LinkCallback link_cb,
|
||||||
void *link_cb_cls)
|
void *link_cb_cls)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
|
struct TALER_EXCHANGE_LinkHandle *lh;
|
||||||
CURL *eh;
|
CURL *eh;
|
||||||
struct GNUNET_CURL_Context *ctx;
|
struct GNUNET_CURL_Context *ctx;
|
||||||
struct TALER_CoinSpendPublicKeyP coin_pub;
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||||
@ -449,42 +448,41 @@ TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
"/coins/%s/link",
|
"/coins/%s/link",
|
||||||
pub_str);
|
pub_str);
|
||||||
}
|
}
|
||||||
rlh = GNUNET_new (struct TALER_EXCHANGE_RefreshLinkHandle);
|
lh = GNUNET_new (struct TALER_EXCHANGE_LinkHandle);
|
||||||
rlh->exchange = exchange;
|
lh->exchange = exchange;
|
||||||
rlh->link_cb = link_cb;
|
lh->link_cb = link_cb;
|
||||||
rlh->link_cb_cls = link_cb_cls;
|
lh->link_cb_cls = link_cb_cls;
|
||||||
rlh->coin_priv = *coin_priv;
|
lh->coin_priv = *coin_priv;
|
||||||
rlh->url = TEAH_path_to_url (exchange,
|
lh->url = TEAH_path_to_url (exchange,
|
||||||
arg_str);
|
arg_str);
|
||||||
eh = TEL_curl_easy_get (rlh->url);
|
eh = TEL_curl_easy_get (lh->url);
|
||||||
ctx = TEAH_handle_to_context (exchange);
|
ctx = TEAH_handle_to_context (exchange);
|
||||||
rlh->job = GNUNET_CURL_job_add (ctx,
|
lh->job = GNUNET_CURL_job_add (ctx,
|
||||||
eh,
|
eh,
|
||||||
GNUNET_YES,
|
GNUNET_YES,
|
||||||
&handle_refresh_link_finished,
|
&handle_link_finished,
|
||||||
rlh);
|
lh);
|
||||||
return rlh;
|
return lh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel a refresh link request. This function cannot be used
|
* Cancel a link request. This function cannot be used
|
||||||
* on a request handle if the callback was already invoked.
|
* on a request handle if the callback was already invoked.
|
||||||
*
|
*
|
||||||
* @param rlh the refresh link handle
|
* @param lh the link handle
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_refresh_link_cancel (struct
|
TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh)
|
||||||
TALER_EXCHANGE_RefreshLinkHandle *rlh)
|
|
||||||
{
|
{
|
||||||
if (NULL != rlh->job)
|
if (NULL != lh->job)
|
||||||
{
|
{
|
||||||
GNUNET_CURL_job_cancel (rlh->job);
|
GNUNET_CURL_job_cancel (lh->job);
|
||||||
rlh->job = NULL;
|
lh->job = NULL;
|
||||||
}
|
}
|
||||||
GNUNET_free (rlh->url);
|
GNUNET_free (lh->url);
|
||||||
GNUNET_free (rlh);
|
GNUNET_free (lh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* end of exchange_api_refresh_link.c */
|
/* end of exchange_api_link.c */
|
505
src/lib/exchange_api_melt.c
Normal file
505
src/lib/exchange_api_melt.c
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
Copyright (C) 2015-2020 Taler Systems SA
|
||||||
|
|
||||||
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file lib/exchange_api_melt.c
|
||||||
|
* @brief Implementation of the /coins/$COIN_PUB/melt request
|
||||||
|
* @author Christian Grothoff
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include <jansson.h>
|
||||||
|
#include <microhttpd.h> /* just for HTTP status codes */
|
||||||
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
|
#include <gnunet/gnunet_json_lib.h>
|
||||||
|
#include <gnunet/gnunet_curl_lib.h>
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_exchange_service.h"
|
||||||
|
#include "exchange_api_handle.h"
|
||||||
|
#include "taler_signatures.h"
|
||||||
|
#include "exchange_api_curl_defaults.h"
|
||||||
|
#include "exchange_api_refresh_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A /coins/$COIN_PUB/melt Handle
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_MeltHandle
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connection to exchange this request handle will use
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_Handle *exchange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url for this request.
|
||||||
|
*/
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for #TEH_curl_easy_post(). Keeps the data that must
|
||||||
|
* persist for Curl to make the upload.
|
||||||
|
*/
|
||||||
|
struct TALER_CURL_PostContext ctx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for the request.
|
||||||
|
*/
|
||||||
|
struct GNUNET_CURL_Job *job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to call with refresh melt failure results.
|
||||||
|
*/
|
||||||
|
TALER_EXCHANGE_MeltCallback melt_cb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure for @e result_cb and @e melt_failure_cb.
|
||||||
|
*/
|
||||||
|
void *melt_cb_cls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual information about the melt operation.
|
||||||
|
*/
|
||||||
|
struct MeltData *md;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Public information about the coin's denomination key
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_DenomPublicKey dki;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the signature on the "200 OK" response
|
||||||
|
* from the exchange is valid.
|
||||||
|
*
|
||||||
|
* @param mh melt handle
|
||||||
|
* @param json json reply with the signature
|
||||||
|
* @param[out] exchange_pub public key of the exchange used for the signature
|
||||||
|
* @param[out] noreveal_index set to the noreveal index selected by the exchange
|
||||||
|
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
|
||||||
|
const json_t *json,
|
||||||
|
struct TALER_ExchangePublicKeyP *exchange_pub,
|
||||||
|
uint32_t *noreveal_index)
|
||||||
|
{
|
||||||
|
struct TALER_ExchangeSignatureP exchange_sig;
|
||||||
|
const struct TALER_EXCHANGE_Keys *key_state;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
|
||||||
|
GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
struct TALER_RefreshMeltConfirmationPS confirm;
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (json,
|
||||||
|
spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that exchange signing key is permitted */
|
||||||
|
key_state = TALER_EXCHANGE_get_keys (mh->exchange);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_EXCHANGE_test_signing_key (key_state,
|
||||||
|
exchange_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that noreveal index is in permitted range */
|
||||||
|
if (TALER_CNC_KAPPA <= *noreveal_index)
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify signature by exchange */
|
||||||
|
confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
|
||||||
|
confirm.purpose.size = htonl (sizeof (struct
|
||||||
|
TALER_RefreshMeltConfirmationPS));
|
||||||
|
confirm.rc = mh->md->rc;
|
||||||
|
confirm.noreveal_index = htonl (*noreveal_index);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
|
||||||
|
&confirm.purpose,
|
||||||
|
&exchange_sig.eddsa_signature,
|
||||||
|
&exchange_pub->eddsa_pub))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the signatures on the "409 CONFLICT" response from the
|
||||||
|
* exchange demonstrating customer double-spending are valid.
|
||||||
|
*
|
||||||
|
* @param mh melt handle
|
||||||
|
* @param json json reply with the signature(s) and transaction history
|
||||||
|
* @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
|
||||||
|
const json_t *json)
|
||||||
|
{
|
||||||
|
json_t *history;
|
||||||
|
struct TALER_Amount original_value;
|
||||||
|
struct TALER_Amount melt_value_with_fee;
|
||||||
|
struct TALER_Amount total;
|
||||||
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_json ("history", &history),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
|
||||||
|
TALER_JSON_spec_amount ("original_value", &original_value),
|
||||||
|
TALER_JSON_spec_amount ("requested_value", &melt_value_with_fee),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
const struct MeltedCoin *mc;
|
||||||
|
|
||||||
|
/* parse JSON reply */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (json,
|
||||||
|
spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find out which coin was deemed problematic by the exchange */
|
||||||
|
mc = &mh->md->melted_coin;
|
||||||
|
|
||||||
|
/* check basic coin properties */
|
||||||
|
if (0 != TALER_amount_cmp (&original_value,
|
||||||
|
&mc->original_value))
|
||||||
|
{
|
||||||
|
/* We disagree on the value of the coin */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
json_decref (history);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (0 != TALER_amount_cmp (&melt_value_with_fee,
|
||||||
|
&mc->melt_amount_with_fee))
|
||||||
|
{
|
||||||
|
/* We disagree on the value of the coin */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
json_decref (history);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify coin history */
|
||||||
|
history = json_object_get (json,
|
||||||
|
"history");
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_EXCHANGE_verify_coin_history (&mh->dki,
|
||||||
|
original_value.currency,
|
||||||
|
&coin_pub,
|
||||||
|
history,
|
||||||
|
&total))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
json_decref (history);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
json_decref (history);
|
||||||
|
|
||||||
|
/* check if melt operation was really too expensive given history */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total,
|
||||||
|
&total,
|
||||||
|
&melt_value_with_fee))
|
||||||
|
{
|
||||||
|
/* clearly not OK if our transaction would have caused
|
||||||
|
the overflow... */
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 >= TALER_amount_cmp (&total,
|
||||||
|
&original_value))
|
||||||
|
{
|
||||||
|
/* transaction should have still fit */
|
||||||
|
GNUNET_break (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* everything OK, valid proof of double-spending was provided */
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when we're done processing the
|
||||||
|
* HTTP /coins/$COIN_PUB/melt request.
|
||||||
|
*
|
||||||
|
* @param cls the `struct TALER_EXCHANGE_MeltHandle`
|
||||||
|
* @param response_code HTTP response code, 0 on error
|
||||||
|
* @param response parsed JSON result, NULL on error
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_melt_finished (void *cls,
|
||||||
|
long response_code,
|
||||||
|
const void *response)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_MeltHandle *mh = cls;
|
||||||
|
uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
|
||||||
|
struct TALER_ExchangePublicKeyP exchange_pub;
|
||||||
|
const json_t *j = response;
|
||||||
|
|
||||||
|
mh->job = NULL;
|
||||||
|
switch (response_code)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_OK:
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
verify_melt_signature_ok (mh,
|
||||||
|
j,
|
||||||
|
&exchange_pub,
|
||||||
|
&noreveal_index))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
response_code = 0;
|
||||||
|
}
|
||||||
|
if (NULL != mh->melt_cb)
|
||||||
|
{
|
||||||
|
mh->melt_cb (mh->melt_cb_cls,
|
||||||
|
response_code,
|
||||||
|
TALER_JSON_get_error_code (j),
|
||||||
|
noreveal_index,
|
||||||
|
(0 == response_code) ? NULL : &exchange_pub,
|
||||||
|
j);
|
||||||
|
mh->melt_cb = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
|
/* This should never happen, either us or the exchange is buggy
|
||||||
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_CONFLICT:
|
||||||
|
/* Double spending; check signatures on transaction history */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
verify_melt_signature_conflict (mh,
|
||||||
|
j))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
response_code = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_FORBIDDEN:
|
||||||
|
/* Nothing really to verify, exchange says one of the signatures is
|
||||||
|
invalid; assuming we checked them, this should never happen, we
|
||||||
|
should pass the JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_NOT_FOUND:
|
||||||
|
/* Nothing really to verify, this should never
|
||||||
|
happen, we should pass the JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
|
/* Server had an internal issue; we should retry, but this API
|
||||||
|
leaves this to the application */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unexpected response code */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Unexpected response code %u\n",
|
||||||
|
(unsigned int) response_code);
|
||||||
|
GNUNET_break (0);
|
||||||
|
response_code = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NULL != mh->melt_cb)
|
||||||
|
mh->melt_cb (mh->melt_cb_cls,
|
||||||
|
response_code,
|
||||||
|
TALER_JSON_get_error_code (j),
|
||||||
|
UINT32_MAX,
|
||||||
|
NULL,
|
||||||
|
j);
|
||||||
|
TALER_EXCHANGE_melt_cancel (mh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a melt request to the exchange and get the exchange's
|
||||||
|
* response.
|
||||||
|
*
|
||||||
|
* This API is typically used by a wallet. Note that to ensure that
|
||||||
|
* no money is lost in case of hardware failures, the provided
|
||||||
|
* argument should have been constructed using
|
||||||
|
* #TALER_EXCHANGE_refresh_prepare and committed to persistent storage
|
||||||
|
* prior to calling this function.
|
||||||
|
*
|
||||||
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
|
* @param refresh_data_length size of the @a refresh_data (returned
|
||||||
|
* in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
|
||||||
|
* @param refresh_data the refresh data as returned from
|
||||||
|
#TALER_EXCHANGE_refresh_prepare())
|
||||||
|
* @param melt_cb the callback to call with the result
|
||||||
|
* @param melt_cb_cls closure for @a melt_cb
|
||||||
|
* @return a handle for this request; NULL if the argument was invalid.
|
||||||
|
* In this case, neither callback will be called.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_MeltHandle *
|
||||||
|
TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
size_t refresh_data_length,
|
||||||
|
const char *refresh_data,
|
||||||
|
TALER_EXCHANGE_MeltCallback melt_cb,
|
||||||
|
void *melt_cb_cls)
|
||||||
|
{
|
||||||
|
const struct TALER_EXCHANGE_Keys *key_state;
|
||||||
|
const struct TALER_EXCHANGE_DenomPublicKey *dki;
|
||||||
|
json_t *melt_obj;
|
||||||
|
struct TALER_EXCHANGE_MeltHandle *mh;
|
||||||
|
CURL *eh;
|
||||||
|
struct GNUNET_CURL_Context *ctx;
|
||||||
|
struct MeltData *md;
|
||||||
|
struct TALER_CoinSpendSignatureP confirm_sig;
|
||||||
|
struct TALER_RefreshMeltCoinAffirmationPS melt;
|
||||||
|
struct GNUNET_HashCode h_denom_pub;
|
||||||
|
char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
|
||||||
|
|
||||||
|
GNUNET_assert (GNUNET_YES ==
|
||||||
|
TEAH_handle_is_ready (exchange));
|
||||||
|
md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
|
||||||
|
refresh_data_length);
|
||||||
|
if (NULL == md)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
|
||||||
|
melt.purpose.size = htonl (sizeof (struct
|
||||||
|
TALER_RefreshMeltCoinAffirmationPS));
|
||||||
|
melt.rc = md->rc;
|
||||||
|
TALER_amount_hton (&melt.amount_with_fee,
|
||||||
|
&md->melted_coin.melt_amount_with_fee);
|
||||||
|
TALER_amount_hton (&melt.melt_fee,
|
||||||
|
&md->melted_coin.fee_melt);
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
|
||||||
|
&melt.coin_pub.eddsa_pub);
|
||||||
|
GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv,
|
||||||
|
&melt.purpose,
|
||||||
|
&confirm_sig.eddsa_signature);
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_hash (md->melted_coin.pub_key.rsa_public_key,
|
||||||
|
&h_denom_pub);
|
||||||
|
melt_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
|
||||||
|
"coin_pub",
|
||||||
|
GNUNET_JSON_from_data_auto (&melt.coin_pub),
|
||||||
|
"denom_pub_hash",
|
||||||
|
GNUNET_JSON_from_data_auto (&h_denom_pub),
|
||||||
|
"denom_sig",
|
||||||
|
GNUNET_JSON_from_rsa_signature (
|
||||||
|
md->melted_coin.sig.rsa_signature),
|
||||||
|
"confirm_sig",
|
||||||
|
GNUNET_JSON_from_data_auto (&confirm_sig),
|
||||||
|
"value_with_fee",
|
||||||
|
TALER_JSON_from_amount (
|
||||||
|
&md->melted_coin.melt_amount_with_fee),
|
||||||
|
"rc",
|
||||||
|
GNUNET_JSON_from_data_auto (&melt.rc));
|
||||||
|
if (NULL == melt_obj)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (md);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
end = GNUNET_STRINGS_data_to_string (&melt.coin_pub,
|
||||||
|
sizeof (struct
|
||||||
|
TALER_CoinSpendPublicKeyP),
|
||||||
|
pub_str,
|
||||||
|
sizeof (pub_str));
|
||||||
|
*end = '\0';
|
||||||
|
GNUNET_snprintf (arg_str,
|
||||||
|
sizeof (arg_str),
|
||||||
|
"/coins/%s/melt",
|
||||||
|
pub_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
key_state = TALER_EXCHANGE_get_keys (exchange);
|
||||||
|
dki = TALER_EXCHANGE_get_denomination_key (key_state,
|
||||||
|
&md->melted_coin.pub_key);
|
||||||
|
|
||||||
|
/* and now we can at last begin the actual request handling */
|
||||||
|
mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
|
||||||
|
mh->exchange = exchange;
|
||||||
|
mh->dki = *dki;
|
||||||
|
mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
|
||||||
|
not copy the pointer */
|
||||||
|
mh->melt_cb = melt_cb;
|
||||||
|
mh->melt_cb_cls = melt_cb_cls;
|
||||||
|
mh->md = md;
|
||||||
|
mh->url = TEAH_path_to_url (exchange,
|
||||||
|
arg_str);
|
||||||
|
eh = TEL_curl_easy_get (mh->url);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_curl_easy_post (&mh->ctx,
|
||||||
|
eh,
|
||||||
|
melt_obj))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
curl_easy_cleanup (eh);
|
||||||
|
json_decref (melt_obj);
|
||||||
|
GNUNET_free (mh->url);
|
||||||
|
GNUNET_free (mh);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
json_decref (melt_obj);
|
||||||
|
ctx = TEAH_handle_to_context (exchange);
|
||||||
|
mh->job = GNUNET_CURL_job_add2 (ctx,
|
||||||
|
eh,
|
||||||
|
mh->ctx.headers,
|
||||||
|
&handle_melt_finished,
|
||||||
|
mh);
|
||||||
|
return mh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a melt request. This function cannot be used
|
||||||
|
* on a request handle if either callback was already invoked.
|
||||||
|
*
|
||||||
|
* @param mh the refresh melt handle
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh)
|
||||||
|
{
|
||||||
|
if (NULL != mh->job)
|
||||||
|
{
|
||||||
|
GNUNET_CURL_job_cancel (mh->job);
|
||||||
|
mh->job = NULL;
|
||||||
|
}
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (mh->md); /* does not free 'md' itself */
|
||||||
|
GNUNET_free (mh->md);
|
||||||
|
GNUNET_free (mh->url);
|
||||||
|
TALER_curl_easy_post_finished (&mh->ctx);
|
||||||
|
GNUNET_free (mh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* end of exchange_api_melt.c */
|
File diff suppressed because it is too large
Load Diff
631
src/lib/exchange_api_refresh_common.c
Normal file
631
src/lib/exchange_api_refresh_common.c
Normal file
@ -0,0 +1,631 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
Copyright (C) 2015-2020 Taler Systems SA
|
||||||
|
|
||||||
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file lib/exchange_api_refresh_common.c
|
||||||
|
* @brief Serialization logic shared between melt and reveal steps during refreshing
|
||||||
|
* @author Christian Grothoff
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include "exchange_api_refresh_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all information associated with a melted coin session.
|
||||||
|
*
|
||||||
|
* @param mc melted coin to release, the pointer itself is NOT
|
||||||
|
* freed (as it is typically not allocated by itself)
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
free_melted_coin (struct MeltedCoin *mc)
|
||||||
|
{
|
||||||
|
if (NULL != mc->pub_key.rsa_public_key)
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_free (mc->pub_key.rsa_public_key);
|
||||||
|
if (NULL != mc->sig.rsa_signature)
|
||||||
|
GNUNET_CRYPTO_rsa_signature_free (mc->sig.rsa_signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all information associated with a melting session. Note
|
||||||
|
* that we allow the melting session to be only partially initialized,
|
||||||
|
* as we use this function also when freeing melt data that was not
|
||||||
|
* fully initialized (i.e. due to failures in #TALER_EXCHANGE_deserialize_melt_data_()).
|
||||||
|
*
|
||||||
|
* @param md melting data to release, the pointer itself is NOT
|
||||||
|
* freed (as it is typically not allocated by itself)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (struct MeltData *md)
|
||||||
|
{
|
||||||
|
free_melted_coin (&md->melted_coin);
|
||||||
|
if (NULL != md->fresh_pks)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i<md->num_fresh_coins; i++)
|
||||||
|
if (NULL != md->fresh_pks[i].rsa_public_key)
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_free (md->fresh_pks[i].rsa_public_key);
|
||||||
|
GNUNET_free (md->fresh_pks);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
GNUNET_free_non_null (md->fresh_coins[i]);
|
||||||
|
/* Finally, clean up a bit...
|
||||||
|
(NOTE: compilers might optimize this away, so this is
|
||||||
|
not providing any strong assurances that the key material
|
||||||
|
is purged.) */
|
||||||
|
memset (md,
|
||||||
|
0,
|
||||||
|
sizeof (struct MeltData));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize information about a coin we are melting.
|
||||||
|
*
|
||||||
|
* @param mc information to serialize
|
||||||
|
* @param buf buffer to write data in, NULL to just compute
|
||||||
|
* required size
|
||||||
|
* @param off offeset at @a buf to use
|
||||||
|
* @return number of bytes written to @a buf at @a off, or if
|
||||||
|
* @a buf is NULL, number of bytes required; 0 on error
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
serialize_melted_coin (const struct MeltedCoin *mc,
|
||||||
|
char *buf,
|
||||||
|
size_t off)
|
||||||
|
{
|
||||||
|
struct MeltedCoinP mcp;
|
||||||
|
unsigned int i;
|
||||||
|
char *pbuf;
|
||||||
|
size_t pbuf_size;
|
||||||
|
char *sbuf;
|
||||||
|
size_t sbuf_size;
|
||||||
|
|
||||||
|
sbuf_size = GNUNET_CRYPTO_rsa_signature_encode (mc->sig.rsa_signature,
|
||||||
|
&sbuf);
|
||||||
|
pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (mc->pub_key.rsa_public_key,
|
||||||
|
&pbuf);
|
||||||
|
if (NULL == buf)
|
||||||
|
{
|
||||||
|
GNUNET_free (sbuf);
|
||||||
|
GNUNET_free (pbuf);
|
||||||
|
return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
|
||||||
|
}
|
||||||
|
if ( (sbuf_size > UINT16_MAX) ||
|
||||||
|
(pbuf_size > UINT16_MAX) )
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mcp.coin_priv = mc->coin_priv;
|
||||||
|
TALER_amount_hton (&mcp.melt_amount_with_fee,
|
||||||
|
&mc->melt_amount_with_fee);
|
||||||
|
TALER_amount_hton (&mcp.fee_melt,
|
||||||
|
&mc->fee_melt);
|
||||||
|
TALER_amount_hton (&mcp.original_value,
|
||||||
|
&mc->original_value);
|
||||||
|
for (i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
mcp.transfer_priv[i] = mc->transfer_priv[i];
|
||||||
|
mcp.expire_deposit = GNUNET_TIME_absolute_hton (mc->expire_deposit);
|
||||||
|
mcp.pbuf_size = htons ((uint16_t) pbuf_size);
|
||||||
|
mcp.sbuf_size = htons ((uint16_t) sbuf_size);
|
||||||
|
memcpy (&buf[off],
|
||||||
|
&mcp,
|
||||||
|
sizeof (struct MeltedCoinP));
|
||||||
|
memcpy (&buf[off + sizeof (struct MeltedCoinP)],
|
||||||
|
pbuf,
|
||||||
|
pbuf_size);
|
||||||
|
memcpy (&buf[off + sizeof (struct MeltedCoinP) + pbuf_size],
|
||||||
|
sbuf,
|
||||||
|
sbuf_size);
|
||||||
|
GNUNET_free (sbuf);
|
||||||
|
GNUNET_free (pbuf);
|
||||||
|
return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize information about a coin we are melting.
|
||||||
|
*
|
||||||
|
* @param[out] mc information to deserialize
|
||||||
|
* @param buf buffer to read data from
|
||||||
|
* @param size number of bytes available at @a buf to use
|
||||||
|
* @param[out] ok set to #GNUNET_NO to report errors
|
||||||
|
* @return number of bytes read from @a buf, 0 on error
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
deserialize_melted_coin (struct MeltedCoin *mc,
|
||||||
|
const char *buf,
|
||||||
|
size_t size,
|
||||||
|
int *ok)
|
||||||
|
{
|
||||||
|
struct MeltedCoinP mcp;
|
||||||
|
unsigned int i;
|
||||||
|
size_t pbuf_size;
|
||||||
|
size_t sbuf_size;
|
||||||
|
size_t off;
|
||||||
|
|
||||||
|
if (size < sizeof (struct MeltedCoinP))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy (&mcp,
|
||||||
|
buf,
|
||||||
|
sizeof (struct MeltedCoinP));
|
||||||
|
pbuf_size = ntohs (mcp.pbuf_size);
|
||||||
|
sbuf_size = ntohs (mcp.sbuf_size);
|
||||||
|
if (size < sizeof (struct MeltedCoinP) + pbuf_size + sbuf_size)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
off = sizeof (struct MeltedCoinP);
|
||||||
|
mc->pub_key.rsa_public_key
|
||||||
|
= GNUNET_CRYPTO_rsa_public_key_decode (&buf[off],
|
||||||
|
pbuf_size);
|
||||||
|
off += pbuf_size;
|
||||||
|
mc->sig.rsa_signature
|
||||||
|
= GNUNET_CRYPTO_rsa_signature_decode (&buf[off],
|
||||||
|
sbuf_size);
|
||||||
|
off += sbuf_size;
|
||||||
|
if ( (NULL == mc->pub_key.rsa_public_key) ||
|
||||||
|
(NULL == mc->sig.rsa_signature) )
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mc->coin_priv = mcp.coin_priv;
|
||||||
|
TALER_amount_ntoh (&mc->melt_amount_with_fee,
|
||||||
|
&mcp.melt_amount_with_fee);
|
||||||
|
TALER_amount_ntoh (&mc->fee_melt,
|
||||||
|
&mcp.fee_melt);
|
||||||
|
TALER_amount_ntoh (&mc->original_value,
|
||||||
|
&mcp.original_value);
|
||||||
|
for (i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
mc->transfer_priv[i] = mcp.transfer_priv[i];
|
||||||
|
mc->expire_deposit = GNUNET_TIME_absolute_ntoh (mcp.expire_deposit);
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize information about a denomination key.
|
||||||
|
*
|
||||||
|
* @param dk information to serialize
|
||||||
|
* @param buf buffer to write data in, NULL to just compute
|
||||||
|
* required size
|
||||||
|
* @param off offeset at @a buf to use
|
||||||
|
* @return number of bytes written to @a buf at @a off, or if
|
||||||
|
* @a buf is NULL, number of bytes required
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
serialize_denomination_key (const struct TALER_DenominationPublicKey *dk,
|
||||||
|
char *buf,
|
||||||
|
size_t off)
|
||||||
|
{
|
||||||
|
char *pbuf;
|
||||||
|
size_t pbuf_size;
|
||||||
|
uint32_t be;
|
||||||
|
|
||||||
|
pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (dk->rsa_public_key,
|
||||||
|
&pbuf);
|
||||||
|
if (NULL == buf)
|
||||||
|
{
|
||||||
|
GNUNET_free (pbuf);
|
||||||
|
return pbuf_size + sizeof (uint32_t);
|
||||||
|
}
|
||||||
|
be = htonl ((uint32_t) pbuf_size);
|
||||||
|
memcpy (&buf[off],
|
||||||
|
&be,
|
||||||
|
sizeof (uint32_t));
|
||||||
|
memcpy (&buf[off + sizeof (uint32_t)],
|
||||||
|
pbuf,
|
||||||
|
pbuf_size);
|
||||||
|
GNUNET_free (pbuf);
|
||||||
|
return pbuf_size + sizeof (uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize information about a denomination key.
|
||||||
|
*
|
||||||
|
* @param[out] dk information to deserialize
|
||||||
|
* @param buf buffer to read data from
|
||||||
|
* @param size number of bytes available at @a buf to use
|
||||||
|
* @param[out] ok set to #GNUNET_NO to report errors
|
||||||
|
* @return number of bytes read from @a buf, 0 on error
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
deserialize_denomination_key (struct TALER_DenominationPublicKey *dk,
|
||||||
|
const char *buf,
|
||||||
|
size_t size,
|
||||||
|
int *ok)
|
||||||
|
{
|
||||||
|
size_t pbuf_size;
|
||||||
|
uint32_t be;
|
||||||
|
|
||||||
|
if (size < sizeof (uint32_t))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy (&be,
|
||||||
|
buf,
|
||||||
|
sizeof (uint32_t));
|
||||||
|
pbuf_size = ntohl (be);
|
||||||
|
if (size < sizeof (uint32_t) + pbuf_size)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dk->rsa_public_key
|
||||||
|
= GNUNET_CRYPTO_rsa_public_key_decode (&buf[sizeof (uint32_t)],
|
||||||
|
pbuf_size);
|
||||||
|
if (NULL == dk->rsa_public_key)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return sizeof (uint32_t) + pbuf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize information about a fresh coin we are generating.
|
||||||
|
*
|
||||||
|
* @param fc information to serialize
|
||||||
|
* @param buf buffer to write data in, NULL to just compute
|
||||||
|
* required size
|
||||||
|
* @param off offeset at @a buf to use
|
||||||
|
* @return number of bytes written to @a buf at @a off, or if
|
||||||
|
* @a buf is NULL, number of bytes required
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
serialize_fresh_coin (const struct TALER_PlanchetSecretsP *fc,
|
||||||
|
char *buf,
|
||||||
|
size_t off)
|
||||||
|
{
|
||||||
|
if (NULL != buf)
|
||||||
|
memcpy (&buf[off],
|
||||||
|
fc,
|
||||||
|
sizeof (struct TALER_PlanchetSecretsP));
|
||||||
|
return sizeof (struct TALER_PlanchetSecretsP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize information about a fresh coin we are generating.
|
||||||
|
*
|
||||||
|
* @param[out] fc information to deserialize
|
||||||
|
* @param buf buffer to read data from
|
||||||
|
* @param size number of bytes available at @a buf to use
|
||||||
|
* @param[out] ok set to #GNUNET_NO to report errors
|
||||||
|
* @return number of bytes read from @a buf, 0 on error
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
deserialize_fresh_coin (struct TALER_PlanchetSecretsP *fc,
|
||||||
|
const char *buf,
|
||||||
|
size_t size,
|
||||||
|
int *ok)
|
||||||
|
{
|
||||||
|
if (size < sizeof (struct TALER_PlanchetSecretsP))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*ok = GNUNET_NO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy (fc,
|
||||||
|
buf,
|
||||||
|
sizeof (struct TALER_PlanchetSecretsP));
|
||||||
|
return sizeof (struct TALER_PlanchetSecretsP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize melt data.
|
||||||
|
*
|
||||||
|
* @param md data to serialize
|
||||||
|
* @param[out] res_size size of buffer returned
|
||||||
|
* @return serialized melt data
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
serialize_melt_data (const struct MeltData *md,
|
||||||
|
size_t *res_size)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
size_t asize;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
asize = (size_t) -1; /* make the compiler happy */
|
||||||
|
buf = NULL;
|
||||||
|
/* we do 2 iterations, #1 to determine total size, #2 to
|
||||||
|
actually construct the buffer */
|
||||||
|
do {
|
||||||
|
if (0 == size)
|
||||||
|
{
|
||||||
|
size = sizeof (struct MeltDataP);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct MeltDataP *mdp;
|
||||||
|
|
||||||
|
buf = GNUNET_malloc (size);
|
||||||
|
asize = size; /* just for invariant check later */
|
||||||
|
size = sizeof (struct MeltDataP);
|
||||||
|
mdp = (struct MeltDataP *) buf;
|
||||||
|
mdp->rc = md->rc;
|
||||||
|
mdp->num_fresh_coins = htons (md->num_fresh_coins);
|
||||||
|
}
|
||||||
|
size += serialize_melted_coin (&md->melted_coin,
|
||||||
|
buf,
|
||||||
|
size);
|
||||||
|
for (unsigned int i = 0; i<md->num_fresh_coins; i++)
|
||||||
|
size += serialize_denomination_key (&md->fresh_pks[i],
|
||||||
|
buf,
|
||||||
|
size);
|
||||||
|
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
for (unsigned int j = 0; j<md->num_fresh_coins; j++)
|
||||||
|
size += serialize_fresh_coin (&md->fresh_coins[i][j],
|
||||||
|
buf,
|
||||||
|
size);
|
||||||
|
} while (NULL == buf);
|
||||||
|
GNUNET_assert (size == asize);
|
||||||
|
*res_size = size;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize melt data.
|
||||||
|
*
|
||||||
|
* @param buf serialized data
|
||||||
|
* @param buf_size size of @a buf
|
||||||
|
* @return deserialized melt data, NULL on error
|
||||||
|
*/
|
||||||
|
struct MeltData *
|
||||||
|
TALER_EXCHANGE_deserialize_melt_data_ (const char *buf,
|
||||||
|
size_t buf_size)
|
||||||
|
{
|
||||||
|
struct MeltData *md;
|
||||||
|
struct MeltDataP mdp;
|
||||||
|
size_t off;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
if (buf_size < sizeof (struct MeltDataP))
|
||||||
|
return NULL;
|
||||||
|
memcpy (&mdp,
|
||||||
|
buf,
|
||||||
|
sizeof (struct MeltDataP));
|
||||||
|
md = GNUNET_new (struct MeltData);
|
||||||
|
md->rc = mdp.rc;
|
||||||
|
md->num_fresh_coins = ntohs (mdp.num_fresh_coins);
|
||||||
|
md->fresh_pks = GNUNET_new_array (md->num_fresh_coins,
|
||||||
|
struct TALER_DenominationPublicKey);
|
||||||
|
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
md->fresh_coins[i] = GNUNET_new_array (md->num_fresh_coins,
|
||||||
|
struct TALER_PlanchetSecretsP);
|
||||||
|
off = sizeof (struct MeltDataP);
|
||||||
|
ok = GNUNET_YES;
|
||||||
|
off += deserialize_melted_coin (&md->melted_coin,
|
||||||
|
&buf[off],
|
||||||
|
buf_size - off,
|
||||||
|
&ok);
|
||||||
|
for (unsigned int i = 0; (i<md->num_fresh_coins) && (GNUNET_YES == ok); i++)
|
||||||
|
off += deserialize_denomination_key (&md->fresh_pks[i],
|
||||||
|
&buf[off],
|
||||||
|
buf_size - off,
|
||||||
|
&ok);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
for (unsigned int j = 0; (j<md->num_fresh_coins) && (GNUNET_YES == ok); j++)
|
||||||
|
off += deserialize_fresh_coin (&md->fresh_coins[i][j],
|
||||||
|
&buf[off],
|
||||||
|
buf_size - off,
|
||||||
|
&ok);
|
||||||
|
if (off != buf_size)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
ok = GNUNET_NO;
|
||||||
|
}
|
||||||
|
if (GNUNET_YES != ok)
|
||||||
|
{
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (md);
|
||||||
|
GNUNET_free (md);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Melt (partially spent) coins to obtain fresh coins that are
|
||||||
|
* unlinkable to the original coin(s). Note that melting more
|
||||||
|
* than one coin in a single request will make those coins linkable,
|
||||||
|
* so the safest operation only melts one coin at a time.
|
||||||
|
*
|
||||||
|
* This API is typically used by a wallet. Note that to ensure that
|
||||||
|
* no money is lost in case of hardware failures, this operation does
|
||||||
|
* not actually initiate the request. Instead, it generates a buffer
|
||||||
|
* which the caller must store before proceeding with the actual call
|
||||||
|
* to #TALER_EXCHANGE_melt() that will generate the request.
|
||||||
|
*
|
||||||
|
* This function does verify that the given request data is internally
|
||||||
|
* consistent. However, the @a melts_sigs are NOT verified.
|
||||||
|
*
|
||||||
|
* Aside from some non-trivial cryptographic operations that might
|
||||||
|
* take a bit of CPU time to complete, this function returns
|
||||||
|
* its result immediately and does not start any asynchronous
|
||||||
|
* processing. This function is also thread-safe.
|
||||||
|
*
|
||||||
|
* @param melt_priv private key of the coin to melt
|
||||||
|
* @param melt_amount amount specifying how much
|
||||||
|
* the coin will contribute to the melt (including fee)
|
||||||
|
* @param melt_sig signature affirming the
|
||||||
|
* validity of the public keys corresponding to the
|
||||||
|
* @a melt_priv private key
|
||||||
|
* @param melt_pk denomination key information
|
||||||
|
* record corresponding to the @a melt_sig
|
||||||
|
* validity of the keys
|
||||||
|
* @param fresh_pks_len length of the @a pks array
|
||||||
|
* @param fresh_pks array of @a pks_len denominations of fresh coins to create
|
||||||
|
* @param[out] res_size set to the size of the return value, or 0 on error
|
||||||
|
* @return NULL
|
||||||
|
* if the inputs are invalid (i.e. denomination key not with this exchange).
|
||||||
|
* Otherwise, pointer to a buffer of @a res_size to store persistently
|
||||||
|
* before proceeding to #TALER_EXCHANGE_melt().
|
||||||
|
* Non-null results should be freed using GNUNET_free().
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
TALER_EXCHANGE_refresh_prepare (const struct
|
||||||
|
TALER_CoinSpendPrivateKeyP *melt_priv,
|
||||||
|
const struct TALER_Amount *melt_amount,
|
||||||
|
const struct
|
||||||
|
TALER_DenominationSignature *melt_sig,
|
||||||
|
const struct
|
||||||
|
TALER_EXCHANGE_DenomPublicKey *melt_pk,
|
||||||
|
unsigned int fresh_pks_len,
|
||||||
|
const struct
|
||||||
|
TALER_EXCHANGE_DenomPublicKey *fresh_pks,
|
||||||
|
size_t *res_size)
|
||||||
|
{
|
||||||
|
struct MeltData md;
|
||||||
|
char *buf;
|
||||||
|
struct TALER_Amount total;
|
||||||
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||||
|
struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
|
||||||
|
struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
|
||||||
|
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&melt_priv->eddsa_priv,
|
||||||
|
&coin_pub.eddsa_pub);
|
||||||
|
/* build up melt data structure */
|
||||||
|
memset (&md, 0, sizeof (md));
|
||||||
|
md.num_fresh_coins = fresh_pks_len;
|
||||||
|
md.melted_coin.coin_priv = *melt_priv;
|
||||||
|
md.melted_coin.melt_amount_with_fee = *melt_amount;
|
||||||
|
md.melted_coin.fee_melt = melt_pk->fee_refresh;
|
||||||
|
md.melted_coin.original_value = melt_pk->value;
|
||||||
|
md.melted_coin.expire_deposit
|
||||||
|
= melt_pk->expire_deposit;
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (melt_amount->currency,
|
||||||
|
&total));
|
||||||
|
md.melted_coin.pub_key.rsa_public_key
|
||||||
|
= GNUNET_CRYPTO_rsa_public_key_dup (melt_pk->key.rsa_public_key);
|
||||||
|
md.melted_coin.sig.rsa_signature
|
||||||
|
= GNUNET_CRYPTO_rsa_signature_dup (melt_sig->rsa_signature);
|
||||||
|
md.fresh_pks = GNUNET_new_array (fresh_pks_len,
|
||||||
|
struct TALER_DenominationPublicKey);
|
||||||
|
for (unsigned int i = 0; i<fresh_pks_len; i++)
|
||||||
|
{
|
||||||
|
md.fresh_pks[i].rsa_public_key
|
||||||
|
= GNUNET_CRYPTO_rsa_public_key_dup (fresh_pks[i].key.rsa_public_key);
|
||||||
|
if ( (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total,
|
||||||
|
&total,
|
||||||
|
&fresh_pks[i].value)) ||
|
||||||
|
(GNUNET_OK !=
|
||||||
|
TALER_amount_add (&total,
|
||||||
|
&total,
|
||||||
|
&fresh_pks[i].fee_withdraw)) )
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (&md);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* verify that melt_amount is above total cost */
|
||||||
|
if (1 ==
|
||||||
|
TALER_amount_cmp (&total,
|
||||||
|
melt_amount) )
|
||||||
|
{
|
||||||
|
/* Eh, this operation is more expensive than the
|
||||||
|
@a melt_amount. This is not OK. */
|
||||||
|
GNUNET_break (0);
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (&md);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build up coins */
|
||||||
|
for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
|
||||||
|
|
||||||
|
tpk = GNUNET_CRYPTO_ecdhe_key_create ();
|
||||||
|
md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk;
|
||||||
|
GNUNET_free (tpk);
|
||||||
|
|
||||||
|
GNUNET_CRYPTO_ecdhe_key_get_public (
|
||||||
|
&md.melted_coin.transfer_priv[i].ecdhe_priv,
|
||||||
|
&rce[i].transfer_pub.ecdhe_pub);
|
||||||
|
TALER_link_derive_transfer_secret (melt_priv,
|
||||||
|
&md.melted_coin.transfer_priv[i],
|
||||||
|
&trans_sec[i]);
|
||||||
|
md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
|
||||||
|
struct TALER_PlanchetSecretsP);
|
||||||
|
rce[i].new_coins = GNUNET_new_array (fresh_pks_len,
|
||||||
|
struct TALER_RefreshCoinData);
|
||||||
|
for (unsigned int j = 0; j<fresh_pks_len; j++)
|
||||||
|
{
|
||||||
|
struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
|
||||||
|
struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
|
||||||
|
struct TALER_PlanchetDetail pd;
|
||||||
|
|
||||||
|
TALER_planchet_setup_refresh (&trans_sec[i],
|
||||||
|
j,
|
||||||
|
fc);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_planchet_prepare (&md.fresh_pks[j],
|
||||||
|
fc,
|
||||||
|
&pd))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (&md);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
rcd->dk = &md.fresh_pks[j];
|
||||||
|
rcd->coin_ev = pd.coin_ev;
|
||||||
|
rcd->coin_ev_size = pd.coin_ev_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute refresh commitment */
|
||||||
|
TALER_refresh_get_commitment (&md.rc,
|
||||||
|
TALER_CNC_KAPPA,
|
||||||
|
fresh_pks_len,
|
||||||
|
rce,
|
||||||
|
&coin_pub,
|
||||||
|
melt_amount);
|
||||||
|
/* finally, serialize everything */
|
||||||
|
buf = serialize_melt_data (&md,
|
||||||
|
res_size);
|
||||||
|
for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
|
||||||
|
{
|
||||||
|
for (unsigned int j = 0; j < fresh_pks_len; j++)
|
||||||
|
GNUNET_free_non_null (rce[i].new_coins[j].coin_ev);
|
||||||
|
GNUNET_free_non_null (rce[i].new_coins);
|
||||||
|
}
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (&md);
|
||||||
|
return buf;
|
||||||
|
}
|
240
src/lib/exchange_api_refresh_common.h
Normal file
240
src/lib/exchange_api_refresh_common.h
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
Copyright (C) 2015-2020 Taler Systems SA
|
||||||
|
|
||||||
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file lib/exchange_api_refresh.c
|
||||||
|
* @brief Implementation of the /refresh/melt+reveal requests of the exchange's HTTP API
|
||||||
|
* @author Christian Grothoff
|
||||||
|
*/
|
||||||
|
#ifndef REFRESH_COMMON_H
|
||||||
|
#define REFRESH_COMMON_H
|
||||||
|
#include <jansson.h>
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_exchange_service.h"
|
||||||
|
#include "taler_signatures.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* structures for committing refresh data to disk before doing the
|
||||||
|
network interaction(s) */
|
||||||
|
|
||||||
|
GNUNET_NETWORK_STRUCT_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header of serialized information about a coin we are melting.
|
||||||
|
*/
|
||||||
|
struct MeltedCoinP
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private key of the coin.
|
||||||
|
*/
|
||||||
|
struct TALER_CoinSpendPrivateKeyP coin_priv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount this coin contributes to the melt, including fee.
|
||||||
|
*/
|
||||||
|
struct TALER_AmountNBO melt_amount_with_fee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The applicable fee for withdrawing a coin of this denomination
|
||||||
|
*/
|
||||||
|
struct TALER_AmountNBO fee_melt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original value of the coin.
|
||||||
|
*/
|
||||||
|
struct TALER_AmountNBO original_value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer private keys for each cut-and-choose dimension.
|
||||||
|
*/
|
||||||
|
struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp indicating when coins of this denomination become invalid.
|
||||||
|
*/
|
||||||
|
struct GNUNET_TIME_AbsoluteNBO expire_deposit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of the encoded public key that follows.
|
||||||
|
*/
|
||||||
|
uint16_t pbuf_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of the encoded signature that follows.
|
||||||
|
*/
|
||||||
|
uint16_t sbuf_size;
|
||||||
|
|
||||||
|
/* Followed by serializations of:
|
||||||
|
1) struct TALER_DenominationPublicKey pub_key;
|
||||||
|
2) struct TALER_DenominationSignature sig;
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header of serialized data about a melt operation, suitable for
|
||||||
|
* persisting it on disk.
|
||||||
|
*/
|
||||||
|
struct MeltDataP
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash over the melting session.
|
||||||
|
*/
|
||||||
|
struct TALER_RefreshCommitmentP rc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of coins we are melting, in NBO
|
||||||
|
*/
|
||||||
|
uint16_t num_melted_coins GNUNET_PACKED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of coins we are creating, in NBO
|
||||||
|
*/
|
||||||
|
uint16_t num_fresh_coins GNUNET_PACKED;
|
||||||
|
|
||||||
|
/* Followed by serializations of:
|
||||||
|
1) struct MeltedCoinP melted_coins[num_melted_coins];
|
||||||
|
2) struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins];
|
||||||
|
3) TALER_CNC_KAPPA times:
|
||||||
|
3a) struct TALER_PlanchetSecretsP fresh_coins[num_fresh_coins];
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GNUNET_NETWORK_STRUCT_END
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a coin we are melting.
|
||||||
|
*/
|
||||||
|
struct MeltedCoin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private key of the coin.
|
||||||
|
*/
|
||||||
|
struct TALER_CoinSpendPrivateKeyP coin_priv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount this coin contributes to the melt, including fee.
|
||||||
|
*/
|
||||||
|
struct TALER_Amount melt_amount_with_fee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The applicable fee for melting a coin of this denomination
|
||||||
|
*/
|
||||||
|
struct TALER_Amount fee_melt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original value of the coin.
|
||||||
|
*/
|
||||||
|
struct TALER_Amount original_value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer private keys for each cut-and-choose dimension.
|
||||||
|
*/
|
||||||
|
struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp indicating when coins of this denomination become invalid.
|
||||||
|
*/
|
||||||
|
struct GNUNET_TIME_Absolute expire_deposit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denomination key of the original coin.
|
||||||
|
*/
|
||||||
|
struct TALER_DenominationPublicKey pub_key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange's signature over the coin.
|
||||||
|
*/
|
||||||
|
struct TALER_DenominationSignature sig;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Melt data in non-serialized format for convenient processing.
|
||||||
|
*/
|
||||||
|
struct MeltData
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash over the committed data during refresh operation.
|
||||||
|
*/
|
||||||
|
struct TALER_RefreshCommitmentP rc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of coins we are creating
|
||||||
|
*/
|
||||||
|
uint16_t num_fresh_coins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the melted coin.
|
||||||
|
*/
|
||||||
|
struct MeltedCoin melted_coin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of @e num_fresh_coins denomination keys for the coins to be
|
||||||
|
* freshly exchangeed.
|
||||||
|
*/
|
||||||
|
struct TALER_DenominationPublicKey *fresh_pks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arrays of @e num_fresh_coins with information about the fresh
|
||||||
|
* coins to be created, for each cut-and-choose dimension.
|
||||||
|
*/
|
||||||
|
struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize melt data.
|
||||||
|
*
|
||||||
|
* @param md data to serialize
|
||||||
|
* @param[out] res_size size of buffer returned
|
||||||
|
* @return serialized melt data
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
TALER_EXCHANGE_serialize_melt_data_ (const struct MeltData *md,
|
||||||
|
size_t *res_size);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize melt data.
|
||||||
|
*
|
||||||
|
* @param buf serialized data
|
||||||
|
* @param buf_size size of @a buf
|
||||||
|
* @return deserialized melt data, NULL on error
|
||||||
|
*/
|
||||||
|
struct MeltData *
|
||||||
|
TALER_EXCHANGE_deserialize_melt_data_ (const char *buf,
|
||||||
|
size_t buf_size);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all information associated with a melting session. Note
|
||||||
|
* that we allow the melting session to be only partially initialized,
|
||||||
|
* as we use this function also when freeing melt data that was not
|
||||||
|
* fully initialized (i.e. due to failures in #deserialize_melt_data()).
|
||||||
|
*
|
||||||
|
* @param md melting data to release, the pointer itself is NOT
|
||||||
|
* freed (as it is typically not allocated by itself)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (struct MeltData *md);
|
||||||
|
|
||||||
|
#endif
|
512
src/lib/exchange_api_refreshes_reveal.c
Normal file
512
src/lib/exchange_api_refreshes_reveal.c
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
Copyright (C) 2015-2020 Taler Systems SA
|
||||||
|
|
||||||
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file lib/exchange_api_refreshes_reveal.c
|
||||||
|
* @brief Implementation of the /refreshes/$RCH/reveal requests
|
||||||
|
* @author Christian Grothoff
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include <jansson.h>
|
||||||
|
#include <microhttpd.h> /* just for HTTP status codes */
|
||||||
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
|
#include <gnunet/gnunet_json_lib.h>
|
||||||
|
#include <gnunet/gnunet_curl_lib.h>
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_exchange_service.h"
|
||||||
|
#include "exchange_api_handle.h"
|
||||||
|
#include "taler_signatures.h"
|
||||||
|
#include "exchange_api_curl_defaults.h"
|
||||||
|
#include "exchange_api_refresh_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A /refreshes/$RCH/reveal Handle
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_RefreshesRevealHandle
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connection to exchange this request handle will use
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_Handle *exchange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url for this request.
|
||||||
|
*/
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for #TEH_curl_easy_post(). Keeps the data that must
|
||||||
|
* persist for Curl to make the upload.
|
||||||
|
*/
|
||||||
|
struct TALER_CURL_PostContext ctx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for the request.
|
||||||
|
*/
|
||||||
|
struct GNUNET_CURL_Job *job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to call with the result.
|
||||||
|
*/
|
||||||
|
TALER_EXCHANGE_RefreshesRevealCallback reveal_cb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure for @e reveal_cb.
|
||||||
|
*/
|
||||||
|
void *reveal_cb_cls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual information about the melt operation.
|
||||||
|
*/
|
||||||
|
struct MeltData *md;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index selected by the exchange in cut-and-choose to not be revealed.
|
||||||
|
*/
|
||||||
|
uint16_t noreveal_index;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We got a 200 OK response for the /refreshes/$RCH/reveal operation.
|
||||||
|
* Extract the coin signatures and return them to the caller.
|
||||||
|
* The signatures we get from the exchange is for the blinded value.
|
||||||
|
* Thus, we first must unblind them and then should verify their
|
||||||
|
* validity.
|
||||||
|
*
|
||||||
|
* If everything checks out, we return the unblinded signatures
|
||||||
|
* to the application via the callback.
|
||||||
|
*
|
||||||
|
* @param rrh operation handle
|
||||||
|
* @param json reply from the exchange
|
||||||
|
* @param[out] sigs array of length `num_fresh_coins`, initialized to cointain RSA signatures
|
||||||
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
|
||||||
|
const json_t *json,
|
||||||
|
struct TALER_DenominationSignature *sigs)
|
||||||
|
{
|
||||||
|
json_t *jsona;
|
||||||
|
struct GNUNET_JSON_Specification outer_spec[] = {
|
||||||
|
GNUNET_JSON_spec_json ("ev_sigs", &jsona),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (json,
|
||||||
|
outer_spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (! json_is_array (jsona))
|
||||||
|
{
|
||||||
|
/* We expected an array of coins */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (outer_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (rrh->md->num_fresh_coins != json_array_size (jsona))
|
||||||
|
{
|
||||||
|
/* Number of coins generated does not match our expectation */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (outer_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
|
||||||
|
{
|
||||||
|
const struct TALER_PlanchetSecretsP *fc;
|
||||||
|
struct TALER_DenominationPublicKey *pk;
|
||||||
|
json_t *jsonai;
|
||||||
|
struct GNUNET_CRYPTO_RsaSignature *blind_sig;
|
||||||
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||||
|
struct GNUNET_HashCode coin_hash;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
struct TALER_FreshCoin coin;
|
||||||
|
|
||||||
|
fc = &rrh->md->fresh_coins[rrh->noreveal_index][i];
|
||||||
|
pk = &rrh->md->fresh_pks[i];
|
||||||
|
jsonai = json_array_get (jsona, i);
|
||||||
|
GNUNET_assert (NULL != jsonai);
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (jsonai,
|
||||||
|
spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (outer_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* needed to verify the signature, and we didn't store it earlier,
|
||||||
|
hence recomputing it here... */
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
|
||||||
|
&coin_pub.eddsa_pub);
|
||||||
|
GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub,
|
||||||
|
sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
|
||||||
|
&coin_hash);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_planchet_to_coin (pk,
|
||||||
|
blind_sig,
|
||||||
|
fc,
|
||||||
|
&coin_hash,
|
||||||
|
&coin))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_CRYPTO_rsa_signature_free (blind_sig);
|
||||||
|
GNUNET_JSON_parse_free (outer_spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
GNUNET_CRYPTO_rsa_signature_free (blind_sig);
|
||||||
|
sigs[i] = coin.sig;
|
||||||
|
}
|
||||||
|
GNUNET_JSON_parse_free (outer_spec);
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when we're done processing the
|
||||||
|
* HTTP /refreshes/$RCH/reveal request.
|
||||||
|
*
|
||||||
|
* @param cls the `struct TALER_EXCHANGE_RefreshHandle`
|
||||||
|
* @param response_code HTTP response code, 0 on error
|
||||||
|
* @param response parsed JSON result, NULL on error
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_refresh_reveal_finished (void *cls,
|
||||||
|
long response_code,
|
||||||
|
const void *response)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls;
|
||||||
|
const json_t *j = response;
|
||||||
|
|
||||||
|
rrh->job = NULL;
|
||||||
|
switch (response_code)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_OK:
|
||||||
|
{
|
||||||
|
struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset (sigs, 0, sizeof (sigs));
|
||||||
|
ret = refresh_reveal_ok (rrh,
|
||||||
|
j,
|
||||||
|
sigs);
|
||||||
|
if (GNUNET_OK != ret)
|
||||||
|
{
|
||||||
|
response_code = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rrh->reveal_cb (rrh->reveal_cb_cls,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
TALER_EC_NONE,
|
||||||
|
rrh->md->num_fresh_coins,
|
||||||
|
rrh->md->fresh_coins[rrh->noreveal_index],
|
||||||
|
sigs,
|
||||||
|
j);
|
||||||
|
rrh->reveal_cb = NULL;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
|
||||||
|
if (NULL != sigs[i].rsa_signature)
|
||||||
|
GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
|
/* This should never happen, either us or the exchange is buggy
|
||||||
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_CONFLICT:
|
||||||
|
/* Nothing really to verify, exchange says our reveal is inconsitent
|
||||||
|
with our commitment, so either side is buggy; we
|
||||||
|
should pass the JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
|
/* Server had an internal issue; we should retry, but this API
|
||||||
|
leaves this to the application */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unexpected response code */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Unexpected response code %u\n",
|
||||||
|
(unsigned int) response_code);
|
||||||
|
GNUNET_break (0);
|
||||||
|
response_code = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NULL != rrh->reveal_cb)
|
||||||
|
rrh->reveal_cb (rrh->reveal_cb_cls,
|
||||||
|
response_code,
|
||||||
|
TALER_JSON_get_error_code (j),
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
j);
|
||||||
|
TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a /refresh/reval request to the exchange and get the exchange's
|
||||||
|
* response.
|
||||||
|
*
|
||||||
|
* This API is typically used by a wallet. Note that to ensure that
|
||||||
|
* no money is lost in case of hardware failures, the provided
|
||||||
|
* arguments should have been committed to persistent storage
|
||||||
|
* prior to calling this function.
|
||||||
|
*
|
||||||
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
|
* @param refresh_data_length size of the @a refresh_data (returned
|
||||||
|
* in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
|
||||||
|
* @param refresh_data the refresh data as returned from
|
||||||
|
#TALER_EXCHANGE_refresh_prepare())
|
||||||
|
* @param noreveal_index response from the exchange to the
|
||||||
|
* #TALER_EXCHANGE_melt() invocation
|
||||||
|
* @param reveal_cb the callback to call with the final result of the
|
||||||
|
* refresh operation
|
||||||
|
* @param reveal_cb_cls closure for the above callback
|
||||||
|
* @return a handle for this request; NULL if the argument was invalid.
|
||||||
|
* In this case, neither callback will be called.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_RefreshesRevealHandle *
|
||||||
|
TALER_EXCHANGE_refreshes_reveal (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
size_t refresh_data_length,
|
||||||
|
const char *refresh_data,
|
||||||
|
uint32_t noreveal_index,
|
||||||
|
TALER_EXCHANGE_RefreshesRevealCallback
|
||||||
|
reveal_cb,
|
||||||
|
void *reveal_cb_cls)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
|
||||||
|
json_t *transfer_privs;
|
||||||
|
json_t *new_denoms_h;
|
||||||
|
json_t *coin_evs;
|
||||||
|
json_t *reveal_obj;
|
||||||
|
json_t *link_sigs;
|
||||||
|
CURL *eh;
|
||||||
|
struct GNUNET_CURL_Context *ctx;
|
||||||
|
struct MeltData *md;
|
||||||
|
struct TALER_TransferPublicKeyP transfer_pub;
|
||||||
|
char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32];
|
||||||
|
|
||||||
|
if (noreveal_index >= TALER_CNC_KAPPA)
|
||||||
|
{
|
||||||
|
/* We check this here, as it would be really bad to below just
|
||||||
|
disclose all the transfer keys. Note that this error should
|
||||||
|
have been caught way earlier when the exchange replied, but maybe
|
||||||
|
we had some internal corruption that changed the value... */
|
||||||
|
GNUNET_break (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (GNUNET_YES !=
|
||||||
|
TEAH_handle_is_ready (exchange))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
|
||||||
|
refresh_data_length);
|
||||||
|
if (NULL == md)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now transfer_pub */
|
||||||
|
GNUNET_CRYPTO_ecdhe_key_get_public (
|
||||||
|
&md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv,
|
||||||
|
&transfer_pub.ecdhe_pub);
|
||||||
|
|
||||||
|
/* now new_denoms */
|
||||||
|
GNUNET_assert (NULL != (new_denoms_h = json_array ()));
|
||||||
|
GNUNET_assert (NULL != (coin_evs = json_array ()));
|
||||||
|
GNUNET_assert (NULL != (link_sigs = json_array ()));
|
||||||
|
for (unsigned int i = 0; i<md->num_fresh_coins; i++)
|
||||||
|
{
|
||||||
|
struct GNUNET_HashCode denom_hash;
|
||||||
|
struct TALER_PlanchetDetail pd;
|
||||||
|
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_hash (md->fresh_pks[i].rsa_public_key,
|
||||||
|
&denom_hash);
|
||||||
|
GNUNET_assert (0 ==
|
||||||
|
json_array_append_new (new_denoms_h,
|
||||||
|
GNUNET_JSON_from_data_auto (
|
||||||
|
&denom_hash)));
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_planchet_prepare (&md->fresh_pks[i],
|
||||||
|
&md->fresh_coins[noreveal_index][i],
|
||||||
|
&pd))
|
||||||
|
{
|
||||||
|
/* This should have been noticed during the preparation stage. */
|
||||||
|
GNUNET_break (0);
|
||||||
|
json_decref (new_denoms_h);
|
||||||
|
json_decref (coin_evs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
GNUNET_assert (0 ==
|
||||||
|
json_array_append_new (coin_evs,
|
||||||
|
GNUNET_JSON_from_data (pd.coin_ev,
|
||||||
|
pd.coin_ev_size)));
|
||||||
|
|
||||||
|
/* compute link signature */
|
||||||
|
{
|
||||||
|
struct TALER_CoinSpendSignatureP link_sig;
|
||||||
|
struct TALER_LinkDataPS ldp;
|
||||||
|
|
||||||
|
ldp.purpose.size = htonl (sizeof (ldp));
|
||||||
|
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
|
||||||
|
ldp.h_denom_pub = denom_hash;
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
|
||||||
|
&ldp.old_coin_pub.eddsa_pub);
|
||||||
|
ldp.transfer_pub = transfer_pub;
|
||||||
|
GNUNET_CRYPTO_hash (pd.coin_ev,
|
||||||
|
pd.coin_ev_size,
|
||||||
|
&ldp.coin_envelope_hash);
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
GNUNET_CRYPTO_eddsa_sign (
|
||||||
|
&md->melted_coin.coin_priv.eddsa_priv,
|
||||||
|
&ldp.purpose,
|
||||||
|
&link_sig.eddsa_signature));
|
||||||
|
GNUNET_assert (0 ==
|
||||||
|
json_array_append_new (link_sigs,
|
||||||
|
GNUNET_JSON_from_data_auto (
|
||||||
|
&link_sig)));
|
||||||
|
}
|
||||||
|
|
||||||
|
GNUNET_free (pd.coin_ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build array of transfer private keys */
|
||||||
|
GNUNET_assert (NULL != (transfer_privs = json_array ()));
|
||||||
|
for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++)
|
||||||
|
{
|
||||||
|
if (j == noreveal_index)
|
||||||
|
{
|
||||||
|
/* This is crucial: exclude the transfer key for the
|
||||||
|
noreval index! */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GNUNET_assert (0 ==
|
||||||
|
json_array_append_new (transfer_privs,
|
||||||
|
GNUNET_JSON_from_data_auto (
|
||||||
|
&md->melted_coin.transfer_priv[j])));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build main JSON request */
|
||||||
|
reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}",
|
||||||
|
"transfer_pub",
|
||||||
|
GNUNET_JSON_from_data_auto (&transfer_pub),
|
||||||
|
"transfer_privs",
|
||||||
|
transfer_privs,
|
||||||
|
"link_sigs",
|
||||||
|
link_sigs,
|
||||||
|
"new_denoms_h",
|
||||||
|
new_denoms_h,
|
||||||
|
"coin_evs",
|
||||||
|
coin_evs);
|
||||||
|
if (NULL == reveal_obj)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2];
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
end = GNUNET_STRINGS_data_to_string (&md->rc,
|
||||||
|
sizeof (struct
|
||||||
|
TALER_RefreshCommitmentP),
|
||||||
|
pub_str,
|
||||||
|
sizeof (pub_str));
|
||||||
|
*end = '\0';
|
||||||
|
GNUNET_snprintf (arg_str,
|
||||||
|
sizeof (arg_str),
|
||||||
|
"/refreshes/%s/reveal",
|
||||||
|
pub_str);
|
||||||
|
}
|
||||||
|
/* finally, we can actually issue the request */
|
||||||
|
rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle);
|
||||||
|
rrh->exchange = exchange;
|
||||||
|
rrh->noreveal_index = noreveal_index;
|
||||||
|
rrh->reveal_cb = reveal_cb;
|
||||||
|
rrh->reveal_cb_cls = reveal_cb_cls;
|
||||||
|
rrh->md = md;
|
||||||
|
rrh->url = TEAH_path_to_url (rrh->exchange,
|
||||||
|
arg_str);
|
||||||
|
|
||||||
|
eh = TEL_curl_easy_get (rrh->url);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_curl_easy_post (&rrh->ctx,
|
||||||
|
eh,
|
||||||
|
reveal_obj))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
curl_easy_cleanup (eh);
|
||||||
|
json_decref (reveal_obj);
|
||||||
|
GNUNET_free (rrh->url);
|
||||||
|
GNUNET_free (rrh);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
json_decref (reveal_obj);
|
||||||
|
ctx = TEAH_handle_to_context (rrh->exchange);
|
||||||
|
rrh->job = GNUNET_CURL_job_add2 (ctx,
|
||||||
|
eh,
|
||||||
|
rrh->ctx.headers,
|
||||||
|
&handle_refresh_reveal_finished,
|
||||||
|
rrh);
|
||||||
|
return rrh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a refresh reveal request. This function cannot be used
|
||||||
|
* on a request handle if the callback was already invoked.
|
||||||
|
*
|
||||||
|
* @param rrh the refresh reval handle
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_refreshes_reveal_cancel (struct
|
||||||
|
TALER_EXCHANGE_RefreshesRevealHandle *
|
||||||
|
rrh)
|
||||||
|
{
|
||||||
|
if (NULL != rrh->job)
|
||||||
|
{
|
||||||
|
GNUNET_CURL_job_cancel (rrh->job);
|
||||||
|
rrh->job = NULL;
|
||||||
|
}
|
||||||
|
GNUNET_free (rrh->url);
|
||||||
|
TALER_curl_easy_post_finished (&rrh->ctx);
|
||||||
|
TALER_EXCHANGE_free_melt_data_ (rrh->md); /* does not free 'md' itself */
|
||||||
|
GNUNET_free (rrh->md);
|
||||||
|
GNUNET_free (rrh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* exchange_api_refreshes_reveal.c */
|
File diff suppressed because it is too large
Load Diff
308
src/lib/exchange_api_reserves_get.c
Normal file
308
src/lib/exchange_api_reserves_get.c
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
/*
|
||||||
|
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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file lib/exchange_api_reserves_get.c
|
||||||
|
* @brief Implementation of the GET /reserves/$RESERVE_PUB requests
|
||||||
|
* @author Christian Grothoff
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include <jansson.h>
|
||||||
|
#include <microhttpd.h> /* just for HTTP status codes */
|
||||||
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
|
#include <gnunet/gnunet_json_lib.h>
|
||||||
|
#include <gnunet/gnunet_curl_lib.h>
|
||||||
|
#include "taler_exchange_service.h"
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include "exchange_api_handle.h"
|
||||||
|
#include "taler_signatures.h"
|
||||||
|
#include "exchange_api_curl_defaults.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A /reserves/ GET Handle
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_ReservesGetHandle
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connection to exchange this request handle will use
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_Handle *exchange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url for this request.
|
||||||
|
*/
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for the request.
|
||||||
|
*/
|
||||||
|
struct GNUNET_CURL_Job *job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to call with the result.
|
||||||
|
*/
|
||||||
|
TALER_EXCHANGE_ReservesGetCallback cb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key of the reserve we are querying.
|
||||||
|
*/
|
||||||
|
struct TALER_ReservePublicKeyP reserve_pub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure for @a cb.
|
||||||
|
*/
|
||||||
|
void *cb_cls;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We received an #MHD_HTTP_OK status code. Handle the JSON
|
||||||
|
* response.
|
||||||
|
*
|
||||||
|
* @param rgh handle of the request
|
||||||
|
* @param j JSON response
|
||||||
|
* @return #GNUNET_OK on success
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
|
||||||
|
const json_t *j)
|
||||||
|
{
|
||||||
|
json_t *history;
|
||||||
|
unsigned int len;
|
||||||
|
struct TALER_Amount balance;
|
||||||
|
struct TALER_Amount balance_from_history;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
TALER_JSON_spec_amount ("balance", &balance),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (j,
|
||||||
|
spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
history = json_object_get (j,
|
||||||
|
"history");
|
||||||
|
if (NULL == history)
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
len = json_array_size (history);
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
||||||
|
|
||||||
|
rhistory = GNUNET_new_array (len,
|
||||||
|
struct TALER_EXCHANGE_ReserveHistory);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
|
||||||
|
history,
|
||||||
|
&rgh->reserve_pub,
|
||||||
|
balance.currency,
|
||||||
|
&balance_from_history,
|
||||||
|
len,
|
||||||
|
rhistory))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
|
len);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (0 !=
|
||||||
|
TALER_amount_cmp (&balance_from_history,
|
||||||
|
&balance))
|
||||||
|
{
|
||||||
|
/* exchange cannot add up balances!? */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
|
len);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (NULL != rgh->cb)
|
||||||
|
{
|
||||||
|
rgh->cb (rgh->cb_cls,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
TALER_EC_NONE,
|
||||||
|
j,
|
||||||
|
&balance,
|
||||||
|
len,
|
||||||
|
rhistory);
|
||||||
|
rgh->cb = NULL;
|
||||||
|
}
|
||||||
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
|
len);
|
||||||
|
}
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when we're done processing the
|
||||||
|
* HTTP /reserves/ GET request.
|
||||||
|
*
|
||||||
|
* @param cls the `struct TALER_EXCHANGE_ReservesGetHandle`
|
||||||
|
* @param response_code HTTP response code, 0 on error
|
||||||
|
* @param response parsed JSON result, NULL on error
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_reserves_get_finished (void *cls,
|
||||||
|
long response_code,
|
||||||
|
const void *response)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
|
||||||
|
const json_t *j = response;
|
||||||
|
|
||||||
|
rgh->job = NULL;
|
||||||
|
switch (response_code)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_OK:
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
handle_reserves_get_ok (rgh,
|
||||||
|
j))
|
||||||
|
response_code = 0;
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
|
/* This should never happen, either us or the exchange is buggy
|
||||||
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_NOT_FOUND:
|
||||||
|
/* Nothing really to verify, this should never
|
||||||
|
happen, we should pass the JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
|
/* Server had an internal issue; we should retry, but this API
|
||||||
|
leaves this to the application */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unexpected response code */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Unexpected response code %u\n",
|
||||||
|
(unsigned int) response_code);
|
||||||
|
GNUNET_break (0);
|
||||||
|
response_code = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NULL != rgh->cb)
|
||||||
|
{
|
||||||
|
rgh->cb (rgh->cb_cls,
|
||||||
|
response_code,
|
||||||
|
TALER_JSON_get_error_code (j),
|
||||||
|
j,
|
||||||
|
NULL,
|
||||||
|
0, NULL);
|
||||||
|
rgh->cb = NULL;
|
||||||
|
}
|
||||||
|
TALER_EXCHANGE_reserves_get_cancel (rgh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a request to obtain the transaction history of a reserve
|
||||||
|
* from the exchange. Note that while we return the full response to the
|
||||||
|
* caller for further processing, we do already verify that the
|
||||||
|
* response is well-formed (i.e. that signatures included in the
|
||||||
|
* response are all valid and add up to the balance). If the exchange's
|
||||||
|
* reply is not well-formed, we return an HTTP status code of zero to
|
||||||
|
* @a cb.
|
||||||
|
*
|
||||||
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
|
* @param reserve_pub public key of the reserve to inspect
|
||||||
|
* @param cb the callback to call when a reply for this request is available
|
||||||
|
* @param cb_cls closure for the above callback
|
||||||
|
* @return a handle for this request; NULL if the inputs are invalid (i.e.
|
||||||
|
* signatures fail to verify). In this case, the callback is not called.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_ReservesGetHandle *
|
||||||
|
TALER_EXCHANGE_reserves_get (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
const struct
|
||||||
|
TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
TALER_EXCHANGE_ReservesGetCallback cb,
|
||||||
|
void *cb_cls)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_ReservesGetHandle *rgh;
|
||||||
|
struct GNUNET_CURL_Context *ctx;
|
||||||
|
CURL *eh;
|
||||||
|
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16];
|
||||||
|
|
||||||
|
if (GNUNET_YES !=
|
||||||
|
TEAH_handle_is_ready (exchange))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
end = GNUNET_STRINGS_data_to_string (reserve_pub,
|
||||||
|
sizeof (struct
|
||||||
|
TALER_ReservePublicKeyP),
|
||||||
|
pub_str,
|
||||||
|
sizeof (pub_str));
|
||||||
|
*end = '\0';
|
||||||
|
GNUNET_snprintf (arg_str,
|
||||||
|
sizeof (arg_str),
|
||||||
|
"/reserves/%s",
|
||||||
|
pub_str);
|
||||||
|
}
|
||||||
|
rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesGetHandle);
|
||||||
|
rgh->exchange = exchange;
|
||||||
|
rgh->cb = cb;
|
||||||
|
rgh->cb_cls = cb_cls;
|
||||||
|
rgh->reserve_pub = *reserve_pub;
|
||||||
|
rgh->url = TEAH_path_to_url (exchange,
|
||||||
|
arg_str);
|
||||||
|
eh = TEL_curl_easy_get (rgh->url);
|
||||||
|
ctx = TEAH_handle_to_context (exchange);
|
||||||
|
rgh->job = GNUNET_CURL_job_add (ctx,
|
||||||
|
eh,
|
||||||
|
GNUNET_NO,
|
||||||
|
&handle_reserves_get_finished,
|
||||||
|
rgh);
|
||||||
|
return rgh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a reserve status request. This function cannot be used
|
||||||
|
* on a request handle if a response is already served for it.
|
||||||
|
*
|
||||||
|
* @param rgh the reserve status request handle
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_reserves_get_cancel (struct
|
||||||
|
TALER_EXCHANGE_ReservesGetHandle *rgh)
|
||||||
|
{
|
||||||
|
if (NULL != rgh->job)
|
||||||
|
{
|
||||||
|
GNUNET_CURL_job_cancel (rgh->job);
|
||||||
|
rgh->job = NULL;
|
||||||
|
}
|
||||||
|
GNUNET_free (rgh->url);
|
||||||
|
GNUNET_free (rgh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* end of exchange_api_reserve.c */
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014, 2015, 2016 Taler Systems SA
|
Copyright (C) 2014-2020 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 General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -15,8 +15,8 @@
|
|||||||
<http://www.gnu.org/licenses/>
|
<http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file lib/exchange_api_track_transfer.c
|
* @file lib/exchange_api_transfers_get.c
|
||||||
* @brief Implementation of the /track/transfer request of the exchange's HTTP API
|
* @brief Implementation of the GET /transfers/ request
|
||||||
* @author Christian Grothoff
|
* @author Christian Grothoff
|
||||||
*/
|
*/
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A /track/transfer Handle
|
* @brief A /transfers/ GET Handle
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_TransfersGetHandle
|
struct TALER_EXCHANGE_TransfersGetHandle
|
||||||
{
|
{
|
||||||
@ -66,12 +66,12 @@ struct TALER_EXCHANGE_TransfersGetHandle
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We got a #MHD_HTTP_OK response for the /track/transfer request.
|
* We got a #MHD_HTTP_OK response for the /transfers/ request.
|
||||||
* Check that the response is well-formed and if it is, call the
|
* Check that the response is well-formed and if it is, call the
|
||||||
* callback. If not, return an error code.
|
* callback. If not, return an error code.
|
||||||
*
|
*
|
||||||
* This code is very similar to
|
* This code is very similar to
|
||||||
* merchant_api_track_transfer.c::check_track_transfer_response_ok.
|
* merchant_api_track_transfer.c::check_transfers_get_response_ok.
|
||||||
* Any changes should likely be reflected there as well.
|
* Any changes should likely be reflected there as well.
|
||||||
*
|
*
|
||||||
* @param wdh handle to the operation
|
* @param wdh handle to the operation
|
||||||
@ -80,9 +80,9 @@ struct TALER_EXCHANGE_TransfersGetHandle
|
|||||||
* #GNUNET_SYSERR if the response was bogus
|
* #GNUNET_SYSERR if the response was bogus
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_track_transfer_response_ok (struct
|
check_transfers_get_response_ok (struct
|
||||||
TALER_EXCHANGE_TransfersGetHandle *wdh,
|
TALER_EXCHANGE_TransfersGetHandle *wdh,
|
||||||
const json_t *json)
|
const json_t *json)
|
||||||
{
|
{
|
||||||
json_t *details_j;
|
json_t *details_j;
|
||||||
struct GNUNET_HashCode h_wire;
|
struct GNUNET_HashCode h_wire;
|
||||||
@ -248,16 +248,16 @@ check_track_transfer_response_ok (struct
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called when we're done processing the
|
* Function called when we're done processing the
|
||||||
* HTTP /track/transfer request.
|
* HTTP /transfers/ request.
|
||||||
*
|
*
|
||||||
* @param cls the `struct TALER_EXCHANGE_TransfersGetHandle`
|
* @param cls the `struct TALER_EXCHANGE_TransfersGetHandle`
|
||||||
* @param response_code HTTP response code, 0 on error
|
* @param response_code HTTP response code, 0 on error
|
||||||
* @param response parsed JSON result, NULL on error
|
* @param response parsed JSON result, NULL on error
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
handle_track_transfer_finished (void *cls,
|
handle_transfers_get_finished (void *cls,
|
||||||
long response_code,
|
long response_code,
|
||||||
const void *response)
|
const void *response)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
|
struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
|
||||||
const json_t *j = response;
|
const json_t *j = response;
|
||||||
@ -269,8 +269,8 @@ handle_track_transfer_finished (void *cls,
|
|||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (GNUNET_OK ==
|
if (GNUNET_OK ==
|
||||||
check_track_transfer_response_ok (wdh,
|
check_transfers_get_response_ok (wdh,
|
||||||
j))
|
j))
|
||||||
return;
|
return;
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
response_code = 0;
|
response_code = 0;
|
||||||
@ -371,7 +371,7 @@ TALER_EXCHANGE_transfers_get (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
wdh->job = GNUNET_CURL_job_add (ctx,
|
wdh->job = GNUNET_CURL_job_add (ctx,
|
||||||
eh,
|
eh,
|
||||||
GNUNET_YES,
|
GNUNET_YES,
|
||||||
&handle_track_transfer_finished,
|
&handle_transfers_get_finished,
|
||||||
wdh);
|
wdh);
|
||||||
return wdh;
|
return wdh;
|
||||||
}
|
}
|
611
src/lib/exchange_api_withdraw.c
Normal file
611
src/lib/exchange_api_withdraw.c
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
/*
|
||||||
|
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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file lib/exchange_api_withdraw.c
|
||||||
|
* @brief Implementation of the /reserves/$RESERVE_PUB/withdraw requests
|
||||||
|
* @author Christian Grothoff
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include <jansson.h>
|
||||||
|
#include <microhttpd.h> /* just for HTTP status codes */
|
||||||
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
|
#include <gnunet/gnunet_json_lib.h>
|
||||||
|
#include <gnunet/gnunet_curl_lib.h>
|
||||||
|
#include "taler_exchange_service.h"
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include "exchange_api_handle.h"
|
||||||
|
#include "taler_signatures.h"
|
||||||
|
#include "exchange_api_curl_defaults.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A Withdraw Handle
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connection to exchange this request handle will use
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_Handle *exchange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url for this request.
|
||||||
|
*/
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for #TEH_curl_easy_post(). Keeps the data that must
|
||||||
|
* persist for Curl to make the upload.
|
||||||
|
*/
|
||||||
|
struct TALER_CURL_PostContext ctx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for the request.
|
||||||
|
*/
|
||||||
|
struct GNUNET_CURL_Job *job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to call with the result.
|
||||||
|
*/
|
||||||
|
TALER_EXCHANGE_WithdrawCallback cb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secrets of the planchet.
|
||||||
|
*/
|
||||||
|
struct TALER_PlanchetSecretsP ps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denomination key we are withdrawing.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_DenomPublicKey pk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure for @a cb.
|
||||||
|
*/
|
||||||
|
void *cb_cls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash of the public key of the coin we are signing.
|
||||||
|
*/
|
||||||
|
struct GNUNET_HashCode c_hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key of the reserve we are withdrawing from.
|
||||||
|
*/
|
||||||
|
struct TALER_ReservePublicKeyP reserve_pub;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
|
||||||
|
* Extract the coin's signature and return it to the caller. The signature we
|
||||||
|
* get from the exchange is for the blinded value. Thus, we first must
|
||||||
|
* unblind it and then should verify its validity against our coin's hash.
|
||||||
|
*
|
||||||
|
* If everything checks out, we return the unblinded signature
|
||||||
|
* to the application via the callback.
|
||||||
|
*
|
||||||
|
* @param wh operation handle
|
||||||
|
* @param json reply from the exchange
|
||||||
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
reserve_withdraw_ok (struct TALER_EXCHANGE_WithdrawHandle *wh,
|
||||||
|
const json_t *json)
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_RsaSignature *blind_sig;
|
||||||
|
struct TALER_FreshCoin fc;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_rsa_signature ("ev_sig",
|
||||||
|
&blind_sig),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (json,
|
||||||
|
spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_planchet_to_coin (&wh->pk.key,
|
||||||
|
blind_sig,
|
||||||
|
&wh->ps,
|
||||||
|
&wh->c_hash,
|
||||||
|
&fc))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
|
||||||
|
/* signature is valid, return it to the application */
|
||||||
|
wh->cb (wh->cb_cls,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
TALER_EC_NONE,
|
||||||
|
&fc.sig,
|
||||||
|
json);
|
||||||
|
/* make sure callback isn't called again after return */
|
||||||
|
wh->cb = NULL;
|
||||||
|
GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We got a 409 CONFLICT response for the /reserves/$RESERVE_PUB/withdraw operation.
|
||||||
|
* Check the signatures on the withdraw transactions in the provided
|
||||||
|
* history and that the balances add up. We don't do anything directly
|
||||||
|
* with the information, as the JSON will be returned to the application.
|
||||||
|
* However, our job is ensuring that the exchange followed the protocol, and
|
||||||
|
* this in particular means checking all of the signatures in the history.
|
||||||
|
*
|
||||||
|
* @param wh operation handle
|
||||||
|
* @param json reply from the exchange
|
||||||
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
reserve_withdraw_payment_required (struct
|
||||||
|
TALER_EXCHANGE_WithdrawHandle *wh,
|
||||||
|
const json_t *json)
|
||||||
|
{
|
||||||
|
struct TALER_Amount balance;
|
||||||
|
struct TALER_Amount balance_from_history;
|
||||||
|
struct TALER_Amount requested_amount;
|
||||||
|
json_t *history;
|
||||||
|
size_t len;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
TALER_JSON_spec_amount ("balance", &balance),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (json,
|
||||||
|
spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
history = json_object_get (json,
|
||||||
|
"history");
|
||||||
|
if (NULL == history)
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* go over transaction history and compute
|
||||||
|
total incoming and outgoing amounts */
|
||||||
|
len = json_array_size (history);
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
||||||
|
|
||||||
|
/* Use heap allocation as "len" may be very big and thus this may
|
||||||
|
not fit on the stack. Use "GNUNET_malloc_large" as a malicious
|
||||||
|
exchange may theoretically try to crash us by giving a history
|
||||||
|
that does not fit into our memory. */
|
||||||
|
rhistory = GNUNET_malloc_large (sizeof (struct
|
||||||
|
TALER_EXCHANGE_ReserveHistory)
|
||||||
|
* len);
|
||||||
|
if (NULL == rhistory)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_EXCHANGE_parse_reserve_history (wh->exchange,
|
||||||
|
history,
|
||||||
|
&wh->reserve_pub,
|
||||||
|
balance.currency,
|
||||||
|
&balance_from_history,
|
||||||
|
len,
|
||||||
|
rhistory))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
|
len);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
|
len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 !=
|
||||||
|
TALER_amount_cmp (&balance_from_history,
|
||||||
|
&balance))
|
||||||
|
{
|
||||||
|
/* exchange cannot add up balances!? */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
/* Compute how much we expected to charge to the reserve */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&requested_amount,
|
||||||
|
&wh->pk.value,
|
||||||
|
&wh->pk.fee_withdraw))
|
||||||
|
{
|
||||||
|
/* Overflow here? Very strange, our CPU must be fried... */
|
||||||
|
GNUNET_break (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
/* Check that funds were really insufficient */
|
||||||
|
if (0 >= TALER_amount_cmp (&requested_amount,
|
||||||
|
&balance))
|
||||||
|
{
|
||||||
|
/* Requested amount is smaller or equal to reported balance,
|
||||||
|
so this should not have failed. */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when we're done processing the
|
||||||
|
* HTTP /reserves/$RESERVE_PUB/withdraw request.
|
||||||
|
*
|
||||||
|
* @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
|
||||||
|
* @param response_code HTTP response code, 0 on error
|
||||||
|
* @param response parsed JSON result, NULL on error
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_reserve_withdraw_finished (void *cls,
|
||||||
|
long response_code,
|
||||||
|
const void *response)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
|
||||||
|
const json_t *j = response;
|
||||||
|
|
||||||
|
wh->job = NULL;
|
||||||
|
switch (response_code)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_OK:
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
reserve_withdraw_ok (wh,
|
||||||
|
j))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
response_code = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
|
/* This should never happen, either us or the exchange is buggy
|
||||||
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_CONFLICT:
|
||||||
|
/* The exchange says that the reserve has insufficient funds;
|
||||||
|
check the signatures in the history... */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
reserve_withdraw_payment_required (wh,
|
||||||
|
j))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
response_code = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_FORBIDDEN:
|
||||||
|
GNUNET_break (0);
|
||||||
|
/* Nothing really to verify, exchange says one of the signatures is
|
||||||
|
invalid; as we checked them, this should never happen, we
|
||||||
|
should pass the JSON reply to the application */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_NOT_FOUND:
|
||||||
|
/* Nothing really to verify, the exchange basically just says
|
||||||
|
that it doesn't know this reserve. Can happen if we
|
||||||
|
query before the wire transfer went through.
|
||||||
|
We should simply pass the JSON reply to the application. */
|
||||||
|
break;
|
||||||
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
|
/* Server had an internal issue; we should retry, but this API
|
||||||
|
leaves this to the application */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unexpected response code */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Unexpected response code %u\n",
|
||||||
|
(unsigned int) response_code);
|
||||||
|
GNUNET_break (0);
|
||||||
|
response_code = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NULL != wh->cb)
|
||||||
|
{
|
||||||
|
wh->cb (wh->cb_cls,
|
||||||
|
response_code,
|
||||||
|
TALER_JSON_get_error_code (j),
|
||||||
|
NULL,
|
||||||
|
j);
|
||||||
|
wh->cb = NULL;
|
||||||
|
}
|
||||||
|
TALER_EXCHANGE_withdraw_cancel (wh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for #TALER_EXCHANGE_withdraw2() and
|
||||||
|
* #TALER_EXCHANGE_withdraw().
|
||||||
|
*
|
||||||
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
|
* @param pk kind of coin to create
|
||||||
|
* @param reserve_sig signature from the reserve authorizing the withdrawal
|
||||||
|
* @param reserve_pub public key of the reserve to withdraw from
|
||||||
|
* @param ps secrets of the planchet
|
||||||
|
* caller must have committed this value to disk before the call (with @a pk)
|
||||||
|
* @param pd planchet details matching @a ps
|
||||||
|
* @param res_cb the callback to call when the final result for this request is available
|
||||||
|
* @param res_cb_cls closure for @a res_cb
|
||||||
|
* @return NULL
|
||||||
|
* if the inputs are invalid (i.e. denomination key not with this exchange).
|
||||||
|
* In this case, the callback is not called.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *
|
||||||
|
reserve_withdraw_internal (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
const struct TALER_EXCHANGE_DenomPublicKey *pk,
|
||||||
|
const struct TALER_ReserveSignatureP *reserve_sig,
|
||||||
|
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
const struct TALER_PlanchetSecretsP *ps,
|
||||||
|
const struct TALER_PlanchetDetail *pd,
|
||||||
|
TALER_EXCHANGE_WithdrawCallback res_cb,
|
||||||
|
void *res_cb_cls)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *wh;
|
||||||
|
struct GNUNET_CURL_Context *ctx;
|
||||||
|
json_t *withdraw_obj;
|
||||||
|
CURL *eh;
|
||||||
|
struct GNUNET_HashCode h_denom_pub;
|
||||||
|
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
||||||
|
|
||||||
|
{
|
||||||
|
char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
end = GNUNET_STRINGS_data_to_string (reserve_pub,
|
||||||
|
sizeof (struct
|
||||||
|
TALER_ReservePublicKeyP),
|
||||||
|
pub_str,
|
||||||
|
sizeof (pub_str));
|
||||||
|
*end = '\0';
|
||||||
|
GNUNET_snprintf (arg_str,
|
||||||
|
sizeof (arg_str),
|
||||||
|
"/reserves/%s/withdraw",
|
||||||
|
pub_str);
|
||||||
|
}
|
||||||
|
wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
|
||||||
|
wh->exchange = exchange;
|
||||||
|
wh->cb = res_cb;
|
||||||
|
wh->cb_cls = res_cb_cls;
|
||||||
|
wh->pk = *pk;
|
||||||
|
wh->pk.key.rsa_public_key
|
||||||
|
= GNUNET_CRYPTO_rsa_public_key_dup (pk->key.rsa_public_key);
|
||||||
|
wh->reserve_pub = *reserve_pub;
|
||||||
|
wh->c_hash = pd->c_hash;
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key,
|
||||||
|
&h_denom_pub);
|
||||||
|
withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub_hash and coin_ev */
|
||||||
|
" s:o}",/* reserve_pub and reserve_sig */
|
||||||
|
"denom_pub_hash", GNUNET_JSON_from_data_auto (
|
||||||
|
&h_denom_pub),
|
||||||
|
"coin_ev", GNUNET_JSON_from_data (pd->coin_ev,
|
||||||
|
pd->coin_ev_size),
|
||||||
|
"reserve_sig", GNUNET_JSON_from_data_auto (
|
||||||
|
reserve_sig));
|
||||||
|
if (NULL == withdraw_obj)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
|
||||||
|
GNUNET_free (wh);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Attempting to withdraw from reserve %s\n",
|
||||||
|
TALER_B2S (reserve_pub));
|
||||||
|
wh->ps = *ps;
|
||||||
|
wh->url = TEAH_path_to_url (exchange,
|
||||||
|
arg_str);
|
||||||
|
eh = TEL_curl_easy_get (wh->url);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_curl_easy_post (&wh->ctx,
|
||||||
|
eh,
|
||||||
|
withdraw_obj))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
curl_easy_cleanup (eh);
|
||||||
|
json_decref (withdraw_obj);
|
||||||
|
GNUNET_free (wh->url);
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
|
||||||
|
GNUNET_free (wh);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
json_decref (withdraw_obj);
|
||||||
|
ctx = TEAH_handle_to_context (exchange);
|
||||||
|
wh->job = GNUNET_CURL_job_add2 (ctx,
|
||||||
|
eh,
|
||||||
|
wh->ctx.headers,
|
||||||
|
&handle_reserve_withdraw_finished,
|
||||||
|
wh);
|
||||||
|
return wh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Withdraw a coin from the exchange using a /reserve/withdraw request. Note
|
||||||
|
* that to ensure that no money is lost in case of hardware failures,
|
||||||
|
* the caller must have committed (most of) the arguments to disk
|
||||||
|
* before calling, and be ready to repeat the request with the same
|
||||||
|
* arguments in case of failures.
|
||||||
|
*
|
||||||
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
|
* @param pk kind of coin to create
|
||||||
|
* @param reserve_priv private key of the reserve to withdraw from
|
||||||
|
* @param ps secrets of the planchet
|
||||||
|
* caller must have committed this value to disk before the call (with @a pk)
|
||||||
|
* @param res_cb the callback to call when the final result for this request is available
|
||||||
|
* @param res_cb_cls closure for the above callback
|
||||||
|
* @return handle for the operation on success, NULL on error, i.e.
|
||||||
|
* if the inputs are invalid (i.e. denomination key not with this exchange).
|
||||||
|
* In this case, the callback is not called.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *
|
||||||
|
TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
const struct TALER_EXCHANGE_DenomPublicKey *pk,
|
||||||
|
const struct
|
||||||
|
TALER_ReservePrivateKeyP *reserve_priv,
|
||||||
|
const struct TALER_PlanchetSecretsP *ps,
|
||||||
|
TALER_EXCHANGE_WithdrawCallback
|
||||||
|
res_cb,
|
||||||
|
void *res_cb_cls)
|
||||||
|
{
|
||||||
|
struct TALER_Amount amount_with_fee;
|
||||||
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
|
struct TALER_WithdrawRequestPS req;
|
||||||
|
struct TALER_PlanchetDetail pd;
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *wh;
|
||||||
|
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
|
||||||
|
&req.reserve_pub.eddsa_pub);
|
||||||
|
req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
|
||||||
|
req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_amount_add (&amount_with_fee,
|
||||||
|
&pk->fee_withdraw,
|
||||||
|
&pk->value))
|
||||||
|
{
|
||||||
|
/* exchange gave us denomination keys that overflow like this!? */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
TALER_amount_hton (&req.amount_with_fee,
|
||||||
|
&amount_with_fee);
|
||||||
|
TALER_amount_hton (&req.withdraw_fee,
|
||||||
|
&pk->fee_withdraw);
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_planchet_prepare (&pk->key,
|
||||||
|
ps,
|
||||||
|
&pd))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
req.h_denomination_pub = pd.denom_pub_hash;
|
||||||
|
GNUNET_CRYPTO_hash (pd.coin_ev,
|
||||||
|
pd.coin_ev_size,
|
||||||
|
&req.h_coin_envelope);
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
|
||||||
|
&req.purpose,
|
||||||
|
&reserve_sig.eddsa_signature));
|
||||||
|
wh = reserve_withdraw_internal (exchange,
|
||||||
|
pk,
|
||||||
|
&reserve_sig,
|
||||||
|
&req.reserve_pub,
|
||||||
|
ps,
|
||||||
|
&pd,
|
||||||
|
res_cb,
|
||||||
|
res_cb_cls);
|
||||||
|
GNUNET_free (pd.coin_ev);
|
||||||
|
return wh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Withdraw a coin from the exchange using a /reserve/withdraw
|
||||||
|
* request. This API is typically used by a wallet to withdraw a tip
|
||||||
|
* where the reserve's signature was created by the merchant already.
|
||||||
|
*
|
||||||
|
* Note that to ensure that no money is lost in case of hardware
|
||||||
|
* failures, the caller must have committed (most of) the arguments to
|
||||||
|
* disk before calling, and be ready to repeat the request with the
|
||||||
|
* same arguments in case of failures.
|
||||||
|
*
|
||||||
|
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||||
|
* @param pk kind of coin to create
|
||||||
|
* @param reserve_sig signature from the reserve authorizing the withdrawal
|
||||||
|
* @param reserve_pub public key of the reserve to withdraw from
|
||||||
|
* @param ps secrets of the planchet
|
||||||
|
* caller must have committed this value to disk before the call (with @a pk)
|
||||||
|
* @param res_cb the callback to call when the final result for this request is available
|
||||||
|
* @param res_cb_cls closure for @a res_cb
|
||||||
|
* @return NULL
|
||||||
|
* if the inputs are invalid (i.e. denomination key not with this exchange).
|
||||||
|
* In this case, the callback is not called.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *
|
||||||
|
TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
|
const struct
|
||||||
|
TALER_EXCHANGE_DenomPublicKey *pk,
|
||||||
|
const struct
|
||||||
|
TALER_ReserveSignatureP *reserve_sig,
|
||||||
|
const struct
|
||||||
|
TALER_ReservePublicKeyP *reserve_pub,
|
||||||
|
const struct TALER_PlanchetSecretsP *ps,
|
||||||
|
TALER_EXCHANGE_WithdrawCallback
|
||||||
|
res_cb,
|
||||||
|
void *res_cb_cls)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGE_WithdrawHandle *wh;
|
||||||
|
struct TALER_PlanchetDetail pd;
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_planchet_prepare (&pk->key,
|
||||||
|
ps,
|
||||||
|
&pd))
|
||||||
|
{
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
wh = reserve_withdraw_internal (exchange,
|
||||||
|
pk,
|
||||||
|
reserve_sig,
|
||||||
|
reserve_pub,
|
||||||
|
ps,
|
||||||
|
&pd,
|
||||||
|
res_cb,
|
||||||
|
res_cb_cls);
|
||||||
|
GNUNET_free (pd.coin_ev);
|
||||||
|
return wh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a withdraw status request. This function cannot be used
|
||||||
|
* on a request handle if a response is already served for it.
|
||||||
|
*
|
||||||
|
* @param sign the withdraw sign request handle
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
|
||||||
|
{
|
||||||
|
if (NULL != wh->job)
|
||||||
|
{
|
||||||
|
GNUNET_CURL_job_cancel (wh->job);
|
||||||
|
wh->job = NULL;
|
||||||
|
}
|
||||||
|
GNUNET_free (wh->url);
|
||||||
|
TALER_curl_easy_post_finished (&wh->ctx);
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
|
||||||
|
GNUNET_free (wh);
|
||||||
|
}
|
@ -190,10 +190,10 @@ run (void *cls,
|
|||||||
/**
|
/**
|
||||||
* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x
|
* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x
|
||||||
* EUR:0.13) */
|
* EUR:0.13) */
|
||||||
TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
|
TALER_TESTING_cmd_melt_double ("refresh-melt-1",
|
||||||
"refresh-withdraw-coin-1",
|
"refresh-withdraw-coin-1",
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
NULL),
|
NULL),
|
||||||
/**
|
/**
|
||||||
* Complete (successful) melt operation, and withdraw the coins
|
* Complete (successful) melt operation, and withdraw the coins
|
||||||
*/
|
*/
|
||||||
|
@ -239,10 +239,10 @@ run (void *cls,
|
|||||||
/**
|
/**
|
||||||
* Melt the rest of the coin's value
|
* Melt the rest of the coin's value
|
||||||
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
|
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
|
||||||
TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
|
TALER_TESTING_cmd_melt_double ("refresh-melt-1",
|
||||||
"refresh-withdraw-coin-1",
|
"refresh-withdraw-coin-1",
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
NULL),
|
NULL),
|
||||||
/**
|
/**
|
||||||
* Complete (successful) melt operation, and
|
* Complete (successful) melt operation, and
|
||||||
* withdraw the coins
|
* withdraw the coins
|
||||||
@ -286,16 +286,16 @@ run (void *cls,
|
|||||||
MHD_HTTP_OK),
|
MHD_HTTP_OK),
|
||||||
/* Test running a failing melt operation (same operation
|
/* Test running a failing melt operation (same operation
|
||||||
* again must fail) */
|
* again must fail) */
|
||||||
TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing",
|
TALER_TESTING_cmd_melt ("refresh-melt-failing",
|
||||||
"refresh-withdraw-coin-1",
|
"refresh-withdraw-coin-1",
|
||||||
MHD_HTTP_CONFLICT,
|
MHD_HTTP_CONFLICT,
|
||||||
NULL),
|
NULL),
|
||||||
/* Test running a failing melt operation (on a coin that
|
/* Test running a failing melt operation (on a coin that
|
||||||
was itself revealed and subsequently deposited) */
|
was itself revealed and subsequently deposited) */
|
||||||
TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing-2",
|
TALER_TESTING_cmd_melt ("refresh-melt-failing-2",
|
||||||
"refresh-reveal-1",
|
"refresh-reveal-1",
|
||||||
MHD_HTTP_CONFLICT,
|
MHD_HTTP_CONFLICT,
|
||||||
NULL),
|
NULL),
|
||||||
|
|
||||||
TALER_TESTING_cmd_end ()
|
TALER_TESTING_cmd_end ()
|
||||||
};
|
};
|
||||||
|
@ -98,10 +98,10 @@ run (void *cls,
|
|||||||
/**
|
/**
|
||||||
* Melt SOME of the rest of the coin's value
|
* Melt SOME of the rest of the coin's value
|
||||||
* (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */
|
* (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */
|
||||||
TALER_TESTING_cmd_refresh_melt ("refresh-melt-1",
|
TALER_TESTING_cmd_melt ("refresh-melt-1",
|
||||||
"withdraw-revocation-coin-1",
|
"withdraw-revocation-coin-1",
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
NULL),
|
NULL),
|
||||||
/**
|
/**
|
||||||
* Complete (successful) melt operation, and withdraw the coins
|
* Complete (successful) melt operation, and withdraw the coins
|
||||||
*/
|
*/
|
||||||
@ -133,11 +133,11 @@ run (void *cls,
|
|||||||
/* Melt original coin AGAIN, but only create one 0.1 EUR coin;
|
/* Melt original coin AGAIN, but only create one 0.1 EUR coin;
|
||||||
This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
|
This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
|
||||||
leaving EUR:3.69. */
|
leaving EUR:3.69. */
|
||||||
TALER_TESTING_cmd_refresh_melt ("refresh-melt-2",
|
TALER_TESTING_cmd_melt ("refresh-melt-2",
|
||||||
"withdraw-revocation-coin-1",
|
"withdraw-revocation-coin-1",
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
"EUR:0.1",
|
"EUR:0.1",
|
||||||
NULL),
|
NULL),
|
||||||
/**
|
/**
|
||||||
* Complete (successful) melt operation, and withdraw the coins
|
* Complete (successful) melt operation, and withdraw the coins
|
||||||
*/
|
*/
|
||||||
|
@ -140,7 +140,7 @@ run (void *cls,
|
|||||||
/**
|
/**
|
||||||
* Melt the rest of the coin's value
|
* Melt the rest of the coin's value
|
||||||
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
|
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
|
||||||
TALER_TESTING_cmd_refresh_melt
|
TALER_TESTING_cmd_melt
|
||||||
("refresh-melt",
|
("refresh-melt",
|
||||||
"refresh-withdraw-coin",
|
"refresh-withdraw-coin",
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
|
@ -87,7 +87,7 @@ struct RefreshMeltState
|
|||||||
/**
|
/**
|
||||||
* Melt handle while operation is running.
|
* Melt handle while operation is running.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
|
struct TALER_EXCHANGE_MeltHandle *rmh;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interpreter state.
|
* Interpreter state.
|
||||||
@ -173,7 +173,7 @@ struct RefreshRevealState
|
|||||||
/**
|
/**
|
||||||
* Reveal handle while operation is running.
|
* Reveal handle while operation is running.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
|
struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience struct to keep in one place all the
|
* Convenience struct to keep in one place all the
|
||||||
@ -230,7 +230,7 @@ struct RefreshLinkState
|
|||||||
/**
|
/**
|
||||||
* Handle to the ongoing operation.
|
* Handle to the ongoing operation.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
|
struct TALER_EXCHANGE_LinkHandle *rlh;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interpreter state.
|
* Interpreter state.
|
||||||
@ -423,7 +423,7 @@ refresh_reveal_run (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rms = melt_cmd->cls;
|
rms = melt_cmd->cls;
|
||||||
rrs->rrh = TALER_EXCHANGE_refresh_reveal
|
rrs->rrh = TALER_EXCHANGE_refreshes_reveal
|
||||||
(is->exchange,
|
(is->exchange,
|
||||||
rms->refresh_data_length,
|
rms->refresh_data_length,
|
||||||
rms->refresh_data,
|
rms->refresh_data,
|
||||||
@ -459,7 +459,7 @@ refresh_reveal_cleanup (void *cls,
|
|||||||
rrs->is->ip,
|
rrs->is->ip,
|
||||||
cmd->label);
|
cmd->label);
|
||||||
|
|
||||||
TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
|
TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh);
|
||||||
rrs->rrh = NULL;
|
rrs->rrh = NULL;
|
||||||
}
|
}
|
||||||
if (NULL != rrs->retry_task)
|
if (NULL != rrs->retry_task)
|
||||||
@ -730,7 +730,7 @@ refresh_link_run (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* finally, use private key from withdraw sign command */
|
/* finally, use private key from withdraw sign command */
|
||||||
rls->rlh = TALER_EXCHANGE_refresh_link
|
rls->rlh = TALER_EXCHANGE_link
|
||||||
(is->exchange, coin_priv, &link_cb, rls);
|
(is->exchange, coin_priv, &link_cb, rls);
|
||||||
|
|
||||||
if (NULL == rls->rlh)
|
if (NULL == rls->rlh)
|
||||||
@ -762,7 +762,7 @@ refresh_link_cleanup (void *cls,
|
|||||||
"Command %u (%s) did not complete\n",
|
"Command %u (%s) did not complete\n",
|
||||||
rls->is->ip,
|
rls->is->ip,
|
||||||
cmd->label);
|
cmd->label);
|
||||||
TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
|
TALER_EXCHANGE_link_cancel (rls->rlh);
|
||||||
rls->rlh = NULL;
|
rls->rlh = NULL;
|
||||||
}
|
}
|
||||||
if (NULL != rls->retry_task)
|
if (NULL != rls->retry_task)
|
||||||
@ -782,13 +782,13 @@ refresh_link_cleanup (void *cls,
|
|||||||
* @param is the interpreter state.
|
* @param is the interpreter state.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
refresh_melt_run (void *cls,
|
melt_run (void *cls,
|
||||||
const struct TALER_TESTING_Command *cmd,
|
const struct TALER_TESTING_Command *cmd,
|
||||||
struct TALER_TESTING_Interpreter *is);
|
struct TALER_TESTING_Interpreter *is);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task scheduled to re-try #refresh_melt_run.
|
* Task scheduled to re-try #melt_run.
|
||||||
*
|
*
|
||||||
* @param cls a `struct RefreshMeltState`
|
* @param cls a `struct RefreshMeltState`
|
||||||
*/
|
*/
|
||||||
@ -798,9 +798,9 @@ do_melt_retry (void *cls)
|
|||||||
struct RefreshMeltState *rms = cls;
|
struct RefreshMeltState *rms = cls;
|
||||||
|
|
||||||
rms->retry_task = NULL;
|
rms->retry_task = NULL;
|
||||||
refresh_melt_run (rms,
|
melt_run (rms,
|
||||||
NULL,
|
NULL,
|
||||||
rms->is);
|
rms->is);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -870,7 +870,7 @@ melt_cb (void *cls,
|
|||||||
{
|
{
|
||||||
TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
|
TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
|
||||||
rms->is->commands[rms->is->ip].label);
|
rms->is->commands[rms->is->ip].label);
|
||||||
rms->rmh = TALER_EXCHANGE_refresh_melt
|
rms->rmh = TALER_EXCHANGE_melt
|
||||||
(rms->is->exchange, rms->refresh_data_length,
|
(rms->is->exchange, rms->refresh_data_length,
|
||||||
rms->refresh_data, &melt_cb, rms);
|
rms->refresh_data, &melt_cb, rms);
|
||||||
rms->double_melt = GNUNET_NO;
|
rms->double_melt = GNUNET_NO;
|
||||||
@ -888,9 +888,9 @@ melt_cb (void *cls,
|
|||||||
* @param is the interpreter state.
|
* @param is the interpreter state.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
refresh_melt_run (void *cls,
|
melt_run (void *cls,
|
||||||
const struct TALER_TESTING_Command *cmd,
|
const struct TALER_TESTING_Command *cmd,
|
||||||
struct TALER_TESTING_Interpreter *is)
|
struct TALER_TESTING_Interpreter *is)
|
||||||
{
|
{
|
||||||
struct RefreshMeltState *rms = cls;
|
struct RefreshMeltState *rms = cls;
|
||||||
unsigned int num_fresh_coins;
|
unsigned int num_fresh_coins;
|
||||||
@ -1006,11 +1006,11 @@ refresh_melt_run (void *cls,
|
|||||||
TALER_TESTING_interpreter_fail (rms->is);
|
TALER_TESTING_interpreter_fail (rms->is);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rms->rmh = TALER_EXCHANGE_refresh_melt (is->exchange,
|
rms->rmh = TALER_EXCHANGE_melt (is->exchange,
|
||||||
rms->refresh_data_length,
|
rms->refresh_data_length,
|
||||||
rms->refresh_data,
|
rms->refresh_data,
|
||||||
&melt_cb,
|
&melt_cb,
|
||||||
rms);
|
rms);
|
||||||
|
|
||||||
if (NULL == rms->rmh)
|
if (NULL == rms->rmh)
|
||||||
{
|
{
|
||||||
@ -1030,8 +1030,8 @@ refresh_melt_run (void *cls,
|
|||||||
* @param cmd the command which is being cleaned up.
|
* @param cmd the command which is being cleaned up.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
refresh_melt_cleanup (void *cls,
|
melt_cleanup (void *cls,
|
||||||
const struct TALER_TESTING_Command *cmd)
|
const struct TALER_TESTING_Command *cmd)
|
||||||
{
|
{
|
||||||
struct RefreshMeltState *rms = cls;
|
struct RefreshMeltState *rms = cls;
|
||||||
|
|
||||||
@ -1040,7 +1040,7 @@ refresh_melt_cleanup (void *cls,
|
|||||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||||
"Command %u (%s) did not complete\n",
|
"Command %u (%s) did not complete\n",
|
||||||
rms->is->ip, rms->is->commands[rms->is->ip].label);
|
rms->is->ip, rms->is->commands[rms->is->ip].label);
|
||||||
TALER_EXCHANGE_refresh_melt_cancel (rms->rmh);
|
TALER_EXCHANGE_melt_cancel (rms->rmh);
|
||||||
rms->rmh = NULL;
|
rms->rmh = NULL;
|
||||||
}
|
}
|
||||||
if (NULL != rms->retry_task)
|
if (NULL != rms->retry_task)
|
||||||
@ -1073,10 +1073,10 @@ refresh_melt_cleanup (void *cls,
|
|||||||
* @return #GNUNET_OK on success.
|
* @return #GNUNET_OK on success.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
refresh_melt_traits (void *cls,
|
melt_traits (void *cls,
|
||||||
const void **ret,
|
const void **ret,
|
||||||
const char *trait,
|
const char *trait,
|
||||||
unsigned int index)
|
unsigned int index)
|
||||||
{
|
{
|
||||||
struct RefreshMeltState *rms = cls;
|
struct RefreshMeltState *rms = cls;
|
||||||
|
|
||||||
@ -1161,10 +1161,10 @@ parse_amounts (struct RefreshMeltState *rms,
|
|||||||
* @return the command.
|
* @return the command.
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_refresh_melt (const char *label,
|
TALER_TESTING_cmd_melt (const char *label,
|
||||||
const char *coin_reference,
|
const char *coin_reference,
|
||||||
unsigned int expected_response_code,
|
unsigned int expected_response_code,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
struct RefreshMeltState *rms;
|
struct RefreshMeltState *rms;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@ -1180,9 +1180,9 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
|
|||||||
struct TALER_TESTING_Command cmd = {
|
struct TALER_TESTING_Command cmd = {
|
||||||
.label = label,
|
.label = label,
|
||||||
.cls = rms,
|
.cls = rms,
|
||||||
.run = &refresh_melt_run,
|
.run = &melt_run,
|
||||||
.cleanup = &refresh_melt_cleanup,
|
.cleanup = &melt_cleanup,
|
||||||
.traits = &refresh_melt_traits
|
.traits = &melt_traits
|
||||||
};
|
};
|
||||||
|
|
||||||
return cmd;
|
return cmd;
|
||||||
@ -1203,10 +1203,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
|
|||||||
* @return the command.
|
* @return the command.
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_refresh_melt_double (const char *label,
|
TALER_TESTING_cmd_melt_double (const char *label,
|
||||||
const char *coin_reference,
|
const char *coin_reference,
|
||||||
unsigned int expected_response_code,
|
unsigned int expected_response_code,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
struct RefreshMeltState *rms;
|
struct RefreshMeltState *rms;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@ -1223,9 +1223,9 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
|
|||||||
struct TALER_TESTING_Command cmd = {
|
struct TALER_TESTING_Command cmd = {
|
||||||
.label = label,
|
.label = label,
|
||||||
.cls = rms,
|
.cls = rms,
|
||||||
.run = &refresh_melt_run,
|
.run = &melt_run,
|
||||||
.cleanup = &refresh_melt_cleanup,
|
.cleanup = &melt_cleanup,
|
||||||
.traits = &refresh_melt_traits
|
.traits = &melt_traits
|
||||||
};
|
};
|
||||||
|
|
||||||
return cmd;
|
return cmd;
|
||||||
@ -1240,11 +1240,11 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
|
|||||||
* @return modified command.
|
* @return modified command.
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd)
|
TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
|
||||||
{
|
{
|
||||||
struct RefreshMeltState *rms;
|
struct RefreshMeltState *rms;
|
||||||
|
|
||||||
GNUNET_assert (&refresh_melt_run == cmd.run);
|
GNUNET_assert (&melt_run == cmd.run);
|
||||||
rms = cmd.cls;
|
rms = cmd.cls;
|
||||||
rms->do_retry = GNUNET_YES;
|
rms->do_retry = GNUNET_YES;
|
||||||
return cmd;
|
return cmd;
|
||||||
|
Loading…
Reference in New Issue
Block a user