summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2023-07-03 16:18:40 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2023-07-03 16:18:40 +0200
commit40629e89920267dadba39f5f7f2ab3d844088a0e (patch)
tree0c10bc6e983ed628f7a7ce1c9aa5d166733e0118
parent9c3ddcbc183a9a96f10cdb14f28258ea61f5f7c5 (diff)
downloadexchange-40629e89920267dadba39f5f7f2ab3d844088a0e.tar.gz
exchange-40629e89920267dadba39f5f7f2ab3d844088a0e.tar.bz2
exchange-40629e89920267dadba39f5f7f2ab3d844088a0e.zip
[age-withdraw] added library function for age-withdraw
- Added TALER_EXCHANGE_age_withdraw - Also: Change TALER_EXCHANGE_batch_withdraw and related functions to use GNUNET_CURL_ctx, TALER_EXCHANGE_keys and const char *echange_url
-rw-r--r--src/exchange/taler-exchange-httpd_age-withdraw.c1
-rw-r--r--src/include/taler_crypto_lib.h23
-rw-r--r--src/include/taler_exchange_service.h154
-rw-r--r--src/include/taler_exchangedb_plugin.h3
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/exchange_api_age_withdraw.c1033
-rw-r--r--src/lib/exchange_api_batch_withdraw.c30
-rw-r--r--src/lib/exchange_api_batch_withdraw2.c35
-rw-r--r--src/lib/exchange_api_common.c4
-rw-r--r--src/lib/exchange_api_csr_withdraw.c27
-rw-r--r--src/lib/exchange_api_curl_defaults.h1
-rw-r--r--src/lib/exchange_api_handle.c2
-rw-r--r--src/lib/exchange_api_melt.c1
-rw-r--r--src/lib/exchange_api_refreshes_reveal.c1
-rw-r--r--src/lib/exchange_api_withdraw.c34
-rw-r--r--src/lib/exchange_api_withdraw2.c33
-rw-r--r--src/testing/testing_api_cmd_batch_withdraw.c15
-rw-r--r--src/testing/testing_api_cmd_withdraw.c15
-rw-r--r--src/util/exchange_signatures.c28
-rw-r--r--src/util/wallet_signatures.c15
20 files changed, 1354 insertions, 102 deletions
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c b/src/exchange/taler-exchange-httpd_age-withdraw.c
index c451e5266..2f30ad8f6 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw.c
@@ -69,6 +69,7 @@ struct AgeWithdrawContext
/**
* kappa * #num_coins hashes of blinded coin planchets.
+ * FIXME[oec]: Make the [][] structure more explicit.
*/
struct TALER_BlindedPlanchet *coin_evs;
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 3af5d326d..3ad441cb9 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -439,7 +439,9 @@ struct TALER_AgeCommitmentPublicKeyP
/*
- * @brief Hash to represent the commitment to n*kappa blinded keys during a age-withdrawal.
+ * @brief Hash to represent the commitment to n*kappa blinded keys during a
+ * age-withdrawal. It is the running SHA512 hash over the hashes of the blinded
+ * envelopes of n*kappa coins.
*/
struct TALER_AgeWithdrawCommitmentHashP
{
@@ -3726,7 +3728,7 @@ TALER_wallet_withdraw_verify (
/**
* Sign age-withdraw request.
*
- * @param h_commitment hash all n*kappa blinded coins in the commitment for the age-withdraw
+ * @param h_commitment hash over all n*kappa blinded coins in the commitment for the age-withdraw
* @param amount_with_fee amount to debit the reserve for
* @param mask the mask that defines the age groups
* @param max_age maximum age from which the age group is derived, that the withdrawn coins must be restricted to.
@@ -3762,7 +3764,6 @@ TALER_wallet_age_withdraw_verify (
const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_ReserveSignatureP *reserve_sig);
-
/**
* Verify exchange melt confirmation.
*
@@ -4871,6 +4872,22 @@ TALER_exchange_online_age_withdraw_confirmation_sign (
struct TALER_ExchangeSignatureP *sig);
+/**
+ * Verfiy an exchange age-withdraw confirmation
+ *
+ * @param h_commitment Commitment over all n*kappa coin candidates from the original request to age-withdraw
+ * @param noreveal_index The index returned by the exchange
+ * @param exchange_pub The public key used for signing
+ * @param exchange_sig The signature from the exchange
+ */
+enum GNUNET_GenericReturnValue
+TALER_exchange_online_age_withdraw_confirmation_verify (
+ const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
+ uint32_t noreveal_index,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const struct TALER_ExchangeSignatureP *exchange_sig);
+
+
/* ********************* offline signing ************************** */
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index bc6a230b6..d4efc15d5 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -18,6 +18,7 @@
* @brief C interface of libtalerexchange, a C library to use exchange's HTTP API
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#ifndef _TALER_EXCHANGE_SERVICE_H
#define _TALER_EXCHANGE_SERVICE_H
@@ -1618,7 +1619,8 @@ typedef void
/**
* Get a CS R using a /csr-withdraw request.
*
- * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param curl_ctx The curl context to use for the requests
+ * @param exchange_url Base-URL to the excnange
* @param pk Which denomination key is the /csr request for
* @param nonce client nonce for the request
* @param res_cb the callback to call when the final result for this request is available
@@ -1629,7 +1631,8 @@ typedef void
*/
struct TALER_EXCHANGE_CsRWithdrawHandle *
TALER_EXCHANGE_csr_withdraw (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
const struct TALER_EXCHANGE_DenomPublicKey *pk,
const struct TALER_CsNonce *nonce,
TALER_EXCHANGE_CsRWithdrawCallback res_cb,
@@ -2448,7 +2451,9 @@ typedef void
* 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 curl_ctx The curl context to use
+ * @param exchange_url The base-URL of the exchange
+ * @param keys The /keys material from the exchange
* @param reserve_priv private key of the reserve to withdraw from
* @param wci inputs that determine the planchet
* @param res_cb the callback to call when the final result for this request is available
@@ -2459,7 +2464,9 @@ typedef void
*/
struct TALER_EXCHANGE_WithdrawHandle *
TALER_EXCHANGE_withdraw (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ struct TALER_EXCHANGE_Keys *keys,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_EXCHANGE_WithdrawCoinInput *wci,
TALER_EXCHANGE_WithdrawCallback res_cb,
@@ -2575,7 +2582,9 @@ typedef void
* 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 curl_ctx The curl context to use
+ * @param exchange_url The base-URL of the exchange
+ * @param keys The /keys material from the exchange
* @param reserve_priv private key of the reserve to withdraw from
* @param wcis inputs that determine the planchets
* @param wci_length number of entries in @a wcis
@@ -2587,7 +2596,9 @@ typedef void
*/
struct TALER_EXCHANGE_BatchWithdrawHandle *
TALER_EXCHANGE_batch_withdraw (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_Keys *keys,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_EXCHANGE_WithdrawCoinInput *wcis,
unsigned int wci_length,
@@ -2668,7 +2679,9 @@ struct TALER_EXCHANGE_Withdraw2Handle;
* 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 curl_ctx The curl-context to use
+ * @param exchange_url The base-URL of the exchange
+ * @param keys The /keys material from the exchange
* @param pd planchet details of the planchet to withdraw
* @param reserve_priv private key of the reserve to withdraw from
* @param res_cb the callback to call when the final result for this request is available
@@ -2679,7 +2692,9 @@ struct TALER_EXCHANGE_Withdraw2Handle;
*/
struct TALER_EXCHANGE_Withdraw2Handle *
TALER_EXCHANGE_withdraw2 (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ struct TALER_EXCHANGE_Keys *keys,
const struct TALER_PlanchetDetail *pd,
const struct TALER_ReservePrivateKeyP *reserve_priv,
TALER_EXCHANGE_Withdraw2Callback res_cb,
@@ -2765,7 +2780,9 @@ struct TALER_EXCHANGE_BatchWithdraw2Handle;
* 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 curl_ctx The curl context to use
+ * @param exchange_url The base-URL of the exchange
+ * @param keys The /keys material from the exchange
* @param pds array of planchet details of the planchet to withdraw
* @param pds_length number of entries in the @a pds array
* @param reserve_priv private key of the reserve to withdraw from
@@ -2777,7 +2794,9 @@ struct TALER_EXCHANGE_BatchWithdraw2Handle;
*/
struct TALER_EXCHANGE_BatchWithdraw2Handle *
TALER_EXCHANGE_batch_withdraw2 (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_Keys *keys,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_PlanchetDetail *pds,
unsigned int pds_length,
@@ -2796,6 +2815,119 @@ TALER_EXCHANGE_batch_withdraw2_cancel (
struct TALER_EXCHANGE_BatchWithdraw2Handle *wh);
+/* ********************* /reserve/$RESERVE_PUB/age-withdraw *************** */
+
+/**
+ * @brief Information needed to withdraw age restricted coins.
+ */
+struct TALER_EXCHANGE_AgeWithdrawCoinInput
+{
+ /* The master secret from which we derive all other relevant values for
+ * the coin: private key, nonces (if applicable) and age restriction
+ */
+ const struct TALER_PlanchetMasterSecretP secret[TALER_CNC_KAPPA];
+
+ /* The denomination of the coin. Must support age restriction, i.e
+ * its .keys.age_mask MUST not be 0 */
+ const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+};
+
+/**
+ * @brief A handle to a /reserves/$RESERVE_PUB/age-withdraw request
+ */
+struct TALER_EXCHANGE_AgeWithdrawHandle;
+
+/**
+ * @brief Details about the response for a age withdraw request.
+ */
+struct TALER_EXCHANGE_AgeWithdrawResponse
+{
+ /**
+ * HTTP response data.
+ */
+ struct TALER_EXCHANGE_HttpResponse hr;
+
+ /**
+ * Details about the response
+ */
+ union
+ {
+ /**
+ * Details if the status is #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * Index that should not be revealed during the age-withdraw reveal phase.
+ * The struct TALER_PlanchetMasterSecretP * from the request
+ * with this index are the ones to keep.
+ */
+ uint8_t noreveal_index;
+
+ /**
+ * Signature of the exchange over the origina TALER_AgeWithdrawRequestPS
+ */
+ struct TALER_ExchangeSignatureP exchange_sig;
+
+ /**
+ * Key used by the exchange for @e exchange_sig
+ */
+ struct TALER_ExchangePublicKeyP exchange_pub;
+
+ } ok;
+ /* FIXME[oec]: error cases */
+ } details;
+};
+
+typedef void
+(*TALER_EXCHANGE_AgeWithdrawCallback)(
+ void *cls,
+ const struct TALER_EXCHANGE_AgeWithdrawResponse *awr);
+
+/**
+ * Submit an age-withdraw 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 @a rd should be committed to persistent storage
+ * prior to calling this function.
+ *
+ * @param curl_ctx The curl context
+ * @param exchange_url The base url of the exchange
+ * @parm keys The denomination keys from the exchange
+ * @param reserve_priv The pivate key to the reserve
+ * @param coin_inputs The input for the coins to withdraw
+ * @param num_coins The number of elements in @e coin_inputs
+ * @param max_age The maximum age we commit to.
+ * @param res_cb A callback for the result, maybe NULL
+ * @param res_cb_cls A closure for @e res_cb, maybe NULL
+ * @return a handle for this request; NULL if the argument was invalid.
+ * In this case, the callback will not be called.
+ */
+struct TALER_EXCHANGE_AgeWithdrawHandle *
+TALER_EXCHANGE_age_withdraw (
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ const struct TALER_EXCHANGE_AgeWithdrawCoinInput *coin_inputs,
+ size_t num_coins,
+ uint8_t max_age,
+ TALER_EXCHANGE_AgeWithdrawCallback res_cb,
+ void *res_cb_cls);
+
+/**
+ * Cancel a age-withdraw request. This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param awh the age-withdraw handle
+ */
+void
+TALER_EXCHANGE_age_withdraw_cancel (
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh);
+
+
/* ********************* /refresh/melt+reveal ***************************** */
@@ -3565,7 +3697,7 @@ TALER_EXCHANGE_verify_coin_history (
*/
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_parse_reserve_history (
- struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_EXCHANGE_Keys *keys,
const json_t *history,
const struct TALER_ReservePublicKeyP *reserve_pub,
const char *currency,
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index c4fcad562..1b1a657c2 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1215,8 +1215,7 @@ struct TALER_EXCHANGEDB_AgeWithdraw
/**
* Signature confirming the age withdrawal commitment, matching @e
- * reserve_pub, @e maximum_age_group and @e h_commitment and @e
- * total_amount_with_fee.
+ * reserve_pub, @e max_age and @e h_commitment and @e amount_with_fee.
*/
struct TALER_ReserveSignatureP reserve_sig;
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 53190bc5b..762a3f805 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -22,6 +22,7 @@ libtalerexchange_la_LDFLAGS = \
-no-undefined
libtalerexchange_la_SOURCES = \
exchange_api_add_aml_decision.c \
+ exchange_api_age_withdraw.c \
exchange_api_auditor_add_denomination.c \
exchange_api_batch_deposit.c \
exchange_api_batch_withdraw.c \
diff --git a/src/lib/exchange_api_age_withdraw.c b/src/lib/exchange_api_age_withdraw.c
new file mode 100644
index 000000000..4e146c15c
--- /dev/null
+++ b/src/lib/exchange_api_age_withdraw.c
@@ -0,0 +1,1033 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 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_age_withdraw.c
+ * @brief Implementation of /reserves/$RESERVE_PUB/age-withdraw requests
+ * @author Özgür Kesim
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_common.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_curl_lib.h"
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "exchange_api_common.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+struct CoinCandidate
+{
+ /**
+ * Master key material for the coin candidates.
+ */
+ struct TALER_PlanchetMasterSecretP secret;
+
+ /**
+ * Age commitment for the coin candidates, calculated from the @e ps and a
+ * given maximum age
+ */
+ struct TALER_AgeCommitmentProof age_commitment_proof;
+
+ /**
+ * Age commitment for the coin.
+ */
+ struct TALER_AgeCommitmentHash h_age_commitment;
+
+ /**
+ * blinding secret
+ */
+ union TALER_DenominationBlindingKeyP blinding_key;
+
+ /**
+ * Private key of the coin we are withdrawing.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+ /**
+ * Details of the planchet.
+ */
+ struct TALER_PlanchetDetail planchet_detail;
+
+ /**
+ * Values of the @cipher selected
+ */
+ struct TALER_ExchangeWithdrawValues alg_values;
+
+ /**
+ * Hash of the public key of the coin we are signing.
+ */
+ struct TALER_CoinPubHashP h_coin_pub;
+
+ /* Blinded hash of the coin */
+ struct TALER_BlindedCoinHashP blinded_coin_h;
+
+ /**
+ * The following fields are needed as closure for the call to /csr-withdrwaw
+ * per coin-candidate.
+ */
+
+ /* Denomination information, needed for CS coins for the step after /csr-withdraw */
+ struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+
+ /**
+ * Handler for the CS R request (only used for TALER_DENOMINATION_CS denominations)
+ */
+ struct TALER_EXCHANGE_CsRWithdrawHandle *csr_withdraw_handle;
+
+ /* Needed in the closure for csr-withdraw calls */
+ struct TALER_EXCHANGE_AgeWithdrawHandle *age_withdraw_handle;
+
+};
+
+
+/**
+ * Data we keep per coin in the batch.
+ */
+struct CoinData
+{
+
+ /**
+ * Denomination key we are withdrawing.
+ */
+ struct TALER_EXCHANGE_DenomPublicKey denom_pub;
+
+ /**
+ * The Candidates for the coin
+ */
+ struct CoinCandidate coin_candidates[TALER_CNC_KAPPA];
+
+};
+
+
+/**
+ * @brief A /reserves/$RESERVE_PUB/age-withdraw request-handle
+ */
+struct TALER_EXCHANGE_AgeWithdrawHandle
+{
+
+ /**
+ * Reserve private key.
+ */
+ const struct TALER_ReservePrivateKeyP *reserve_priv;
+
+ /**
+ * Reserve public key, calculated
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Signature of the reserve for the request, calculated after all
+ * parameters for the coins are collected.
+ */
+ struct TALER_ReserveSignatureP reserve_sig;
+
+ /*
+ * The denomination keys of the exchange
+ */
+ struct TALER_EXCHANGE_Keys *keys;
+
+ /**
+ * The base-URL of the exchange.
+ */
+ const char *exchange_url;
+
+ /**
+ * The age mask, extacted from the denominations.
+ * MUST be the same for all denominations
+ *
+ */
+ struct TALER_AgeMask age_mask;
+
+ /**
+ * Maximum age to commit to.
+ */
+ uint8_t max_age;
+
+ /**
+ * Length of the @e coin_data Array
+ */
+ size_t num_coins;
+
+ /**
+ * Array of per-coin data
+ */
+ struct CoinData *coin_data;
+
+ /**
+ * The commitment calculated as SHA512 hash over all blinded_coin_h
+ */
+ struct TALER_AgeWithdrawCommitmentHashP h_commitment;
+
+ /**
+ * Total amount requested (value plus withdraw fee).
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * Number of /csr-withdraw requests still pending.
+ */
+ unsigned int csr_pending;
+
+ /**
+ * The url for this request.
+ */
+ char *request_url;
+
+ /**
+ * Context for curl.
+ */
+ struct GNUNET_CURL_Context *curl_ctx;
+
+ /**
+ * CURL handle for the request job.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Post Context
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * Function to call with age-withdraw response results.
+ */
+ TALER_EXCHANGE_AgeWithdrawCallback callback;
+
+ /**
+ * Closure for @e age_withdraw_cb
+ */
+ void *callback_cls;
+
+};
+
+/**
+ * We got a 200 OK response for the /reserves/$RESERVE_PUB/age-withdraw operation.
+ * Extract the noreveal_index and return it to the caller.
+ *
+ * @param awh operation handle
+ * @param j_response reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static enum GNUNET_GenericReturnValue
+reserve_age_withdraw_ok (
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh,
+ const json_t *j_response)
+{
+ struct TALER_EXCHANGE_AgeWithdrawResponse response = {
+ .hr.reply = j_response,
+ .hr.http_status = MHD_HTTP_OK
+ };
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint8 ("noreaveal_index",
+ &response.details.ok.noreveal_index),
+ GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+ &response.details.ok.exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &response.details.ok.exchange_pub)
+ };
+
+ if (GNUNET_OK!=
+ GNUNET_JSON_parse (j_response,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK !=
+ TALER_exchange_online_age_withdraw_confirmation_verify (
+ &awh->h_commitment,
+ response.details.ok.noreveal_index,
+ &response.details.ok.exchange_pub,
+ &response.details.ok.exchange_sig))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+
+ }
+ awh->callback (awh->callback_cls,
+ &response);
+ /* make sure the callback isn't called again */
+ awh->callback = NULL;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * FIXME: This function should be common to batch- and age-withdraw
+ *
+ * We got a 409 CONFLICT response for the /reserves/$RESERVE_PUB/age-withdraw operation.
+ * Check the signatures on the batch 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 keys The denomination keys from the exchange
+ * @param reserve_pub The reserve's public key
+ * @param requested_amount The requested amount
+ * @param json reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static enum GNUNET_GenericReturnValue
+reserve_age_withdraw_payment_required (
+ struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *requested_amount,
+ const json_t *json)
+{
+ struct TALER_Amount balance;
+ struct TALER_Amount total_in_from_history;
+ struct TALER_Amount total_out_from_history;
+ json_t *history;
+ size_t len;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("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_ReserveHistoryEntry *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_ReserveHistoryEntry)
+ * len);
+ if (NULL == rhistory)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_parse_reserve_history (
+ keys,
+ history,
+ reserve_pub,
+ balance.currency,
+ &total_in_from_history,
+ &total_out_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);
+ }
+
+ /* 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/age-withdraw request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_AgeWithdrawHandle`
+ * @param aw2r response data
+ */
+static void
+handle_reserve_age_withdraw_finished (
+ void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh = cls;
+ const json_t *j_response = response;
+ struct TALER_EXCHANGE_AgeWithdrawResponse awr = {
+ .hr.reply = j_response,
+ .hr.http_status = (unsigned int) response_code
+ };
+
+ awh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ awr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ reserve_age_withdraw_ok (awh,
+ j_response))
+ {
+ GNUNET_break_op (0);
+ awr.hr.http_status = 0;
+ awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ break;
+ }
+ GNUNET_assert (NULL == awh->callback);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return;
+ case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+ /* only validate reply is well-formed */
+ {
+ uint64_t ptu;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("legitimization_uuid",
+ &ptu),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (j_response,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ awr.hr.http_status = 0;
+ awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ 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 */
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ GNUNET_break_op (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 */
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ 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. */
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ case MHD_HTTP_CONFLICT:
+ /* The exchange says that the reserve has insufficient funds;
+ check the signatures in the history... */
+ if (GNUNET_OK !=
+ reserve_age_withdraw_payment_required (awh->keys,
+ &awh->reserve_pub,
+ &awh->amount_with_fee,
+ j_response))
+ {
+ GNUNET_break_op (0);
+ awr.hr.http_status = 0;
+ awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
+ else
+ {
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ }
+ break;
+ case MHD_HTTP_GONE:
+ /* could happen if denomination was revoked */
+ /* Note: one might want to check /keys for revocation
+ signature here, alas tricky in case our /keys
+ is outdated => left to clients */
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_break_op (0);
+ awr.hr.ec = TALER_JSON_get_error_code (j_response);
+ awr.hr.hint = TALER_JSON_get_error_hint (j_response);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for exchange age-withdraw\n",
+ (unsigned int) response_code,
+ (int) awr.hr.ec);
+ break;
+ }
+ awh->callback (awh->callback_cls,
+ &awr);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+}
+
+
+/**
+ * Runs the actual age-withdraw operation. If there were CS-denominations
+ * involved, started once the all calls to /csr-withdraw are done.
+ *
+ * @param[in,out] awh age withdraw handler
+ */
+static void
+perform_protocol (
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh)
+{
+#define FAIL_IF(cond) \
+ do { \
+ if ((cond)) \
+ { \
+ GNUNET_break (! (cond)); \
+ goto ERROR; \
+ } \
+ } while(0)
+
+ struct GNUNET_HashContext *coins_hctx;
+ json_t *j_denoms = NULL;
+ json_t *j_array_candidates = NULL;
+ json_t *j_request_body = NULL;
+ CURL *curlh = NULL;
+
+
+ GNUNET_assert (0 == awh->csr_pending);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Attempting to age-withdraw from reserve %s with maximum age %d\n",
+ TALER_B2S (&awh->reserve_pub),
+ awh->max_age);
+
+ coins_hctx = GNUNET_CRYPTO_hash_context_start ();
+ FAIL_IF (NULL == coins_hctx);
+
+
+ j_denoms = json_array ();
+ j_array_candidates = json_array ();
+ FAIL_IF ((NULL == j_denoms) ||
+ (NULL == j_array_candidates));
+
+ for (size_t i = 0; i< awh->num_coins; i++)
+ {
+ /* Build the denomination array */
+ {
+ struct TALER_EXCHANGE_DenomPublicKey *denom =
+ &awh->coin_data[i].denom_pub;
+ json_t *jdenom = GNUNET_JSON_PACK (
+ TALER_JSON_pack_denom_pub (NULL,
+ &denom->key));
+
+ FAIL_IF (NULL == jdenom);
+ FAIL_IF (0 < json_array_append_new (j_denoms,
+ jdenom));
+
+ /* Build the candidate array */
+ {
+ const struct CoinCandidate *can = awh->coin_data[i].coin_candidates;
+ json_t *j_can = json_array ();
+ FAIL_IF (NULL == j_can);
+
+ for (size_t k = 0; k < TALER_CNC_KAPPA; k++)
+ {
+ json_t *jc = GNUNET_JSON_PACK (
+ TALER_JSON_pack_blinded_planchet (
+ NULL,
+ &can->planchet_detail.blinded_planchet));
+
+ FAIL_IF (NULL == jc);
+ FAIL_IF (0 < json_array_append_new (j_can,
+ jc));
+
+ GNUNET_CRYPTO_hash_context_read (coins_hctx,
+ &can->blinded_coin_h,
+ sizeof(can->blinded_coin_h));
+ }
+ }
+ }
+ }
+
+ /* Sign the request */
+ {
+ struct TALER_AgeWithdrawCommitmentHashP coins_commitment_h;
+
+ GNUNET_CRYPTO_hash_context_finish (coins_hctx,
+ &coins_commitment_h.hash);
+
+ TALER_wallet_age_withdraw_sign (&coins_commitment_h,
+ &awh->amount_with_fee,
+ &awh->age_mask,
+ awh->max_age,
+ awh->reserve_priv,
+ &awh->reserve_sig);
+ }
+
+ /* Initiate the POST-request */
+ j_request_body = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("denoms_h", j_denoms),
+ GNUNET_JSON_pack_array_steal ("blinded_coin_evs", j_array_candidates),
+ GNUNET_JSON_pack_uint64 ("max_age", awh->max_age),
+ GNUNET_JSON_pack_data_auto ("reserve_sig", &awh->reserve_sig));
+ FAIL_IF (NULL == j_request_body);
+
+ curlh = TALER_EXCHANGE_curl_easy_get_ (awh->request_url);
+ FAIL_IF (NULL == curlh);
+ FAIL_IF (GNUNET_OK !=
+ TALER_curl_easy_post (&awh->post_ctx,
+ curlh,
+ j_request_body));
+ json_decref (j_request_body);
+ j_request_body = NULL;
+
+ awh->job = GNUNET_CURL_job_add2 (awh->curl_ctx,
+ curlh,
+ awh->post_ctx.headers,
+ &handle_reserve_age_withdraw_finished,
+ awh);
+ FAIL_IF (NULL == awh->job);
+
+ /* No errors, return */
+ return;
+
+ERROR:
+ if (NULL != j_denoms)
+ json_decref (j_denoms);
+ if (NULL != j_array_candidates)
+ json_decref (j_array_candidates);
+ if (NULL != j_request_body)
+ json_decref (j_request_body);
+ if (NULL != curlh)
+ curl_easy_cleanup (curlh);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return;
+#undef FAIL_IF
+}
+
+
+/**
+ * Prepares the request URL for the age-withdraw request
+ *
+ * @param awh The handler
+ */
+static
+enum GNUNET_GenericReturnValue
+prepare_url (
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh,
+ const char *exchange_url)
+{
+ 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 (
+ &awh->reserve_pub,
+ sizeof (awh->reserve_pub),
+ pub_str,
+ sizeof (pub_str));
+ *end = '\0';
+ GNUNET_snprintf (arg_str,
+ sizeof (arg_str),
+ "reserves/%s/age-withdraw",
+ pub_str);
+
+ awh->request_url = TALER_url_join (exchange_url,
+ arg_str,
+ NULL);
+ if (NULL == awh->request_url)
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief Function called when CSR withdraw retrieval is finished
+ *
+ * @param cls the `struct CoinCandidate *`
+ * @param csrr replies from the /csr-withdraw request
+ */
+static void
+csr_withdraw_done (
+ void *cls,
+ const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr)
+{
+ struct CoinCandidate *can = cls;
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh = can->age_withdraw_handle;
+ struct TALER_EXCHANGE_AgeWithdrawResponse awr = { .hr = csrr->hr };
+
+ can->csr_withdraw_handle = NULL;
+
+ switch (csrr->hr.http_status)
+ {
+ case MHD_HTTP_OK:
+ {
+ bool success = true;
+ /* Complete the initialization of the coin with CS denomination */
+ can->alg_values = csrr->details.ok.alg_values;
+ TALER_planchet_setup_coin_priv (&can->secret,
+ &can->alg_values,
+ &can->coin_priv);
+ TALER_planchet_blinding_secret_create (&can->secret,
+ &can->alg_values,
+ &can->blinding_key);
+ /* This initializes the 2nd half of the
+ can->planchet_detail.blinded_planchet! */
+ if (GNUNET_OK !=
+ TALER_planchet_prepare (&can->denom_pub->key,
+ &can->alg_values,
+ &can->blinding_key,
+ &can->coin_priv,
+ &can->h_age_commitment,
+ &can->h_coin_pub,
+ &can->planchet_detail))
+ {
+ GNUNET_break (0);
+ success = false;
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ }
+
+ if (GNUNET_OK !=
+ TALER_coin_ev_hash (&can->planchet_detail.blinded_planchet,
+ &can->planchet_detail.denom_pub_hash,
+ &can->blinded_coin_h))
+ {
+ GNUNET_break (0);
+ success = false;
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ }
+
+ awh->csr_pending--;
+
+ /* No more pending requests to /csr-withdraw, we can now perform the
+ * actual age-withdraw operation */
+ if (0 == awh->csr_pending && success)
+ perform_protocol (awh);
+ return;
+ }
+ default:
+ break;
+ }
+
+ awh->callback (awh->callback_cls,
+ &awr);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+}
+
+
+/**
+ * @brief Prepare the coins for the call to age-withdraw and calculates
+ * the total amount with fees.
+ *
+ * For denomination with CS as cipher, initiates the preflight to retrieve the
+ * csr-parameter via /csr-withdraw.
+ *
+ * @param awh The handler to the age-withdraw
+ * @param coin_inputs The input for the individial coin(-candidates)
+ * @param num_coins The number of coins in @e coin_inputs
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+static
+enum GNUNET_GenericReturnValue
+prepare_coins (
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh,
+ const struct TALER_EXCHANGE_AgeWithdrawCoinInput *coin_inputs,
+ size_t num_coins)
+{
+
+ if (GNUNET_OK != TALER_amount_set_zero (
+ awh->keys->currency,
+ &awh->amount_with_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ awh->coin_data = GNUNET_new_array (awh->num_coins,
+ struct CoinData);
+
+ GNUNET_assert (0 < num_coins);
+ awh->age_mask = coin_inputs[0].denom_pub->key.age_mask;
+
+ for (size_t i = 0; i < num_coins; i++)
+ {
+ struct CoinData *cd = &awh->coin_data[i];
+ const struct TALER_EXCHANGE_AgeWithdrawCoinInput *input = &coin_inputs[i];
+ cd->denom_pub = *input->denom_pub;
+
+ /* The mask must be the same for all coins */
+ if (awh->age_mask.bits != input->denom_pub->key.age_mask.bits)
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+
+ TALER_denom_pub_deep_copy (&cd->denom_pub.key,
+ &input->denom_pub->key);
+
+ /* Accumulate total value with fees */
+ {
+ struct TALER_Amount coin_total;
+
+ if (0 >
+ TALER_amount_add (&coin_total,
+ &cd->denom_pub.fees.withdraw,
+ &cd->denom_pub.value))
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 >
+ TALER_amount_add (&awh->amount_with_fee,
+ &awh->amount_with_fee,
+ &coin_total))
+ {
+ /* Overflow here? Very strange, our CPU must be fried... */
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
+ {
+ struct CoinCandidate *can = &cd->coin_candidates[k];
+
+ can->secret = input->secret[k];
+
+ /* Derive the age restriction from the given secret and
+ * the maximum age */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_age_restriction_from_secret (
+ &can->secret,
+ &input->denom_pub->key.age_mask,
+ awh->max_age,
+ &can->age_commitment_proof));
+ TALER_age_commitment_hash (&can->age_commitment_proof.commitment,
+ &can->h_age_commitment);
+
+ switch (input->denom_pub->key.cipher)
+ {
+ case TALER_DENOMINATION_RSA:
+ {
+ can->alg_values.cipher = TALER_DENOMINATION_RSA;
+ TALER_planchet_setup_coin_priv (&can->secret,
+ &can->alg_values,
+ &can->coin_priv);
+ TALER_planchet_blinding_secret_create (&can->secret,
+ &can->alg_values,
+ &can->blinding_key);
+ if (GNUNET_OK !=
+ TALER_planchet_prepare (&cd->denom_pub.key,
+ &can->alg_values,
+ &can->blinding_key,
+ &can->coin_priv,
+ &can->h_age_commitment,
+ &can->h_coin_pub,
+ &can->planchet_detail))
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_coin_ev_hash (&can->planchet_detail.blinded_planchet,
+ &can->planchet_detail.denom_pub_hash,
+ &can->blinded_coin_h))
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+ break;
+ }
+ case TALER_DENOMINATION_CS:
+ {
+ /**
+ * Save the handler and the denomination for the callback
+ * after the call to csr-withdraw */
+ can->age_withdraw_handle = awh;
+ can->denom_pub = &cd->denom_pub;
+
+ TALER_cs_withdraw_nonce_derive (
+ &can->secret,
+ &can->planchet_detail
+ .blinded_planchet
+ .details
+ .cs_blinded_planchet
+ .nonce);
+
+ /* Note that we only initialize the first half
+ of the blinded_planchet here; the other part
+ will be done after the /csr-withdraw request! */
+ can->planchet_detail.blinded_planchet.cipher = TALER_DENOMINATION_CS;
+ can->csr_withdraw_handle = NULL;
+ TALER_EXCHANGE_csr_withdraw (
+ awh->curl_ctx,
+ awh->exchange_url,
+ &cd->denom_pub,
+ &can->planchet_detail
+ .blinded_planchet
+ .details
+ .cs_blinded_planchet
+ .nonce,
+ &csr_withdraw_done,
+ &can);
+ if (NULL == can->csr_withdraw_handle)
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+
+ awh->csr_pending++;
+ break;
+ }
+ default:
+ GNUNET_break (0);
+ TALER_EXCHANGE_age_withdraw_cancel (awh);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ return GNUNET_OK;
+};
+
+struct TALER_EXCHANGE_AgeWithdrawHandle *
+TALER_EXCHANGE_age_withdraw (
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ const struct TALER_EXCHANGE_AgeWithdrawCoinInput *coin_inputs,
+ size_t num_coins,
+ uint8_t max_age,
+ TALER_EXCHANGE_AgeWithdrawCallback res_cb,
+ void *res_cb_cls)
+{
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh;
+
+ awh = GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawHandle);
+ awh->exchange_url = exchange_url;
+ awh->keys = TALER_EXCHANGE_keys_incref (keys);
+ awh->curl_ctx = curl_ctx;
+ awh->reserve_priv = reserve_priv;
+ awh->callback = res_cb;
+ awh->callback_cls = res_cb_cls;
+ awh->num_coins = num_coins;
+ awh->max_age = max_age;
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&awh->reserve_priv->eddsa_priv,
+ &awh->reserve_pub.eddsa_pub);
+
+
+ if (GNUNET_OK != prepare_url (awh,
+ exchange_url))
+ return NULL;
+
+ if (GNUNET_OK != prepare_coins (awh,
+ coin_inputs,
+ num_coins))
+ return NULL;
+
+ /* If there were no CS denominations, we can now perform the actual
+ * age-withdraw protocol. Otherwise, there are calls to /csr-withdraw
+ * in flight and once they finish, the age-withdraw-protocol will be
+ * called from within the csr_withdraw_done-function.
+ */
+ if (0 == awh->csr_pending)
+ perform_protocol (awh);
+
+ return awh;
+}
+
+
+void
+TALER_EXCHANGE_age_withdraw_cancel (
+ struct TALER_EXCHANGE_AgeWithdrawHandle *awh)
+{
+ /* Cleanup coin data */
+ for (unsigned int i = 0; i<awh->num_coins; i++)
+ {
+ struct CoinData *cd = &awh->coin_data[i];
+
+ for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
+ {
+ struct CoinCandidate *can = &cd->coin_candidates[k];
+
+ if (NULL != can->csr_withdraw_handle)
+ {
+ TALER_EXCHANGE_csr_withdraw_cancel (can->csr_withdraw_handle);
+ can->csr_withdraw_handle = NULL;
+ }
+ TALER_blinded_planchet_free (&can->planchet_detail.blinded_planchet);
+ }
+ TALER_denom_pub_free (&cd->denom_pub.key);
+ }
+ GNUNET_free (awh->coin_data);
+
+ /* Cleanup CURL job data */
+ if (NULL != awh->job)
+ {
+ GNUNET_CURL_job_cancel (awh->job);
+ awh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&awh->post_ctx);
+ TALER_EXCHANGE_keys_decref (awh->keys);
+ GNUNET_free (awh->request_url);
+ GNUNET_free (awh);
+
+}
+
+
+/* exchange_api_age_withdraw.c */
diff --git a/src/lib/exchange_api_batch_withdraw.c b/src/lib/exchange_api_batch_withdraw.c
index 4817ae403..07ad37f63 100644
--- a/src/lib/exchange_api_batch_withdraw.c
+++ b/src/lib/exchange_api_batch_withdraw.c
@@ -97,9 +97,20 @@ struct TALER_EXCHANGE_BatchWithdrawHandle
{
/**
- * The connection to exchange this request handle will use
+ * The curl context to use
*/
- struct TALER_EXCHANGE_Handle *exchange;
+ struct GNUNET_CURL_Context *curl_ctx;
+
+ /**
+ * The base URL to the exchange
+ */
+ const char *exchange_url;
+
+ /**
+ * The /keys information from the exchange
+ */
+ const struct TALER_EXCHANGE_Keys *keys;
+
/**
* Handle for the actual (internal) batch withdraw operation.
@@ -255,7 +266,9 @@ phase_two (struct TALER_EXCHANGE_BatchWithdrawHandle *wh)
pds[i] = cd->pd;
}
wh->wh2 = TALER_EXCHANGE_batch_withdraw2 (
- wh->exchange,
+ wh->curl_ctx,
+ wh->exchange_url,
+ wh->keys,
wh->reserve_priv,
pds,
wh->num_coins,
@@ -322,7 +335,9 @@ withdraw_cs_stage_two_callback (
struct TALER_EXCHANGE_BatchWithdrawHandle *
TALER_EXCHANGE_batch_withdraw (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_Keys *keys,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_EXCHANGE_WithdrawCoinInput *wcis,
unsigned int wci_length,
@@ -332,7 +347,9 @@ TALER_EXCHANGE_batch_withdraw (
struct TALER_EXCHANGE_BatchWithdrawHandle *wh;
wh = GNUNET_new (struct TALER_EXCHANGE_BatchWithdrawHandle);
- wh->exchange = exchange;
+ wh->curl_ctx = curl_ctx;
+ wh->exchange_url = exchange_url;
+ wh->keys = keys;
wh->cb = res_cb;
wh->cb_cls = res_cb_cls;
wh->reserve_priv = reserve_priv;
@@ -386,7 +403,8 @@ TALER_EXCHANGE_batch_withdraw (
will be done after the /csr-withdraw request! */
cd->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
cd->csrh = TALER_EXCHANGE_csr_withdraw (
- exchange,
+ curl_ctx,
+ exchange_url,
&cd->pk,
&cd->pd.blinded_planchet.details.cs_blinded_planchet.nonce,
&withdraw_cs_stage_two_callback,
diff --git a/src/lib/exchange_api_batch_withdraw2.c b/src/lib/exchange_api_batch_withdraw2.c
index 6dd421cea..a855fc57e 100644
--- a/src/lib/exchange_api_batch_withdraw2.c
+++ b/src/lib/exchange_api_batch_withdraw2.c
@@ -39,14 +39,14 @@ struct TALER_EXCHANGE_BatchWithdraw2Handle
{
/**
- * The connection to exchange this request handle will use
+ * The url for this request.
*/
- struct TALER_EXCHANGE_Handle *exchange;
+ char *url;
/**
- * The url for this request.
+ * The /keys material from the exchange
*/
- char *url;
+ const struct TALER_EXCHANGE_Keys *keys;
/**
* Handle for the request.
@@ -219,7 +219,7 @@ reserve_batch_withdraw_payment_required (
if (GNUNET_OK !=
TALER_EXCHANGE_parse_reserve_history (
- TALER_EXCHANGE_get_keys (wh->exchange),
+ wh->keys,
history,
&wh->reserve_pub,
balance.currency,
@@ -387,7 +387,9 @@ handle_reserve_batch_withdraw_finished (void *cls,
struct TALER_EXCHANGE_BatchWithdraw2Handle *
TALER_EXCHANGE_batch_withdraw2 (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_Keys *keys,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_PlanchetDetail *pds,
unsigned int pds_length,
@@ -395,21 +397,15 @@ TALER_EXCHANGE_batch_withdraw2 (
void *res_cb_cls)
{
struct TALER_EXCHANGE_BatchWithdraw2Handle *wh;
- const struct TALER_EXCHANGE_Keys *keys;
const struct TALER_EXCHANGE_DenomPublicKey *dk;
struct TALER_ReserveSignatureP reserve_sig;
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
struct TALER_BlindedCoinHashP bch;
json_t *jc;
- keys = TALER_EXCHANGE_get_keys (exchange);
- if (NULL == keys)
- {
- GNUNET_break (0);
- return NULL;
- }
+ GNUNET_assert (NULL != keys);
wh = GNUNET_new (struct TALER_EXCHANGE_BatchWithdraw2Handle);
- wh->exchange = exchange;
+ wh->keys = keys;
wh->cb = res_cb;
wh->cb_cls = res_cb_cls;
wh->num_coins = pds_length;
@@ -430,14 +426,15 @@ TALER_EXCHANGE_batch_withdraw2 (
*end = '\0';
GNUNET_snprintf (arg_str,
sizeof (arg_str),
- "/reserves/%s/batch-withdraw",
+ "reserves/%s/batch-withdraw",
pub_str);
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Attempting to batch-withdraw from reserve %s\n",
TALER_B2S (&wh->reserve_pub));
- wh->url = TEAH_path_to_url (exchange,
- arg_str);
+ wh->url = TALER_url_join (exchange_url,
+ arg_str,
+ NULL);
if (NULL == wh->url)
{
GNUNET_break (0);
@@ -513,13 +510,11 @@ TALER_EXCHANGE_batch_withdraw2 (
}
{
CURL *eh;
- struct GNUNET_CURL_Context *ctx;
json_t *req;
req = GNUNET_JSON_PACK (
GNUNET_JSON_pack_array_steal ("planchets",
jc));
- ctx = TEAH_handle_to_context (exchange);
eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
if ( (NULL == eh) ||
(GNUNET_OK !=
@@ -535,7 +530,7 @@ TALER_EXCHANGE_batch_withdraw2 (
return NULL;
}
json_decref (req);
- wh->job = GNUNET_CURL_job_add2 (ctx,
+ wh->job = GNUNET_CURL_job_add2 (curl_ctx,
eh,
wh->post_ctx.headers,
&handle_reserve_batch_withdraw_finished,
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index f43673d76..77a5cf816 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -36,7 +36,7 @@ struct HistoryParseContext
/**
* Keys of the exchange we use.
*/
- struct TALER_EXCHANGE_Keys *keys;
+ const struct TALER_EXCHANGE_Keys *keys;
/**
* Our reserve public key.
@@ -647,7 +647,7 @@ parse_close (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_parse_reserve_history (
- struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_EXCHANGE_Keys *keys,
const json_t *history,
const struct TALER_ReservePublicKeyP *reserve_pub,
const char *currency,
diff --git a/src/lib/exchange_api_csr_withdraw.c b/src/lib/exchange_api_csr_withdraw.c
index fca3fff8e..4c1d83a9a 100644
--- a/src/lib/exchange_api_csr_withdraw.c
+++ b/src/lib/exchange_api_csr_withdraw.c
@@ -39,11 +39,6 @@
struct TALER_EXCHANGE_CsRWithdrawHandle
{
/**
- * The connection to exchange this request handle will use
- */
- struct TALER_EXCHANGE_Handle *exchange;
-
- /**
* Function to call with the result.
*/
TALER_EXCHANGE_CsRWithdrawCallback cb;
@@ -204,11 +199,13 @@ handle_csr_finished (void *cls,
struct TALER_EXCHANGE_CsRWithdrawHandle *
-TALER_EXCHANGE_csr_withdraw (struct TALER_EXCHANGE_Handle *exchange,
- const struct TALER_EXCHANGE_DenomPublicKey *pk,
- const struct TALER_CsNonce *nonce,
- TALER_EXCHANGE_CsRWithdrawCallback res_cb,
- void *res_cb_cls)
+TALER_EXCHANGE_csr_withdraw (
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_DenomPublicKey *pk,
+ const struct TALER_CsNonce *nonce,
+ TALER_EXCHANGE_CsRWithdrawCallback res_cb,
+ void *res_cb_cls)
{
struct TALER_EXCHANGE_CsRWithdrawHandle *csrh;
@@ -218,11 +215,11 @@ TALER_EXCHANGE_csr_withdraw (struct TALER_EXCHANGE_Handle *exchange,
return NULL;
}
csrh = GNUNET_new (struct TALER_EXCHANGE_CsRWithdrawHandle);
- csrh->exchange = exchange;
csrh->cb = res_cb;
csrh->cb_cls = res_cb_cls;
- csrh->url = TEAH_path_to_url (exchange,
- "/csr-withdraw");
+ csrh->url = TALER_url_join (exchange_url,
+ "csr-withdraw",
+ NULL);
if (NULL == csrh->url)
{
GNUNET_free (csrh);
@@ -231,7 +228,6 @@ TALER_EXCHANGE_csr_withdraw (struct TALER_EXCHANGE_Handle *exchange,
{
CURL *eh;
- struct GNUNET_CURL_Context *ctx;
json_t *req;
req = GNUNET_JSON_PACK (
@@ -242,7 +238,6 @@ TALER_EXCHANGE_csr_withdraw (struct TALER_EXCHANGE_Handle *exchange,
&pk->h_key,
sizeof(struct TALER_DenominationHashP)));
GNUNET_assert (NULL != req);
- ctx = TEAH_handle_to_context (exchange);
eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url);
if ( (NULL == eh) ||
(GNUNET_OK !=
@@ -259,7 +254,7 @@ TALER_EXCHANGE_csr_withdraw (struct TALER_EXCHANGE_Handle *exchange,
return NULL;
}
json_decref (req);
- csrh->job = GNUNET_CURL_job_add2 (ctx,
+ csrh->job = GNUNET_CURL_job_add2 (curl_ctx,
eh,
csrh->post_ctx.headers,
&handle_csr_finished,
diff --git a/src/lib/exchange_api_curl_defaults.h b/src/lib/exchange_api_curl_defaults.h
index 009d72ab8..c4ba04fc5 100644
--- a/src/lib/exchange_api_curl_defaults.h
+++ b/src/lib/exchange_api_curl_defaults.h
@@ -25,7 +25,6 @@
#define _TALER_CURL_DEFAULTS_H
-#include "platform.h"
#include <gnunet/gnunet_curl_lib.h>
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index fe73f0504..0bb3c2085 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -1229,6 +1229,8 @@ TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange,
bool force_download = 0 != (flags & TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
bool pull_all_keys = 0 != (flags & TALER_EXCHANGE_CKF_PULL_ALL_KEYS);
+ GNUNET_assert (NULL != exchange);
+
if ( (NULL != cb) &&
( (exchange->cert_cb != cb) ||
(exchange->cert_cb_cls != cb_cls) ) )
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
index 1da4184d2..f19f98c54 100644
--- a/src/lib/exchange_api_melt.c
+++ b/src/lib/exchange_api_melt.c
@@ -504,6 +504,7 @@ csr_cb (void *cls,
}
+/* FIXME: refactor this to use struct TALER_EXCHANGE_Handle */
struct TALER_EXCHANGE_MeltHandle *
TALER_EXCHANGE_melt (
struct GNUNET_CURL_Context *ctx,
diff --git a/src/lib/exchange_api_refreshes_reveal.c b/src/lib/exchange_api_refreshes_reveal.c
index 9b6f9b19c..e698a2977 100644
--- a/src/lib/exchange_api_refreshes_reveal.c
+++ b/src/lib/exchange_api_refreshes_reveal.c
@@ -303,6 +303,7 @@ handle_refresh_reveal_finished (void *cls,
}
+/* FIXME: refactor this to use struct TALER_EXCHANGE_Handle */
struct TALER_EXCHANGE_RefreshesRevealHandle *
TALER_EXCHANGE_refreshes_reveal (
struct GNUNET_CURL_Context *ctx,
diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c
index 2a3c095a6..87218989a 100644
--- a/src/lib/exchange_api_withdraw.c
+++ b/src/lib/exchange_api_withdraw.c
@@ -39,9 +39,19 @@ struct TALER_EXCHANGE_WithdrawHandle
{
/**
- * The connection to exchange this request handle will use
+ * The curl context to use
*/
- struct TALER_EXCHANGE_Handle *exchange;
+ struct GNUNET_CURL_Context *curl_ctx;
+
+ /**
+ * The base-URL to the exchange
+ */
+ const char *exchange_url;
+
+ /**
+ * The /keys material from the exchange
+ */
+ struct TALER_EXCHANGE_Keys *keys;
/**
* Handle for the actual (internal) withdraw operation.
@@ -232,7 +242,9 @@ withdraw_cs_stage_two_callback (
GNUNET_break (0);
break;
}
- wh->wh2 = TALER_EXCHANGE_withdraw2 (wh->exchange,
+ wh->wh2 = TALER_EXCHANGE_withdraw2 (wh->curl_ctx,
+ wh->exchange_url,
+ wh->keys,
&wh->pd,
wh->reserve_priv,
&handle_reserve_withdraw_finished,
@@ -249,7 +261,9 @@ withdraw_cs_stage_two_callback (
struct TALER_EXCHANGE_WithdrawHandle *
TALER_EXCHANGE_withdraw (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ struct TALER_EXCHANGE_Keys *keys,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_EXCHANGE_WithdrawCoinInput *wci,
TALER_EXCHANGE_WithdrawCallback res_cb,
@@ -258,7 +272,9 @@ TALER_EXCHANGE_withdraw (
struct TALER_EXCHANGE_WithdrawHandle *wh;
wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
- wh->exchange = exchange;
+ wh->keys = TALER_EXCHANGE_keys_incref (keys);
+ wh->exchange_url = exchange_url;
+ wh->curl_ctx = curl_ctx;
wh->cb = res_cb;
wh->cb_cls = res_cb_cls;
wh->reserve_priv = reserve_priv;
@@ -292,7 +308,9 @@ TALER_EXCHANGE_withdraw (
GNUNET_free (wh);
return NULL;
}
- wh->wh2 = TALER_EXCHANGE_withdraw2 (exchange,
+ wh->wh2 = TALER_EXCHANGE_withdraw2 (curl_ctx,
+ exchange_url,
+ keys,
&wh->pd,
wh->reserve_priv,
&handle_reserve_withdraw_finished,
@@ -309,7 +327,8 @@ TALER_EXCHANGE_withdraw (
will be done after the /csr-withdraw request! */
wh->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
wh->csrh = TALER_EXCHANGE_csr_withdraw (
- exchange,
+ curl_ctx,
+ exchange_url,
&wh->pk,
&wh->pd.blinded_planchet.details.cs_blinded_planchet.nonce,
&withdraw_cs_stage_two_callback,
@@ -339,6 +358,7 @@ TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
TALER_EXCHANGE_withdraw2_cancel (wh->wh2);
wh->wh2 = NULL;
}
+ TALER_EXCHANGE_keys_decref (wh->keys);
TALER_denom_pub_free (&wh->pk.key);
GNUNET_free (wh);
}
diff --git a/src/lib/exchange_api_withdraw2.c b/src/lib/exchange_api_withdraw2.c
index daac429c7..11028ed5b 100644
--- a/src/lib/exchange_api_withdraw2.c
+++ b/src/lib/exchange_api_withdraw2.c
@@ -39,9 +39,9 @@ struct TALER_EXCHANGE_Withdraw2Handle
{
/**
- * The connection to exchange this request handle will use
+ * The /keys material from the exchange
*/
- struct TALER_EXCHANGE_Handle *exchange;
+ struct TALER_EXCHANGE_Keys *keys;
/**
* The url for this request.
@@ -192,8 +192,7 @@ reserve_withdraw_payment_required (
}
if (GNUNET_OK !=
- TALER_EXCHANGE_parse_reserve_history (TALER_EXCHANGE_get_keys (
- wh->exchange),
+ TALER_EXCHANGE_parse_reserve_history (wh->keys,
history,
&wh->reserve_pub,
balance.currency,
@@ -361,25 +360,21 @@ handle_reserve_withdraw_finished (void *cls,
struct TALER_EXCHANGE_Withdraw2Handle *
TALER_EXCHANGE_withdraw2 (
- struct TALER_EXCHANGE_Handle *exchange,
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ struct TALER_EXCHANGE_Keys *keys,
const struct TALER_PlanchetDetail *pd,
const struct TALER_ReservePrivateKeyP *reserve_priv,
TALER_EXCHANGE_Withdraw2Callback res_cb,
void *res_cb_cls)
{
struct TALER_EXCHANGE_Withdraw2Handle *wh;
- const struct TALER_EXCHANGE_Keys *keys;
const struct TALER_EXCHANGE_DenomPublicKey *dk;
struct TALER_ReserveSignatureP reserve_sig;
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
struct TALER_BlindedCoinHashP bch;
- keys = TALER_EXCHANGE_get_keys (exchange);
- if (NULL == keys)
- {
- GNUNET_break (0);
- return NULL;
- }
+ GNUNET_assert (NULL != keys);
dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
&pd->denom_pub_hash);
if (NULL == dk)
@@ -388,7 +383,7 @@ TALER_EXCHANGE_withdraw2 (
return NULL;
}
wh = GNUNET_new (struct TALER_EXCHANGE_Withdraw2Handle);
- wh->exchange = exchange;
+ wh->keys = TALER_EXCHANGE_keys_incref (keys);
wh->cb = res_cb;
wh->cb_cls = res_cb_cls;
/* Compute how much we expected to charge to the reserve */
@@ -418,7 +413,7 @@ TALER_EXCHANGE_withdraw2 (
*end = '\0';
GNUNET_snprintf (arg_str,
sizeof (arg_str),
- "/reserves/%s/withdraw",
+ "reserves/%s/withdraw",
pub_str);
}
@@ -448,8 +443,9 @@ TALER_EXCHANGE_withdraw2 (
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Attempting to withdraw from reserve %s\n",
TALER_B2S (&wh->reserve_pub));
- wh->url = TEAH_path_to_url (exchange,
- arg_str);
+ wh->url = TALER_url_join (exchange_url,
+ arg_str,
+ NULL);
if (NULL == wh->url)
{
json_decref (withdraw_obj);
@@ -458,9 +454,7 @@ TALER_EXCHANGE_withdraw2 (
}
{
CURL *eh;
- struct GNUNET_CURL_Context *ctx;
- ctx = TEAH_handle_to_context (exchange);
eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
if ( (NULL == eh) ||
(GNUNET_OK !=
@@ -477,7 +471,7 @@ TALER_EXCHANGE_withdraw2 (
return NULL;
}
json_decref (withdraw_obj);
- wh->job = GNUNET_CURL_job_add2 (ctx,
+ wh->job = GNUNET_CURL_job_add2 (curl_ctx,
eh,
wh->post_ctx.headers,
&handle_reserve_withdraw_finished,
@@ -498,5 +492,6 @@ TALER_EXCHANGE_withdraw2_cancel (struct TALER_EXCHANGE_Withdraw2Handle *wh)
}
GNUNET_free (wh->url);
TALER_curl_easy_post_finished (&wh->post_ctx);
+ TALER_EXCHANGE_keys_decref (wh->keys);
GNUNET_free (wh);
}
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c
index e0b8285ae..9283f0325 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -321,12 +321,15 @@ batch_withdraw_run (void *cls,
wci->ps = &cs->ps;
wci->ach = cs->h_age_commitment;
}
- ws->wsh = TALER_EXCHANGE_batch_withdraw (exchange,
- rp,
- wcis,
- ws->num_coins,
- &reserve_batch_withdraw_cb,
- ws);
+ ws->wsh = TALER_EXCHANGE_batch_withdraw (
+ TALER_TESTING_interpreter_get_context (is),
+ TALER_TESTING_get_exchange_url (is),
+ TALER_TESTING_get_keys (is),
+ rp,
+ wcis,
+ ws->num_coins,
+ &reserve_batch_withdraw_cb,
+ ws);
if (NULL == ws->wsh)
{
GNUNET_break (0);
diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c
index 8188eeae9..a6315f91e 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -410,7 +410,7 @@ withdraw_run (void *cls,
if (NULL == ws->pk)
{
- dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
+ dpk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (is),
&ws->amount,
ws->age > 0);
if (NULL == dpk)
@@ -443,11 +443,14 @@ withdraw_run (void *cls,
.ps = &ws->ps,
.ach = ws->h_age_commitment
};
- ws->wsh = TALER_EXCHANGE_withdraw (exchange,
- rp,
- &wci,
- &reserve_withdraw_cb,
- ws);
+ ws->wsh = TALER_EXCHANGE_withdraw (
+ TALER_TESTING_interpreter_get_context (is),
+ TALER_TESTING_get_exchange_url (is),
+ TALER_TESTING_get_keys (is),
+ rp,
+ &wci,
+ &reserve_withdraw_cb,
+ ws);
}
if (NULL == ws->wsh)
{
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index 6f8ebdaff..3aa464aa7 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -413,6 +413,34 @@ TALER_exchange_online_age_withdraw_confirmation_sign (
}
+enum GNUNET_GenericReturnValue
+TALER_exchange_online_age_withdraw_confirmation_verify (
+ const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
+ uint32_t noreveal_index,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const struct TALER_ExchangeSignatureP *exchange_sig)
+{
+ struct TALER_AgeWithdrawConfirmationPS confirm = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW),
+ .purpose.size = htonl (sizeof (confirm)),
+ .h_commitment = *h_commitment,
+ .noreveal_index = htonl (noreveal_index)
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (
+ TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW,
+ &confirm,
+ &exchange_sig->eddsa_signature,
+ &exchange_pub->eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
/* TODO:oec: add signature for age-withdraw, age-reveal */
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index 34d097d77..823641ed4 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -22,6 +22,7 @@
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
+#include <gnunet/gnunet_common.h>
GNUNET_NETWORK_STRUCT_BEGIN
@@ -621,9 +622,9 @@ struct TALER_AgeWithdrawRequestPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
- * Hash of the commitment of n*kappa coins
+ * The reserve's public key
*/
- struct TALER_AgeWithdrawCommitmentHashP h_commitment GNUNET_PACKED;
+ struct TALER_ReservePublicKeyP reserve_pub;
/**
* Value of the coin being exchanged (matching the denomination key)
@@ -635,7 +636,12 @@ struct TALER_AgeWithdrawRequestPS
struct TALER_AmountNBO amount_with_fee;
/**
- * The mask that defines the age groups
+ * Running SHA512 hash of the commitment of n*kappa coins
+ */
+ struct TALER_AgeWithdrawCommitmentHashP h_commitment;
+
+ /**
+ * The mask that defines the age groups. MUST be the same for all denominations.
*/
struct TALER_AgeMask mask;
@@ -665,6 +671,8 @@ TALER_wallet_age_withdraw_sign (
.max_age_group = TALER_get_age_group (mask, max_age)
};
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &req.reserve_pub.eddsa_pub);
TALER_amount_hton (&req.amount_with_fee,
amount_with_fee);
GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
@@ -685,6 +693,7 @@ TALER_wallet_age_withdraw_verify (
struct TALER_AgeWithdrawRequestPS awsrd = {
.purpose.size = htonl (sizeof (awsrd)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW),
+ .reserve_pub = *reserve_pub,
.h_commitment = *h_commitment,
.mask = *mask,
.max_age_group = TALER_get_age_group (mask, max_age)