big rename fest related to #6067 API renaming

This commit is contained in:
Christian Grothoff 2020-02-29 16:42:10 +01:00
parent de9ab28ab9
commit 0a2b049864
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
32 changed files with 3632 additions and 3451 deletions

View File

@ -3763,7 +3763,7 @@ refresh_session_cb (void *cls,
NULL);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
report_row_inconsistency ("refresh_melt",
report_row_inconsistency ("melt",
rowid,
"denomination key not found");
return GNUNET_OK;

View File

@ -374,8 +374,8 @@ run (void *cls,
i,
j);
unit[2] =
TALER_TESTING_cmd_refresh_melt_with_retry
(TALER_TESTING_cmd_refresh_melt
TALER_TESTING_cmd_melt_with_retry
(TALER_TESTING_cmd_melt
(add_label (melt_label),
withdraw_label,
MHD_HTTP_OK,

View File

@ -51,11 +51,11 @@ taler_exchange_httpd_SOURCES = \
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_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_recoup.c taler-exchange-httpd_recoup.h \
taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.h \
taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \
taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \
taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \

View File

@ -33,9 +33,9 @@
#include "taler-exchange-httpd_reserves_get.h"
#include "taler-exchange-httpd_withdraw.h"
#include "taler-exchange-httpd_recoup.h"
#include "taler-exchange-httpd_refresh_link.h"
#include "taler-exchange-httpd_refresh_melt.h"
#include "taler-exchange-httpd_refresh_reveal.h"
#include "taler-exchange-httpd_link.h"
#include "taler-exchange-httpd_melt.h"
#include "taler-exchange-httpd_refreshes_reveal.h"
#include "taler-exchange-httpd_terms.h"
#include "taler-exchange-httpd_transfers_get.h"
#include "taler-exchange-httpd_deposits_get.h"

View File

@ -26,7 +26,7 @@
#include <microhttpd.h>
#include "taler_mhd_lib.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_keystate.h"

View File

@ -14,7 +14,7 @@
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
* @author Florian Dold
* @author Benedikt Mueller
@ -27,7 +27,7 @@
#include "taler_json_lib.h"
#include "taler_mhd_lib.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_keystate.h"
@ -48,14 +48,14 @@
* @return a MHD result code
*/
static int
reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
const struct
TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
struct TALER_EXCHANGEDB_TransactionList *
tl,
const struct TALER_Amount *requested,
const struct TALER_Amount *residual)
reply_melt_insufficient_funds (struct MHD_Connection *connection,
const struct
TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
struct TALER_EXCHANGEDB_TransactionList *
tl,
const struct TALER_Amount *requested,
const struct TALER_Amount *residual)
{
json_t *history;
@ -96,9 +96,9 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
* @return a MHD status code
*/
static int
reply_refresh_melt_success (struct MHD_Connection *connection,
const struct TALER_RefreshCommitmentP *rc,
uint32_t noreveal_index)
reply_melt_success (struct MHD_Connection *connection,
const struct TALER_RefreshCommitmentP *rc,
uint32_t noreveal_index)
{
struct TALER_RefreshMeltConfirmationPS body;
struct TALER_ExchangePublicKeyP pub;
@ -139,7 +139,7 @@ struct RefreshMeltContext
/**
* noreveal_index is only initialized during
* #refresh_melt_transaction().
* #melt_transaction().
*/
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
@ -253,14 +253,14 @@ refresh_check_melt (struct MHD_Connection *connection,
TALER_amount_subtract (&coin_residual,
&spent,
&rmc->refresh_session.amount_with_fee));
*mhd_ret = reply_refresh_melt_insufficient_funds (connection,
&rmc->refresh_session.coin
.coin_pub,
&rmc->coin_value,
tl,
&rmc->refresh_session.
amount_with_fee,
&coin_residual);
*mhd_ret = reply_melt_insufficient_funds (connection,
&rmc->refresh_session.coin
.coin_pub,
&rmc->coin_value,
tl,
&rmc->refresh_session.
amount_with_fee,
&coin_residual);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return GNUNET_DB_STATUS_HARD_ERROR;
@ -294,10 +294,10 @@ refresh_check_melt (struct MHD_Connection *connection,
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
refresh_melt_transaction (void *cls,
struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
melt_transaction (void *cls,
struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{
struct RefreshMeltContext *rmc = cls;
enum GNUNET_DB_QueryStatus qs;
@ -311,9 +311,9 @@ refresh_melt_transaction (void *cls,
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
TALER_LOG_DEBUG ("Found already-melted coin\n");
*mhd_ret = reply_refresh_melt_success (connection,
&rmc->refresh_session.rc,
noreveal_index);
*mhd_ret = reply_melt_success (connection,
&rmc->refresh_session.rc,
noreveal_index);
/* Note: we return "hard error" to ensure the wrapper
does not retry the transaction, and to also not generate
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
* session signature and then hand things of to execute the melt
* 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[in,out] rmc details about the melt request
* @return MHD result code
*/
static int
handle_refresh_melt (struct MHD_Connection *connection,
struct RefreshMeltContext *rmc)
handle_melt (struct MHD_Connection *connection,
struct RefreshMeltContext *rmc)
{
/* verify signature of coin for melt operation */
@ -415,15 +415,15 @@ handle_refresh_melt (struct MHD_Connection *connection,
TEH_DB_run_transaction (connection,
"run melt",
&mhd_ret,
&refresh_melt_transaction,
&melt_transaction,
rmc))
return mhd_ret;
}
/* generate ordinary response */
return reply_refresh_melt_success (connection,
&rmc->refresh_session.rc,
rmc->refresh_session.noreveal_index);
return reply_melt_success (connection,
&rmc->refresh_session.rc,
rmc->refresh_session.noreveal_index);
}
@ -571,8 +571,8 @@ check_for_denomination_key (struct MHD_Connection *connection,
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
"melt amount smaller than melting fee");
}
return handle_refresh_melt (connection,
rmc);
return handle_melt (connection,
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
* components and then hands things of to #check_for_denomination_key() to
* validate the melted coins, the signature and execute the melt using
* handle_refresh_melt().
* handle_melt().
* @param connection the MHD connection to handle
* @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 */

View File

@ -14,7 +14,7 @@
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
* @author Florian Dold
* @author Benedikt Mueller
@ -32,7 +32,7 @@
* 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
* validate the melted coins, the signature and execute the melt using
* handle_refresh_melt().
* handle_melt().
* @param connection the MHD connection to handle
* @param coin_pub public key of the coin

View File

@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-exchange-httpd_refresh_reveal.c
* @brief Handle /refresh/reveal requests
* @file taler-exchange-httpd_refreshes_reveal.c
* @brief Handle /refreshes/$RCH/reveal requests
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
@ -26,7 +26,7 @@
#include <microhttpd.h>
#include "taler_mhd_lib.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_keystate.h"
@ -299,7 +299,7 @@ refresh_reveal_transaction (void *cls,
int *mhd_ret)
{
struct RevealContext *rctx = cls;
struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
struct TALER_EXCHANGEDB_RefreshMelt melt;
enum GNUNET_DB_QueryStatus qs;
/* 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,
session,
&rctx->rc,
&refresh_melt);
&melt);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
*mhd_ret = TALER_MHD_reply_with_error (connection,
@ -319,7 +319,7 @@ refresh_reveal_transaction (void *cls,
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return 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);
*mhd_ret = TALER_MHD_reply_with_error (connection,
@ -331,7 +331,7 @@ refresh_reveal_transaction (void *cls,
/* 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
this function -- in contrast to the other offsets! */
struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
@ -343,7 +343,7 @@ refresh_reveal_transaction (void *cls,
{
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 */
rce->transfer_pub = rctx->gamma_tp;
@ -360,7 +360,7 @@ refresh_reveal_transaction (void *cls,
GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
&rce->transfer_pub.ecdhe_pub);
TALER_link_reveal_transfer_secret (tpriv,
&refresh_melt.session.coin.coin_pub,
&melt.session.coin.coin_pub,
&ts);
rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
struct TALER_RefreshCoinData);
@ -387,15 +387,15 @@ refresh_reveal_transaction (void *cls,
TALER_CNC_KAPPA,
rctx->num_fresh_coins,
rcs,
&refresh_melt.session.coin.coin_pub,
&refresh_melt.session.amount_with_fee);
&melt.session.coin.coin_pub,
&melt.session.amount_with_fee);
/* Free resources allocated above */
for (unsigned int i = 0; i<TALER_CNC_KAPPA; 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... */
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;
refresh_cost = refresh_melt.melt_fee;
refresh_cost = melt.melt_fee;
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
{
struct TALER_Amount fee_withdraw;
@ -450,7 +450,7 @@ refresh_reveal_transaction (void *cls,
}
}
if (0 < TALER_amount_cmp (&refresh_cost,
&refresh_melt.session.amount_with_fee))
&melt.session.amount_with_fee))
{
GNUNET_break_op (0);
*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 TALER_RefreshCoinData rcds[num_fresh_coins];
struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
struct TALER_EXCHANGEDB_RefreshMelt melt;
int res;
/* 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,
NULL,
&rctx->rc,
&refresh_melt)))
&melt)))
{
switch (qs)
{
@ -663,7 +663,7 @@ resolve_refresh_reveal_denominations (struct TEH_KS_StateHandle *key_state,
ldp.purpose.size = htonl (sizeof (ldp));
ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
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;
GNUNET_CRYPTO_hash (rcds[i].coin_ev,
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,
&ldp.purpose,
&link_sigs[i].eddsa_signature,
&refresh_melt.session.coin.coin_pub.
&melt.session.coin.coin_pub.
eddsa_pub))
{
GNUNET_break_op (0);

View File

@ -3389,8 +3389,8 @@ postgres_select_refunds_by_coin (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database handle to use, NULL if not run in any transaction
* @param rc commitment hash to use to locate the operation
* @param[out] refresh_melt where to store the result; note that
* refresh_melt->session.coin.denom_sig will be set to NULL
* @param[out] melt where to store the result; note that
* 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)
* @return transaction status
*/
@ -3398,7 +3398,7 @@ static enum GNUNET_DB_QueryStatus
postgres_get_melt (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_RefreshCommitmentP *rc,
struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
struct TALER_EXCHANGEDB_RefreshMelt *melt)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@ -3407,30 +3407,30 @@ postgres_get_melt (void *cls,
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
&refresh_melt->session.coin.
&melt->session.coin.
denom_pub_hash),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
&refresh_melt->melt_fee),
&melt->melt_fee),
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",
&refresh_melt->session.coin.coin_pub),
&melt->session.coin.coin_pub),
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",
&refresh_melt->session.amount_with_fee),
&melt->session.amount_with_fee),
GNUNET_PQ_result_spec_end
};
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)
session = postgres_get_session (pg);
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_melt",
params,
rs);
refresh_melt->session.rc = *rc;
melt->session.rc = *rc;
return qs;
}

View File

@ -697,7 +697,7 @@ void
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);
/* ********************* /refund *********************** */
/* ********************* /coins/$COIN_PUB/refund *********************** */
/**
* @brief A Refund Handle
@ -926,7 +926,7 @@ void
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
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;
@ -1175,13 +1175,12 @@ typedef void
unsigned int http_status,
enum TALER_ErrorCode ec,
const struct
TALER_DenominationSignature *
sig,
TALER_DenominationSignature *sig,
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
* 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
* 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
* 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
TALER_EXCHANGE_withdraw_cancel (struct
TALER_EXCHANGE_WithdrawHandle *
sign);
TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh);
/* ********************* /refresh/melt+reveal ***************************** */
@ -1273,7 +1270,7 @@ TALER_EXCHANGE_withdraw_cancel (struct
* no money is lost in case of hardware failures, is 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_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
* consistent. However, the @a melts_sigs are NOT verified.
@ -1298,7 +1295,7 @@ TALER_EXCHANGE_withdraw_cancel (struct
* @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_refresh_melt().
* before proceeding to #TALER_EXCHANGE_melt().
* Non-null results should be freed using GNUNET_free().
*/
char *
@ -1315,18 +1312,19 @@ TALER_EXCHANGE_refresh_prepare (const struct
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
* result of the /refresh/melt stage. If successful, the @a noreveal_index
* should be committed to disk prior to proceeding #TALER_EXCHANGE_refresh_reveal().
* Callbacks of this type are used to notify the application about the result
* of the /coins/$COIN_PUB/melt stage. If successful, the @a noreveal_index
* should be committed to disk prior to proceeding
* #TALER_EXCHANGE_refreshes_reveal().
*
* @param cls closure
* @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)
*/
typedef void
(*TALER_EXCHANGE_RefreshMeltCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint32_t noreveal_index,
const struct
TALER_ExchangePublicKeyP *sign_key,
const json_t *full_response);
(*TALER_EXCHANGE_MeltCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint32_t noreveal_index,
const struct TALER_ExchangePublicKeyP *sign_key,
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.
*
* 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.
* In this case, neither callback will be called.
*/
struct TALER_EXCHANGE_RefreshMeltHandle *
TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange,
size_t refresh_data_length,
const char *refresh_data,
TALER_EXCHANGE_RefreshMeltCallback melt_cb,
void *melt_cb_cls);
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);
/**
* 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.
*
* @param rmh the refresh handle
* @param mh the melt handle
*/
void
TALER_EXCHANGE_refresh_melt_cancel (struct
TALER_EXCHANGE_RefreshMeltHandle *rmh);
TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh);
/* ********************* /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)
*/
typedef void
(*TALER_EXCHANGE_RefreshRevealCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
unsigned int num_coins,
const struct
TALER_PlanchetSecretsP *coin_privs,
const struct
TALER_DenominationSignature *sigs,
const json_t *full_response);
(*TALER_EXCHANGE_RefreshesRevealCallback)(void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
unsigned int num_coins,
const struct
TALER_PlanchetSecretsP *coin_privs,
const struct
TALER_DenominationSignature *sigs,
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.
*
* 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
#TALER_EXCHANGE_refresh_prepare())
* @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
* 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_RefreshRevealHandle *
TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
size_t refresh_data_length,
const char *refresh_data,
uint32_t noreveal_index,
TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
void *reveal_cb_cls);
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);
/**
@ -1462,24 +1459,25 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
* @param rrh the refresh reval handle
*/
void
TALER_EXCHANGE_refresh_reveal_cancel (struct
TALER_EXCHANGE_RefreshRevealHandle *rrh);
TALER_EXCHANGE_refreshes_reveal_cancel (struct
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
* submitting a /refresh/link request to a exchange. If the operation was
* successful, this function returns the signatures over the coins
* that were created when the original coin was melted.
* Callbacks of this type are used to return the final result of submitting a
* /coins/$COIN_PUB/link request to a exchange. If the operation was
* successful, this function returns the signatures over the coins that were
* created when the original coin was melted.
*
* @param cls closure
* @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)
*/
typedef void
(*TALER_EXCHANGE_RefreshLinkCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
unsigned int num_coins,
const struct
TALER_CoinSpendPrivateKeyP *coin_privs,
const struct
TALER_DenominationSignature *sigs,
const struct
TALER_DenominationPublicKey *pubs,
const json_t *full_response);
(*TALER_EXCHANGE_LinkCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
unsigned int num_coins,
const struct
TALER_CoinSpendPrivateKeyP *coin_privs,
const struct
TALER_DenominationSignature *sigs,
const struct
TALER_DenominationPublicKey *pubs,
const json_t *full_response);
/**
* Submit a refresh 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
* against those trying to receive a funds transfer by abusing the
* /refresh protocol.
* This API is typically not used by anyone, it is more a threat against those
* trying to receive a funds transfer by abusing the refresh protocol.
*
* @param exchange the exchange handle; the exchange must be ready to operate
* @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
* @return a handle for this request
*/
struct TALER_EXCHANGE_RefreshLinkHandle *
TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
TALER_EXCHANGE_RefreshLinkCallback link_cb,
void *link_cb_cls);
struct TALER_EXCHANGE_LinkHandle *
TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
TALER_EXCHANGE_LinkCallback link_cb,
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.
*
* @param rlh the refresh link handle
* @param lh the link handle
*/
void
TALER_EXCHANGE_refresh_link_cancel (struct
TALER_EXCHANGE_RefreshLinkHandle *rlh);
TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh);
/* ********************* /transfers/$WTID *********************** */
@ -1709,6 +1704,46 @@ TALER_EXCHANGE_verify_coin_history (const struct
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 *********************** */

View File

@ -2087,8 +2087,8 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database handle to use
* @param rc commitment to use for the lookup
* @param[out] refresh_melt where to store the result; note that
* refresh_melt->session.coin.denom_sig will be set to NULL
* @param[out] melt where to store the result; note that
* 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)
* @return transaction status
*/
@ -2096,7 +2096,7 @@ struct TALER_EXCHANGEDB_Plugin
(*get_melt)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_RefreshCommitmentP *rc,
struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt);
struct TALER_EXCHANGEDB_RefreshMelt *melt);
/**

View File

@ -1307,10 +1307,10 @@ TALER_TESTING_cmd_deposit_with_retry (struct TALER_TESTING_Command cmd);
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_melt (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...);
TALER_TESTING_cmd_melt (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...);
/**
@ -1326,10 +1326,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_melt_double (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...);
TALER_TESTING_cmd_melt_double (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...);
/**
@ -1339,7 +1339,7 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
* @return modified 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);
/**

View File

@ -25,13 +25,16 @@ libtalerexchange_la_SOURCES = \
exchange_api_common.c \
exchange_api_handle.c exchange_api_handle.h \
exchange_api_deposit.c \
exchange_api_deposits_get.c \
exchange_api_link.c \
exchange_api_melt.c \
exchange_api_recoup.c \
exchange_api_refresh.c \
exchange_api_refresh_link.c \
exchange_api_refresh_common.c exchange_api_refresh_common.h \
exchange_api_refreshes_reveal.c \
exchange_api_refund.c \
exchange_api_reserve.c \
exchange_api_track_transaction.c \
exchange_api_track_transfer.c \
exchange_api_reserves_get.c \
exchange_api_transfers_get.c \
exchange_api_withdraw.c \
exchange_api_wire.c
libtalerexchange_la_LIBADD = \
libtalerauditor.la \

View File

@ -26,6 +26,422 @@
#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",
&timestamp),
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.
*

View File

@ -15,8 +15,8 @@
<http://www.gnu.org/licenses/>
*/
/**
* @file lib/exchange_api_track_transaction.c
* @brief Implementation of the /track/transaction request of the exchange's HTTP API
* @file lib/exchange_api_deposits_get.c
* @brief Implementation of the /deposits/ GET request
* @author Christian Grothoff
*/
#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.
*
* @param dwh the wire deposits request handle
*/
void
TALER_EXCHANGE_deposits_get_cancel (struct
TALER_EXCHANGE_DepositGetHandle *
dwh)
TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh)
{
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 */

View File

@ -15,8 +15,8 @@
<http://www.gnu.org/licenses/>
*/
/**
* @file lib/exchange_api_refresh_link.c
* @brief Implementation of the /refresh/link request of the exchange's HTTP API
* @file lib/exchange_api_link.c
* @brief Implementation of the /coins/$COIN_PUB/link request
* @author Christian Grothoff
*/
#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.
*/
TALER_EXCHANGE_RefreshLinkCallback link_cb;
TALER_EXCHANGE_LinkCallback link_cb;
/**
* Closure for @e cb.
@ -73,7 +73,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
* Parse the provided linkage data from the "200 OK" response
* 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 coin_num number of the coin to decode
* @param trans_pub our transfer public key
@ -83,13 +83,13 @@ struct TALER_EXCHANGE_RefreshLinkHandle
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static int
parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
const json_t *json,
unsigned int coin_num,
const struct TALER_TransferPublicKeyP *trans_pub,
struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_DenominationSignature *sig,
struct TALER_DenominationPublicKey *pub)
parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
const json_t *json,
unsigned int coin_num,
const struct TALER_TransferPublicKeyP *trans_pub,
struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_DenominationSignature *sig,
struct TALER_DenominationPublicKey *pub)
{
struct GNUNET_CRYPTO_RsaSignature *bsig;
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,
&rlh->coin_priv,
&lh->coin_priv,
&secret);
TALER_planchet_setup_refresh (&secret,
coin_num,
@ -133,7 +133,7 @@ parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
ldp.purpose.size = htonl (sizeof (ldp));
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.transfer_pub = *trans_pub;
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
* 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
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static int
parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
const json_t *json)
parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
const json_t *json)
{
unsigned int session;
unsigned int num_coins;
@ -277,14 +277,14 @@ parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
{
GNUNET_assert (i + off_coin < num_coins);
if (GNUNET_OK !=
parse_refresh_link_coin (rlh,
json_array_get (jsona,
i),
i,
&trans_pub,
&coin_privs[i + off_coin],
&sigs[i + off_coin],
&pubs[i + off_coin]))
parse_link_coin (lh,
json_array_get (jsona,
i),
i,
&trans_pub,
&coin_privs[i + off_coin],
&sigs[i + off_coin],
&pubs[i + off_coin]))
{
GNUNET_break_op (0);
break;
@ -304,15 +304,15 @@ parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
if (off_coin == num_coins)
{
rlh->link_cb (rlh->link_cb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
num_coins,
coin_privs,
sigs,
pubs,
json);
rlh->link_cb = NULL;
lh->link_cb (lh->link_cb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
num_coins,
coin_privs,
sigs,
pubs,
json);
lh->link_cb = NULL;
ret = GNUNET_OK;
}
else
@ -337,29 +337,29 @@ parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
/**
* 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 parsed JSON result, NULL on error
*/
static void
handle_refresh_link_finished (void *cls,
long response_code,
const void *response)
handle_link_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_EXCHANGE_RefreshLinkHandle *rlh = cls;
struct TALER_EXCHANGE_LinkHandle *lh = cls;
const json_t *j = response;
rlh->job = NULL;
lh->job = NULL;
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
parse_refresh_link_ok (rlh,
j))
parse_link_ok (lh,
j))
{
GNUNET_break_op (0);
response_code = 0;
@ -386,25 +386,24 @@ handle_refresh_link_finished (void *cls,
response_code = 0;
break;
}
if (NULL != rlh->link_cb)
rlh->link_cb (rlh->link_cb_cls,
response_code,
TALER_JSON_get_error_code (j),
0,
NULL,
NULL,
NULL,
j);
TALER_EXCHANGE_refresh_link_cancel (rlh);
if (NULL != lh->link_cb)
lh->link_cb (lh->link_cb_cls,
response_code,
TALER_JSON_get_error_code (j),
0,
NULL,
NULL,
NULL,
j);
TALER_EXCHANGE_link_cancel (lh);
}
/**
* 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
* against those trying to receive a funds transfer by abusing the
* /refresh protocol.
* This API is typically not used by anyone, it is more a threat against those
* trying to receive a funds transfer by abusing the refresh protocol.
*
* @param exchange the exchange handle; the exchange must be ready to operate
* @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
* @return a handle for this request
*/
struct TALER_EXCHANGE_RefreshLinkHandle *
TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
TALER_EXCHANGE_RefreshLinkCallback link_cb,
void *link_cb_cls)
struct TALER_EXCHANGE_LinkHandle *
TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
TALER_EXCHANGE_LinkCallback link_cb,
void *link_cb_cls)
{
struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
struct TALER_EXCHANGE_LinkHandle *lh;
CURL *eh;
struct GNUNET_CURL_Context *ctx;
struct TALER_CoinSpendPublicKeyP coin_pub;
@ -449,42 +448,41 @@ TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
"/coins/%s/link",
pub_str);
}
rlh = GNUNET_new (struct TALER_EXCHANGE_RefreshLinkHandle);
rlh->exchange = exchange;
rlh->link_cb = link_cb;
rlh->link_cb_cls = link_cb_cls;
rlh->coin_priv = *coin_priv;
rlh->url = TEAH_path_to_url (exchange,
arg_str);
eh = TEL_curl_easy_get (rlh->url);
lh = GNUNET_new (struct TALER_EXCHANGE_LinkHandle);
lh->exchange = exchange;
lh->link_cb = link_cb;
lh->link_cb_cls = link_cb_cls;
lh->coin_priv = *coin_priv;
lh->url = TEAH_path_to_url (exchange,
arg_str);
eh = TEL_curl_easy_get (lh->url);
ctx = TEAH_handle_to_context (exchange);
rlh->job = GNUNET_CURL_job_add (ctx,
eh,
GNUNET_YES,
&handle_refresh_link_finished,
rlh);
return rlh;
lh->job = GNUNET_CURL_job_add (ctx,
eh,
GNUNET_YES,
&handle_link_finished,
lh);
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.
*
* @param rlh the refresh link handle
* @param lh the link handle
*/
void
TALER_EXCHANGE_refresh_link_cancel (struct
TALER_EXCHANGE_RefreshLinkHandle *rlh)
TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh)
{
if (NULL != rlh->job)
if (NULL != lh->job)
{
GNUNET_CURL_job_cancel (rlh->job);
rlh->job = NULL;
GNUNET_CURL_job_cancel (lh->job);
lh->job = NULL;
}
GNUNET_free (rlh->url);
GNUNET_free (rlh);
GNUNET_free (lh->url);
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
View 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

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

View 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

View 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

View 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 */

View File

@ -1,6 +1,6 @@
/*
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
terms of the GNU General Public License as published by the Free Software
@ -15,8 +15,8 @@
<http://www.gnu.org/licenses/>
*/
/**
* @file lib/exchange_api_track_transfer.c
* @brief Implementation of the /track/transfer request of the exchange's HTTP API
* @file lib/exchange_api_transfers_get.c
* @brief Implementation of the GET /transfers/ request
* @author Christian Grothoff
*/
#include "platform.h"
@ -32,7 +32,7 @@
/**
* @brief A /track/transfer Handle
* @brief A /transfers/ GET Handle
*/
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
* callback. If not, return an error code.
*
* 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.
*
* @param wdh handle to the operation
@ -80,9 +80,9 @@ struct TALER_EXCHANGE_TransfersGetHandle
* #GNUNET_SYSERR if the response was bogus
*/
static int
check_track_transfer_response_ok (struct
TALER_EXCHANGE_TransfersGetHandle *wdh,
const json_t *json)
check_transfers_get_response_ok (struct
TALER_EXCHANGE_TransfersGetHandle *wdh,
const json_t *json)
{
json_t *details_j;
struct GNUNET_HashCode h_wire;
@ -248,16 +248,16 @@ check_track_transfer_response_ok (struct
/**
* Function called when we're done processing the
* HTTP /track/transfer request.
* HTTP /transfers/ request.
*
* @param cls the `struct TALER_EXCHANGE_TransfersGetHandle`
* @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error
*/
static void
handle_track_transfer_finished (void *cls,
long response_code,
const void *response)
handle_transfers_get_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
const json_t *j = response;
@ -269,8 +269,8 @@ handle_track_transfer_finished (void *cls,
break;
case MHD_HTTP_OK:
if (GNUNET_OK ==
check_track_transfer_response_ok (wdh,
j))
check_transfers_get_response_ok (wdh,
j))
return;
GNUNET_break_op (0);
response_code = 0;
@ -371,7 +371,7 @@ TALER_EXCHANGE_transfers_get (struct TALER_EXCHANGE_Handle *exchange,
wdh->job = GNUNET_CURL_job_add (ctx,
eh,
GNUNET_YES,
&handle_track_transfer_finished,
&handle_transfers_get_finished,
wdh);
return wdh;
}

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

View File

@ -190,10 +190,10 @@ run (void *cls,
/**
* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x
* EUR:0.13) */
TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
"refresh-withdraw-coin-1",
MHD_HTTP_OK,
NULL),
TALER_TESTING_cmd_melt_double ("refresh-melt-1",
"refresh-withdraw-coin-1",
MHD_HTTP_OK,
NULL),
/**
* Complete (successful) melt operation, and withdraw the coins
*/

View File

@ -239,10 +239,10 @@ run (void *cls,
/**
* Melt the rest of the coin's value
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
"refresh-withdraw-coin-1",
MHD_HTTP_OK,
NULL),
TALER_TESTING_cmd_melt_double ("refresh-melt-1",
"refresh-withdraw-coin-1",
MHD_HTTP_OK,
NULL),
/**
* Complete (successful) melt operation, and
* withdraw the coins
@ -286,16 +286,16 @@ run (void *cls,
MHD_HTTP_OK),
/* Test running a failing melt operation (same operation
* again must fail) */
TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing",
"refresh-withdraw-coin-1",
MHD_HTTP_CONFLICT,
NULL),
TALER_TESTING_cmd_melt ("refresh-melt-failing",
"refresh-withdraw-coin-1",
MHD_HTTP_CONFLICT,
NULL),
/* Test running a failing melt operation (on a coin that
was itself revealed and subsequently deposited) */
TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing-2",
"refresh-reveal-1",
MHD_HTTP_CONFLICT,
NULL),
TALER_TESTING_cmd_melt ("refresh-melt-failing-2",
"refresh-reveal-1",
MHD_HTTP_CONFLICT,
NULL),
TALER_TESTING_cmd_end ()
};

View File

@ -98,10 +98,10 @@ run (void *cls,
/**
* Melt SOME of the rest of the coin's value
* (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt ("refresh-melt-1",
"withdraw-revocation-coin-1",
MHD_HTTP_OK,
NULL),
TALER_TESTING_cmd_melt ("refresh-melt-1",
"withdraw-revocation-coin-1",
MHD_HTTP_OK,
NULL),
/**
* 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;
This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
leaving EUR:3.69. */
TALER_TESTING_cmd_refresh_melt ("refresh-melt-2",
"withdraw-revocation-coin-1",
MHD_HTTP_OK,
"EUR:0.1",
NULL),
TALER_TESTING_cmd_melt ("refresh-melt-2",
"withdraw-revocation-coin-1",
MHD_HTTP_OK,
"EUR:0.1",
NULL),
/**
* Complete (successful) melt operation, and withdraw the coins
*/

View File

@ -140,7 +140,7 @@ run (void *cls,
/**
* Melt the rest of the coin's value
* (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
TALER_TESTING_cmd_refresh_melt
TALER_TESTING_cmd_melt
("refresh-melt",
"refresh-withdraw-coin",
MHD_HTTP_OK,

View File

@ -87,7 +87,7 @@ struct RefreshMeltState
/**
* Melt handle while operation is running.
*/
struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
struct TALER_EXCHANGE_MeltHandle *rmh;
/**
* Interpreter state.
@ -173,7 +173,7 @@ struct RefreshRevealState
/**
* 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
@ -230,7 +230,7 @@ struct RefreshLinkState
/**
* Handle to the ongoing operation.
*/
struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
struct TALER_EXCHANGE_LinkHandle *rlh;
/**
* Interpreter state.
@ -423,7 +423,7 @@ refresh_reveal_run (void *cls,
return;
}
rms = melt_cmd->cls;
rrs->rrh = TALER_EXCHANGE_refresh_reveal
rrs->rrh = TALER_EXCHANGE_refreshes_reveal
(is->exchange,
rms->refresh_data_length,
rms->refresh_data,
@ -459,7 +459,7 @@ refresh_reveal_cleanup (void *cls,
rrs->is->ip,
cmd->label);
TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh);
rrs->rrh = NULL;
}
if (NULL != rrs->retry_task)
@ -730,7 +730,7 @@ refresh_link_run (void *cls,
}
/* 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);
if (NULL == rls->rlh)
@ -762,7 +762,7 @@ refresh_link_cleanup (void *cls,
"Command %u (%s) did not complete\n",
rls->is->ip,
cmd->label);
TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
TALER_EXCHANGE_link_cancel (rls->rlh);
rls->rlh = NULL;
}
if (NULL != rls->retry_task)
@ -782,13 +782,13 @@ refresh_link_cleanup (void *cls,
* @param is the interpreter state.
*/
static void
refresh_melt_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is);
melt_run (void *cls,
const struct TALER_TESTING_Command *cmd,
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`
*/
@ -798,9 +798,9 @@ do_melt_retry (void *cls)
struct RefreshMeltState *rms = cls;
rms->retry_task = NULL;
refresh_melt_run (rms,
NULL,
rms->is);
melt_run (rms,
NULL,
rms->is);
}
@ -870,7 +870,7 @@ melt_cb (void *cls,
{
TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
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->refresh_data, &melt_cb, rms);
rms->double_melt = GNUNET_NO;
@ -888,9 +888,9 @@ melt_cb (void *cls,
* @param is the interpreter state.
*/
static void
refresh_melt_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
melt_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RefreshMeltState *rms = cls;
unsigned int num_fresh_coins;
@ -1006,11 +1006,11 @@ refresh_melt_run (void *cls,
TALER_TESTING_interpreter_fail (rms->is);
return;
}
rms->rmh = TALER_EXCHANGE_refresh_melt (is->exchange,
rms->refresh_data_length,
rms->refresh_data,
&melt_cb,
rms);
rms->rmh = TALER_EXCHANGE_melt (is->exchange,
rms->refresh_data_length,
rms->refresh_data,
&melt_cb,
rms);
if (NULL == rms->rmh)
{
@ -1030,8 +1030,8 @@ refresh_melt_run (void *cls,
* @param cmd the command which is being cleaned up.
*/
static void
refresh_melt_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
melt_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RefreshMeltState *rms = cls;
@ -1040,7 +1040,7 @@ refresh_melt_cleanup (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
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;
}
if (NULL != rms->retry_task)
@ -1073,10 +1073,10 @@ refresh_melt_cleanup (void *cls,
* @return #GNUNET_OK on success.
*/
static int
refresh_melt_traits (void *cls,
const void **ret,
const char *trait,
unsigned int index)
melt_traits (void *cls,
const void **ret,
const char *trait,
unsigned int index)
{
struct RefreshMeltState *rms = cls;
@ -1161,10 +1161,10 @@ parse_amounts (struct RefreshMeltState *rms,
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_melt (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...)
TALER_TESTING_cmd_melt (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...)
{
struct RefreshMeltState *rms;
va_list ap;
@ -1180,9 +1180,9 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = rms,
.run = &refresh_melt_run,
.cleanup = &refresh_melt_cleanup,
.traits = &refresh_melt_traits
.run = &melt_run,
.cleanup = &melt_cleanup,
.traits = &melt_traits
};
return cmd;
@ -1203,10 +1203,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_refresh_melt_double (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...)
TALER_TESTING_cmd_melt_double (const char *label,
const char *coin_reference,
unsigned int expected_response_code,
...)
{
struct RefreshMeltState *rms;
va_list ap;
@ -1223,9 +1223,9 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = rms,
.run = &refresh_melt_run,
.cleanup = &refresh_melt_cleanup,
.traits = &refresh_melt_traits
.run = &melt_run,
.cleanup = &melt_cleanup,
.traits = &melt_traits
};
return cmd;
@ -1240,11 +1240,11 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
* @return modified 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;
GNUNET_assert (&refresh_melt_run == cmd.run);
GNUNET_assert (&melt_run == cmd.run);
rms = cmd.cls;
rms->do_retry = GNUNET_YES;
return cmd;