aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/exchange-template/config/exchange-keyup.conf4
-rw-r--r--doc/taler.conf.52
-rw-r--r--src/bank-lib/test_bank_api.c3
-rw-r--r--src/exchange-lib/exchange_api_handle.c26
-rw-r--r--src/exchange-lib/exchange_api_refund.c335
-rw-r--r--src/exchange-lib/test_exchange_api.conf5
-rw-r--r--src/exchange-tools/coins.conf1
-rw-r--r--src/exchange-tools/taler-auditor-sign.c6
-rw-r--r--src/exchange-tools/taler-exchange-keyup.c27
-rw-r--r--src/exchange/Makefile.am1
-rw-r--r--src/exchange/taler-exchange-httpd.c9
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c112
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.h10
-rw-r--r--src/exchange/taler-exchange-httpd_keystate.c9
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c146
-rw-r--r--src/exchange/taler-exchange-httpd_refund.h52
-rw-r--r--src/exchange/test_taler_exchange_aggregator.c3
-rw-r--r--src/exchange/test_taler_exchange_httpd.conf3
-rwxr-xr-xsrc/exchange/test_taler_exchange_httpd.sh4
-rw-r--r--src/exchangedb/perf_taler_exchangedb.c3
-rw-r--r--src/exchangedb/perf_taler_exchangedb_init.c4
-rw-r--r--src/exchangedb/perf_taler_exchangedb_init.h25
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c20
-rw-r--r--src/exchangedb/test_exchangedb.c25
-rw-r--r--src/include/taler_exchange_service.h86
-rw-r--r--src/include/taler_exchangedb_plugin.h61
-rw-r--r--src/include/taler_signatures.h71
27 files changed, 924 insertions, 129 deletions
diff --git a/contrib/exchange-template/config/exchange-keyup.conf b/contrib/exchange-template/config/exchange-keyup.conf
index 7eebfe975..382e121ce 100644
--- a/contrib/exchange-template/config/exchange-keyup.conf
+++ b/contrib/exchange-template/config/exchange-keyup.conf
@@ -28,6 +28,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_5]
@@ -39,6 +40,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_10]
@@ -50,6 +52,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_1000]
@@ -61,4 +64,5 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
rsa_keysize = 2048
diff --git a/doc/taler.conf.5 b/doc/taler.conf.5
index 1d03927ea..7d9a4e92a 100644
--- a/doc/taler.conf.5
+++ b/doc/taler.conf.5
@@ -67,6 +67,8 @@ The following options must be in sections starting with "[coin_]" and are used b
What fee is charged for depositing?
.IP FEE_REFRESH
What fee is charged for refreshing?
+.IP FEE_REFUND
+ What fee is charged for refunds? When a coin is refunded, the deposit fee is returned. Instead, the refund fee is charged to the customer.
.IP RSA_KEYSIZE
What is the RSA keysize modulos (in bits)?
diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c
index 1b695f457..fe02fb8e5 100644
--- a/src/bank-lib/test_bank_api.c
+++ b/src/bank-lib/test_bank_api.c
@@ -520,7 +520,8 @@ main (int argc,
NULL, NULL, NULL,
"taler-bank-manage",
"taler-bank-manage",
- "serve-http", "--port", "8081",
+ "serve-http",
+ "--port", "8081",
NULL);
if (NULL == bankd)
{
diff --git a/src/exchange-lib/exchange_api_handle.c b/src/exchange-lib/exchange_api_handle.c
index bd4746565..47658052d 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -253,29 +253,32 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
struct TALER_Amount fee_withdraw;
struct TALER_Amount fee_deposit;
struct TALER_Amount fee_refresh;
+ struct TALER_Amount fee_refund;
struct TALER_DenominationKeyValidityPS denom_key_issue;
struct GNUNET_CRYPTO_RsaPublicKey *pk;
struct GNUNET_CRYPTO_EddsaSignature sig;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig",
- &sig),
+ &sig),
GNUNET_JSON_spec_absolute_time ("stamp_expire_deposit",
- &deposit_valid_until),
+ &deposit_valid_until),
GNUNET_JSON_spec_absolute_time ("stamp_expire_withdraw",
- &withdraw_valid_until),
+ &withdraw_valid_until),
GNUNET_JSON_spec_absolute_time ("stamp_start",
- &valid_from),
+ &valid_from),
GNUNET_JSON_spec_absolute_time ("stamp_expire_legal",
- &expire_legal),
+ &expire_legal),
TALER_JSON_spec_amount ("value",
- &value),
+ &value),
TALER_JSON_spec_amount ("fee_withdraw",
- &fee_withdraw),
+ &fee_withdraw),
TALER_JSON_spec_amount ("fee_deposit",
- &fee_deposit),
+ &fee_deposit),
TALER_JSON_spec_amount ("fee_refresh",
- &fee_refresh),
+ &fee_refresh),
+ TALER_JSON_spec_amount ("fee_refund",
+ &fee_refund),
GNUNET_JSON_spec_rsa_public_key ("denom_pub",
&pk),
GNUNET_JSON_spec_end()
@@ -309,6 +312,8 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
&fee_deposit);
TALER_amount_hton (&denom_key_issue.fee_refresh,
&fee_refresh);
+ TALER_amount_hton (&denom_key_issue.fee_refund,
+ &fee_refund);
EXITIF (GNUNET_SYSERR ==
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
&denom_key_issue.purpose,
@@ -327,6 +332,7 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
denom_key->fee_withdraw = fee_withdraw;
denom_key->fee_deposit = fee_deposit;
denom_key->fee_refresh = fee_refresh;
+ denom_key->fee_refund = fee_refund;
return GNUNET_OK;
EXITIF_exit:
@@ -435,6 +441,8 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
&dk->fee_deposit);
TALER_amount_hton (&kv.fee_refresh,
&dk->fee_refresh);
+ TALER_amount_hton (&kv.fee_refund,
+ &dk->fee_refund);
kv.denom_hash = dk->h_key;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS,
diff --git a/src/exchange-lib/exchange_api_refund.c b/src/exchange-lib/exchange_api_refund.c
new file mode 100644
index 000000000..ade070093
--- /dev/null
+++ b/src/exchange-lib/exchange_api_refund.c
@@ -0,0 +1,335 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/exchange_api_refund.c
+ * @brief Implementation of the /refund request of the exchange's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.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_common.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+
+
+/**
+ * @brief A Refund Handle
+ */
+struct TALER_EXCHANGE_RefundHandle
+{
+
+ /**
+ * The connection to exchange this request handle will use
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * JSON encoding of the request to POST.
+ */
+ char *json_enc;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_EXCHANGE_RefundResultCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Information the exchange should sign in response.
+ */
+ struct TALER_RefundConfirmationPS depconf;
+
+};
+
+
+/**
+ * Verify that the signature on the "200 OK" response
+ * from the exchange is valid.
+ *
+ * @param rh refund handle
+ * @param json json reply with the signature
+ * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_refund_signature_ok (const struct TALER_EXCHANGE_RefundHandle *rh,
+ const json_t *json)
+{
+ struct TALER_ExchangeSignatureP exchange_sig;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ const struct TALER_EXCHANGE_Keys *key_state;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig),
+ GNUNET_JSON_spec_fixed_auto ("pub", &exchange_pub),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ key_state = TALER_EXCHANGE_get_keys (rh->exchange);
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_test_signing_key (key_state,
+ &exchange_pub))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND,
+ &rh->depconf.purpose,
+ &exchange_sig.eddsa_signature,
+ &exchange_pub.eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /refund request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_RefundHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param json parsed JSON result, NULL on error
+ */
+static void
+handle_refund_finished (void *cls,
+ long response_code,
+ const json_t *json)
+{
+ struct TALER_EXCHANGE_RefundHandle *rh = cls;
+
+ rh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ verify_refund_signature_ok (rh,
+ json))
+ {
+ 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_UNAUTHORIZED:
+ /* 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, 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",
+ response_code);
+ GNUNET_break (0);
+ response_code = 0;
+ break;
+ }
+ rh->cb (rh->cb_cls,
+ response_code,
+ json);
+ TALER_EXCHANGE_refund_cancel (rh);
+}
+
+
+/**
+ * Submit a refund request to the exchange and get the exchange's
+ * response. This API is used by a merchant. Note that
+ * while we return the response verbatim 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). If
+ * the exchange's reply is not well-formed, we return an HTTP status code
+ * of zero to @a cb.
+ *
+ * The @a exchange must be ready to operate (i.e. have
+ * finished processing the /keys reply). If this check fails, we do
+ * NOT initiate the transaction with the exchange and instead return NULL.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param amount the amount to be refunded; must be larger than the refund fee
+ * (as that fee is still being subtracted), and smaller than the amount
+ * (with deposit fee) of the original deposit contribution of this coin
+ * @param refund_fee fee applicable to this coin for the refund
+ * @param h_contract hash of the contact of the merchant with the customer that is being refunded
+ * @param transaction_id transaction id for the transaction being refunded, must match @a h_contract
+ * @param coin_pub coin’s public key of the coin from the original deposit operation
+ * @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
+ * this is needed as we may first do a partial refund and later a full refund. If both
+ * refunds are also over the same amount, we need the @a rtransaction_id to make the disjoint
+ * refund requests different (as requests are idempotent and otherwise the 2nd refund might not work).
+ * @param merchant_priv the private key of the merchant, used to generate signature for refund request
+ * @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_RefundHandle *
+TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
+ const struct TALER_Amount *amount,
+ const struct TALER_Amount *refund_fee,
+ const struct GNUNET_HashCode *h_contract,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t rtransaction_id,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ TALER_EXCHANGE_RefundResultCallback cb,
+ void *cb_cls)
+{
+ struct TALER_EXCHANGE_RefundHandle *rh;
+ struct GNUNET_CURL_Context *ctx;
+ struct TALER_MerchantSignatureP merchant_sig;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ json_t *refund_obj;
+ CURL *eh;
+
+ if (GNUNET_YES !=
+ MAH_handle_is_ready (exchange))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ /* FIXME: create signature! */
+
+ refund_obj = json_pack ("{s:o, s:o," /* amount/fee */
+ " s:o, s:o," /* H_contract, coin_pub */
+ " s:I, s:I," /* transaction id, rtransaction id */
+ " s:o, s:o}", /* merchant_pub, merchant_sig */
+ "amount", TALER_JSON_from_amount (amount),
+ "fee", TALER_JSON_from_amount (amount),
+ "H_contract", GNUNET_JSON_from_data (h_contract,
+ sizeof (struct GNUNET_HashCode)),
+ "coin_pub", GNUNET_JSON_from_data (coin_pub,
+ sizeof (*coin_pub)),
+ "transaction_id", (json_int_t) transaction_id,
+ "rtransaction_id", (json_int_t) rtransaction_id,
+ "merchant_pub", GNUNET_JSON_from_data (&merchant_pub,
+ sizeof (merchant_pub)),
+ "merchant_sig", GNUNET_JSON_from_data (&merchant_sig,
+ sizeof (merchant_sig))
+ );
+
+ rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle);
+ rh->exchange = exchange;
+ rh->cb = cb;
+ rh->cb_cls = cb_cls;
+ rh->url = MAH_path_to_url (exchange, "/refund");
+ rh->depconf.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
+ rh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
+ rh->depconf.h_contract = *h_contract;
+ rh->depconf.transaction_id = GNUNET_htonll (transaction_id);
+ rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id);
+ TALER_amount_hton (&rh->depconf.refund_amount,
+ amount);
+ TALER_amount_hton (&rh->depconf.refund_fee,
+ refund_fee);
+ rh->depconf.coin_pub = *coin_pub;
+ rh->depconf.merchant = *merchant_pub;
+
+ eh = curl_easy_init ();
+ GNUNET_assert (NULL != (rh->json_enc =
+ json_dumps (refund_obj,
+ JSON_COMPACT)));
+ json_decref (refund_obj);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "URL for refund: `%s'\n",
+ rh->url);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ rh->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDS,
+ rh->json_enc));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDSIZE,
+ strlen (rh->json_enc)));
+ ctx = MAH_handle_to_context (exchange);
+ rh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ GNUNET_YES,
+ &handle_refund_finished,
+ rh);
+ return rh;
+}
+
+
+/**
+ * Cancel a refund permission request. This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param refund the refund permission request handle
+ */
+void
+TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund)
+{
+ if (NULL != refund->job)
+ {
+ GNUNET_CURL_job_cancel (refund->job);
+ refund->job = NULL;
+ }
+ GNUNET_free (refund->url);
+ GNUNET_free (refund->json_enc);
+ GNUNET_free (refund);
+}
+
+
+/* end of exchange_api_refund.c */
diff --git a/src/exchange-lib/test_exchange_api.conf b/src/exchange-lib/test_exchange_api.conf
index 3c68f6050..5fcc36552 100644
--- a/src/exchange-lib/test_exchange_api.conf
+++ b/src/exchange-lib/test_exchange_api.conf
@@ -50,6 +50,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_ct_10]
@@ -61,6 +62,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_1]
@@ -72,6 +74,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_5]
@@ -83,6 +86,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_10]
@@ -94,4 +98,5 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
rsa_keysize = 1024
diff --git a/src/exchange-tools/coins.conf b/src/exchange-tools/coins.conf
index 90bf7064c..a0a160773 100644
--- a/src/exchange-tools/coins.conf
+++ b/src/exchange-tools/coins.conf
@@ -22,4 +22,5 @@
# fee_withdraw = EUR:0.00
# fee_deposit = EUR:0.00
# fee_refresh = EUR:0.01
+# fee_refund = EUR:0.01
# rsa_keysize = 1024
diff --git a/src/exchange-tools/taler-auditor-sign.c b/src/exchange-tools/taler-auditor-sign.c
index d5a3a9403..71c80f5ee 100644
--- a/src/exchange-tools/taler-auditor-sign.c
+++ b/src/exchange-tools/taler-auditor-sign.c
@@ -103,6 +103,12 @@ print_dk (const struct TALER_DenominationKeyValidityPS *dk)
"Refresh fee: %s\n",
s = TALER_amount_to_string (&a));
GNUNET_free (s);
+ TALER_amount_ntoh (&a,
+ &dk->fee_refund);
+ fprintf (stdout,
+ "Refund fee: %s\n",
+ s = TALER_amount_to_string (&a));
+ GNUNET_free (s);
fprintf (stdout,
"Validity start time: %s\n",
diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c
index 312b4010e..0cd9a30ca 100644
--- a/src/exchange-tools/taler-exchange-keyup.c
+++ b/src/exchange-tools/taler-exchange-keyup.c
@@ -79,6 +79,11 @@ struct CoinTypeNBOP
struct TALER_AmountNBO fee_refresh;
/**
+ * What is the fee charged for refunds?
+ */
+ struct TALER_AmountNBO fee_refund;
+
+ /**
* Key size in NBO.
*/
uint32_t rsa_keysize;
@@ -141,6 +146,11 @@ struct CoinTypeParams
struct TALER_Amount fee_refresh;
/**
+ * What is the fee charged for refunds?
+ */
+ struct TALER_Amount fee_refund;
+
+ /**
* Time at which this coin is supposed to become valid.
*/
struct GNUNET_TIME_Absolute anchor;
@@ -261,6 +271,8 @@ hash_coin_type (const struct CoinTypeParams *p,
&p->fee_deposit);
TALER_amount_hton (&p_nbo.fee_refresh,
&p->fee_refresh);
+ TALER_amount_hton (&p_nbo.fee_refund,
+ &p->fee_refund);
p_nbo.rsa_keysize = htonl (p->rsa_keysize);
GNUNET_CRYPTO_hash (&p_nbo,
sizeof (struct CoinTypeNBOP),
@@ -707,6 +719,17 @@ get_cointype_params (const char *ct,
"fee_refresh");
return GNUNET_SYSERR;
}
+ if (GNUNET_OK !=
+ TALER_config_get_denom (kcfg,
+ ct,
+ "fee_refund",
+ &params->fee_refund))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "fee_refund");
+ return GNUNET_SYSERR;
+ }
dir = get_cointype_dir (params);
get_anchor (dir,
@@ -756,6 +779,8 @@ create_denomkey_issue (const struct CoinTypeParams *params,
&params->fee_deposit);
TALER_amount_hton (&dki->issue.properties.fee_refresh,
&params->fee_refresh);
+ TALER_amount_hton (&dki->issue.properties.fee_refund,
+ &params->fee_refund);
dki->issue.properties.purpose.purpose
= htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
dki->issue.properties.purpose.size
@@ -776,7 +801,7 @@ create_denomkey_issue (const struct CoinTypeParams *params,
*/
static void
exchange_keys_update_cointype (void *cls,
- const char *coin_alias)
+ const char *coin_alias)
{
int *ret = cls;
struct CoinTypeParams p;
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 8dcd2e689..3dba0bb77 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -35,6 +35,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
taler-exchange-httpd_parsing.c taler-exchange-httpd_parsing.h \
taler-exchange-httpd_refresh.c taler-exchange-httpd_refresh.h \
+ taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
taler-exchange-httpd_reserve.c taler-exchange-httpd_reserve.h \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
taler-exchange-httpd_tracking.c taler-exchange-httpd_tracking.h \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 46e545e50..6efb1492e 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -30,6 +30,7 @@
#include "taler-exchange-httpd_mhd.h"
#include "taler-exchange-httpd_admin.h"
#include "taler-exchange-httpd_deposit.h"
+#include "taler-exchange-httpd_refund.h"
#include "taler-exchange-httpd_reserve.h"
#include "taler-exchange-httpd_wire.h"
#include "taler-exchange-httpd_refresh.h"
@@ -200,6 +201,14 @@ handle_mhd_request (void *cls,
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+ /* Refunding coins */
+ { "/refund", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &TMH_REFUND_handler_refund, MHD_HTTP_OK },
+ { "/refund", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
+
/* Dealing with change */
{ "/refresh/melt", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index efef3d04e..cf5142982 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -21,9 +21,6 @@
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
- *
- * TODO:
- * - ugly if-construction for deposit type
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@@ -119,28 +116,37 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
/**
- * Handle a "/deposit" request. This function parses the
- * JSON information and then calls #verify_and_execute_deposit()
- * to verify the signatures and execute the deposit.
+ * Handle a "/deposit" request. Parses the JSON, and, if successful,
+ * passes the JSON data to #verify_and_execute_deposit() to further
+ * check the details of the operation specified. If everything checks
+ * out, this will ultimately lead to the "/deposit" being executed, or
+ * rejected.
*
+ * @param rh context of the handler
* @param connection the MHD connection to handle
- * @param root root of the posted JSON
- * @param amount how much should be deposited
- * @param wire json describing the wire details (?)
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
-static int
-parse_and_handle_deposit_request (struct MHD_Connection *connection,
- const json_t *root,
- const struct TALER_Amount *amount,
- json_t *wire)
+int
+TMH_DEPOSIT_handler_deposit (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
{
+ json_t *json;
int res;
+ json_t *wire;
struct TALER_EXCHANGEDB_Deposit deposit;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TMH_KS_StateHandle *ks;
struct GNUNET_HashCode my_h_wire;
+ struct TALER_Amount amount;
struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("wire", &wire),
+ TALER_JSON_spec_amount ("f", &amount),
TALER_JSON_spec_denomination_public_key ("denom_pub", &deposit.coin.denom_pub),
TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub),
@@ -155,10 +161,20 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
GNUNET_JSON_spec_end ()
};
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &json);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == json) )
+ return MHD_YES;
memset (&deposit, 0, sizeof (deposit));
res = TMH_PARSE_json_data (connection,
- root,
+ json,
spec);
+ json_decref (json);
if (GNUNET_SYSERR == res)
return MHD_NO; /* hard failure */
if (GNUNET_NO == res)
@@ -205,12 +221,12 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
&dki->issue.properties.fee_deposit);
TMH_KS_release (ks);
deposit.wire = wire;
- deposit.amount_with_fee = *amount;
+ deposit.amount_with_fee = amount;
if (-1 == TALER_amount_cmp (&deposit.amount_with_fee,
&deposit.deposit_fee))
{
/* Total amount smaller than fee, invalid */
- GNUNET_JSON_parse_free (spec);
+ GNUNET_JSON_parse_free (spec);
return TMH_RESPONSE_reply_arg_invalid (connection,
"f");
}
@@ -221,64 +237,4 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
}
-/**
- * Handle a "/deposit" request. Parses the JSON in the post to find
- * the "type" (either DIRECT_DEPOSIT or INCREMENTAL_DEPOSIT), and, if
- * successful, passes the JSON data to
- * #parse_and_handle_deposit_request() to further check the details
- * of the operation specified in the "wire" field of the JSON data.
- * If everything checks out, this will ultimately lead to the
- * "/deposit" being executed, or rejected.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TMH_DEPOSIT_handler_deposit (struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size)
-{
- json_t *json;
- json_t *wire;
- int res;
- struct TALER_Amount amount;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("wire", &wire),
- TALER_JSON_spec_amount ("f", &amount),
- GNUNET_JSON_spec_end ()
- };
-
- res = TMH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &json);
- if (GNUNET_SYSERR == res)
- return MHD_NO;
- if ( (GNUNET_NO == res) || (NULL == json) )
- return MHD_YES;
- res = TMH_PARSE_json_data (connection,
- json,
- spec);
- if (GNUNET_OK != res)
- {
- json_decref (json);
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- }
- res = parse_and_handle_deposit_request (connection,
- json,
- &amount,
- wire);
- GNUNET_JSON_parse_free (spec);
- json_decref (json);
- return res;
-}
-
-
/* end of taler-exchange-httpd_deposit.c */
diff --git a/src/exchange/taler-exchange-httpd_deposit.h b/src/exchange/taler-exchange-httpd_deposit.h
index 6e84573e5..89f1ca56c 100644
--- a/src/exchange/taler-exchange-httpd_deposit.h
+++ b/src/exchange/taler-exchange-httpd_deposit.h
@@ -29,13 +29,9 @@
/**
- * Handle a "/deposit" request. Parses the JSON in the post to find
- * the "type" (either DIRECT_DEPOSIT or INCREMENTAL_DEPOSIT), and, if
- * successful, passes the JSON data to
- * #parse_and_handle_deposit_request() to further check the details
- * of the operation specified in the "wire" field of the JSON data.
- * If everything checks out, this will ultimately lead to the
- * "/deposit" being executed, or rejected.
+ * Handle a "/deposit" request. Parses the JSON, and, if successful,
+ * checks the signatures. If everything checks out, this will
+ * ultimately lead to the "/deposit" being executed, or rejected.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c
index c9db50895..e16d3a62e 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -133,6 +133,7 @@ denom_key_issue_to_json (const struct TALER_DenominationPublicKey *pk,
struct TALER_Amount fee_withdraw;
struct TALER_Amount fee_deposit;
struct TALER_Amount fee_refresh;
+ struct TALER_Amount fee_refund;
TALER_amount_ntoh (&value,
&dki->properties.value);
@@ -142,8 +143,10 @@ denom_key_issue_to_json (const struct TALER_DenominationPublicKey *pk,
&dki->properties.fee_deposit);
TALER_amount_ntoh (&fee_refresh,
&dki->properties.fee_refresh);
+ TALER_amount_ntoh (&fee_refund,
+ &dki->properties.fee_refund);
return
- json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
+ json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
"master_sig",
GNUNET_JSON_from_data (&dki->signature,
sizeof (struct GNUNET_CRYPTO_EddsaSignature)),
@@ -164,7 +167,9 @@ denom_key_issue_to_json (const struct TALER_DenominationPublicKey *pk,
"fee_deposit",
TALER_JSON_from_amount (&fee_deposit),
"fee_refresh",
- TALER_JSON_from_amount (&fee_refresh));
+ TALER_JSON_from_amount (&fee_refresh),
+ "fee_refund",
+ TALER_JSON_from_amount (&fee_refund));
}
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
new file mode 100644
index 000000000..c5f24000d
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -0,0 +1,146 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refund.c
+ * @brief Handle /refund requests; parses the POST and JSON and
+ * verifies the coin signature before handing things off
+ * to the database.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_validation.h"
+
+
+/**
+ * We have parsed the JSON information about the refund, do some basic
+ * sanity checks (especially that the signature on the coin is valid)
+ * and then execute the refund. Note that we need the DB to check
+ * the fee structure, so this is not done here.
+ *
+ * @param connection the MHD connection to handle
+ * @param refund information about the refund
+ * @return MHD result code
+ */
+static int
+verify_and_execute_refund (struct MHD_Connection *connection,
+ const struct TALER_EXCHANGEDB_Refund *refund)
+{
+ struct TALER_RefundRequestPS dr;
+
+ dr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
+ dr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
+ dr.h_contract = refund->h_contract;
+ dr.transaction_id = GNUNET_htonll (refund->transaction_id);
+ dr.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
+ TALER_amount_hton (&dr.refund_amount,
+ &refund->refund_amount);
+ TALER_amount_hton (&dr.refund_fee,
+ &refund->refund_fee);
+ dr.merchant = refund->merchant_pub;
+ dr.coin_pub = refund->coin.coin_pub;
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+ &dr.purpose,
+ &refund->merchant_sig.eddsa_sig,
+ &refund->merchant_pub.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Invalid signature on /refund request\n");
+ return TMH_RESPONSE_reply_signature_invalid (connection,
+ "merchant_sig");
+ }
+#if 1
+ GNUNET_break (0); // FIXME: not implemented
+ return MHD_NO;
+#else
+ return TMH_DB_execute_refund (connection,
+ refund);
+#endif
+}
+
+
+/**
+ * Handle a "/refund" request. Parses the JSON, and, if successful,
+ * passes the JSON data to #parse_and_handle_refund_request() to
+ * further check the details of the operation specified. If
+ * everything checks out, this will ultimately lead to the "/refund"
+ * being executed, or rejected.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFUND_handler_refund (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *json;
+ int res;
+ struct TALER_EXCHANGEDB_Refund refund;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount ("refund_amount", &refund.refund_amount),
+ TALER_JSON_spec_amount ("refund_fee", &refund.refund_fee),
+ GNUNET_JSON_spec_fixed_auto ("H_contract", &refund.h_contract),
+ GNUNET_JSON_spec_uint64 ("transaction_id", &refund.transaction_id),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub", &refund.coin.coin_pub),
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub", &refund.merchant_pub),
+ GNUNET_JSON_spec_uint64 ("rtransaction_id", &refund.rtransaction_id),
+ GNUNET_JSON_spec_fixed_auto ("merchant_sig", &refund.merchant_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &json);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == json) )
+ return MHD_YES;
+ res = TMH_PARSE_json_data (connection,
+ json,
+ spec);
+ json_decref (json);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* hard failure */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* failure */
+ res = verify_and_execute_refund (connection,
+ &refund);
+ GNUNET_JSON_parse_free (spec);
+ return res;
+}
+
+
+/* end of taler-exchange-httpd_refund.c */
diff --git a/src/exchange/taler-exchange-httpd_refund.h b/src/exchange/taler-exchange-httpd_refund.h
new file mode 100644
index 000000000..750b08548
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refund.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refund.h
+ * @brief Handle /refund requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_REFUND_H
+#define TALER_EXCHANGE_HTTPD_REFUND_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a "/refund" request. Parses the JSON, and, if successful,
+ * passes the JSON data to #parse_and_handle_refund_request() to
+ * further check the details of the operation specified. If
+ * everything checks out, this will ultimately lead to the "/refund"
+ * being executed, or rejected.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFUND_handler_refund (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/exchange/test_taler_exchange_aggregator.c b/src/exchange/test_taler_exchange_aggregator.c
index 54d63a461..e4ca89c77 100644
--- a/src/exchange/test_taler_exchange_aggregator.c
+++ b/src/exchange/test_taler_exchange_aggregator.c
@@ -359,6 +359,9 @@ fake_issue (struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount_nbo ("EUR:0.1",
&issue->properties.fee_refresh));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount_nbo ("EUR:0.1",
+ &issue->properties.fee_refund));
}
diff --git a/src/exchange/test_taler_exchange_httpd.conf b/src/exchange/test_taler_exchange_httpd.conf
index ca33301f4..642bbf668 100644
--- a/src/exchange/test_taler_exchange_httpd.conf
+++ b/src/exchange/test_taler_exchange_httpd.conf
@@ -52,6 +52,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_ct_10]
@@ -63,6 +64,7 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
rsa_keysize = 1024
[coin_eur_1]
@@ -74,4 +76,5 @@ duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
rsa_keysize = 1024
diff --git a/src/exchange/test_taler_exchange_httpd.sh b/src/exchange/test_taler_exchange_httpd.sh
index 2b55ef5f4..6e51e06c1 100755
--- a/src/exchange/test_taler_exchange_httpd.sh
+++ b/src/exchange/test_taler_exchange_httpd.sh
@@ -21,8 +21,8 @@
#
#
# Clear environment from variables that override config.
-export XDG_DATA_HOME=
-export XDG_CONFIG_HOME=
+unset XDG_DATA_HOME
+unset XDG_CONFIG_HOME
#
# Setup keys.
taler-exchange-keyup -c test_taler_exchange_httpd.conf
diff --git a/src/exchangedb/perf_taler_exchangedb.c b/src/exchangedb/perf_taler_exchangedb.c
index 6ff7f5331..bac8f4cb8 100644
--- a/src/exchangedb/perf_taler_exchangedb.c
+++ b/src/exchangedb/perf_taler_exchangedb.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
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
@@ -17,6 +17,7 @@
* @file exchangedb/perf_taler_exchangedb.c
* @brief Exchange database performance analysis
* @author Nicolas Fournier
+ * @author Christian Grothoff
*/
#include "platform.h"
#include "perf_taler_exchangedb_interpreter.h"
diff --git a/src/exchangedb/perf_taler_exchangedb_init.c b/src/exchangedb/perf_taler_exchangedb_init.c
index 516f3ea58..67ac56c77 100644
--- a/src/exchangedb/perf_taler_exchangedb_init.c
+++ b/src/exchangedb/perf_taler_exchangedb_init.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014, 2015, 2016 Inria & GNUnet e.V.
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
@@ -17,6 +17,7 @@
* @file exchangedb/perf_taler_exchangedb_init.c
* @brief Interpreter library for exchange database performance analysis
* @author Nicolas Fournier
+ * @author Christian Grothoff
*/
#include "platform.h"
#include "perf_taler_exchangedb_init.h"
@@ -75,6 +76,7 @@ PERF_TALER_EXCHANGEDB_denomination_init ()
TALER_amount_hton (&properties.fee_withdraw, &amount);
TALER_amount_hton (&properties.fee_deposit, &amount);
TALER_amount_hton (&properties.fee_refresh, &amount);
+ TALER_amount_hton (&properties.fee_refund, &amount);
GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
&properties.denom_hash);
issue.properties = properties;
diff --git a/src/exchangedb/perf_taler_exchangedb_init.h b/src/exchangedb/perf_taler_exchangedb_init.h
index 0ff074108..2021023a1 100644
--- a/src/exchangedb/perf_taler_exchangedb_init.h
+++ b/src/exchangedb/perf_taler_exchangedb_init.h
@@ -79,8 +79,7 @@ PERF_TALER_EXCHANGEDB_denomination_init (void);
* @return a copy of @a deposit; NULL if error
*/
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *
-PERF_TALER_EXCHANGEDB_denomination_copy (
- const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki);
+PERF_TALER_EXCHANGEDB_denomination_copy (const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki);
/**
@@ -88,8 +87,7 @@ PERF_TALER_EXCHANGEDB_denomination_copy (
* @param dki pointer to the struct to free
*/
int
-PERF_TALER_EXCHANGEDB_denomination_free (
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki);
+PERF_TALER_EXCHANGEDB_denomination_free (struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki);
/**
@@ -122,8 +120,7 @@ PERF_TALER_EXCHANGEDB_reserve_free (struct PERF_TALER_EXCHANGEDB_Reserve *reserv
* @param dki the denomination key used to sign the key
*/
struct TALER_EXCHANGEDB_Deposit *
-PERF_TALER_EXCHANGEDB_deposit_init (
- const struct PERF_TALER_EXCHANGEDB_Coin *coin);
+PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coin);
/**
@@ -150,9 +147,8 @@ PERF_TALER_EXCHANGEDB_deposit_free (struct TALER_EXCHANGEDB_Deposit *deposit);
* @return a randomly generated CollectableBlindcoin
*/
struct PERF_TALER_EXCHANGEDB_Coin *
-PERF_TALER_EXCHANGEDB_coin_init (
- const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
- const struct PERF_TALER_EXCHANGEDB_Reserve *reserve);
+PERF_TALER_EXCHANGEDB_coin_init (const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
+ const struct PERF_TALER_EXCHANGEDB_Reserve *reserve);
/**
@@ -161,8 +157,7 @@ PERF_TALER_EXCHANGEDB_coin_init (
* @return a copy of coin; NULL if error
*/
struct PERF_TALER_EXCHANGEDB_Coin *
-PERF_TALER_EXCHANGEDB_coin_copy (
- const struct PERF_TALER_EXCHANGEDB_Coin *coin);
+PERF_TALER_EXCHANGEDB_coin_copy (const struct PERF_TALER_EXCHANGEDB_Coin *coin);
/**
@@ -170,8 +165,7 @@ PERF_TALER_EXCHANGEDB_coin_copy (
* @param coin pointer to the structure to free
*/
int
-PERF_TALER_EXCHANGEDB_coin_free (
- struct PERF_TALER_EXCHANGEDB_Coin *coin);
+PERF_TALER_EXCHANGEDB_coin_free (struct PERF_TALER_EXCHANGEDB_Coin *coin);
/**
@@ -186,15 +180,14 @@ PERF_TALER_EXCHANGEDB_refresh_session_init (void);
*/
int
PERF_TALER_EXCHANGEDB_refresh_session_copy (struct TALER_EXCHANGEDB_RefreshSession *session,
- struct TALER_EXCHANGEDB_RefreshSession *copy);
+ struct TALER_EXCHANGEDB_RefreshSession *copy);
/**
* Frees memory of a refresh_session
*/
int
-PERF_TALER_EXCHANGEDB_refresh_session_free (
- struct TALER_EXCHANGEDB_RefreshSession *refresh_session);
+PERF_TALER_EXCHANGEDB_refresh_session_free (struct TALER_EXCHANGEDB_RefreshSession *refresh_session);
/**
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 092aebc48..d69fa7d61 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -272,6 +272,9 @@ postgres_create_tables (void *cls,
",fee_refresh_val INT8 NOT NULL"
",fee_refresh_frac INT4 NOT NULL"
",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refund_val INT8 NOT NULL"
+ ",fee_refund_frac INT4 NOT NULL"
+ ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
")");
/* reserves table is for summarization of a reserve. It is updated when new
funds are added and existing funds are withdrawn. The 'expiration_date'
@@ -544,10 +547,14 @@ postgres_prepare (PGconn *db_conn)
",fee_refresh_val"
",fee_refresh_frac"
",fee_refresh_curr" /* must match coin_curr */
+ ",fee_refund_val"
+ ",fee_refund_frac"
+ ",fee_refund_curr" /* must match coin_curr */
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
- " $11, $12, $13, $14, $15, $16, $17, $18, $19);",
- 19, NULL);
+ " $11, $12, $13, $14, $15, $16, $17, $18,"
+ " $19, $20, $21, $22);",
+ 22, NULL);
/* Used in #postgres_get_denomination_info() */
PREPARE ("denomination_get",
@@ -570,6 +577,9 @@ postgres_prepare (PGconn *db_conn)
",fee_refresh_val"
",fee_refresh_frac"
",fee_refresh_curr" /* must match coin_curr */
+ ",fee_refund_val"
+ ",fee_refund_frac"
+ ",fee_refund_curr" /* must match coin_curr */
" FROM denominations"
" WHERE pub=$1;",
1, NULL);
@@ -1378,6 +1388,7 @@ postgres_insert_denomination_info (void *cls,
TALER_PQ_query_param_amount_nbo (&issue->properties.fee_withdraw),
TALER_PQ_query_param_amount_nbo (&issue->properties.fee_deposit),
TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refresh),
+ TALER_PQ_query_param_amount_nbo (&issue->properties.fee_refund),
GNUNET_PQ_query_param_end
};
/* check fees match coin currency */
@@ -1390,6 +1401,9 @@ postgres_insert_denomination_info (void *cls,
GNUNET_assert (GNUNET_YES ==
TALER_amount_cmp_currency_nbo (&issue->properties.value,
&issue->properties.fee_refresh));
+ GNUNET_assert (GNUNET_YES ==
+ TALER_amount_cmp_currency_nbo (&issue->properties.value,
+ &issue->properties.fee_refund));
result = GNUNET_PQ_exec_prepared (session->conn,
"denomination_insert",
@@ -1476,6 +1490,8 @@ postgres_get_denomination_info (void *cls,
&issue->properties.fee_deposit),
TALER_PQ_result_spec_amount_nbo ("fee_refresh",
&issue->properties.fee_refresh),
+ TALER_PQ_result_spec_amount_nbo ("fee_refund",
+ &issue->properties.fee_refund),
GNUNET_PQ_result_spec_end
};
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index f2c473fd9..3bb9b9f2c 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -16,7 +16,8 @@
/**
* @file exchangedb/test_exchangedb.c
* @brief test cases for DB interaction functions
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Sree Harsha Totakura
+ * @author Christian Grothoff
*/
#include "platform.h"
#include "taler_exchangedb_lib.h"
@@ -114,7 +115,8 @@ create_denom_key_pair (unsigned int size,
const struct TALER_Amount *value,
const struct TALER_Amount *fee_withdraw,
const struct TALER_Amount *fee_deposit,
- const struct TALER_Amount *fee_refresh)
+ const struct TALER_Amount *fee_refresh,
+ const struct TALER_Amount *fee_refund)
{
struct DenomKeyPair *dkp;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation dki;
@@ -147,6 +149,7 @@ create_denom_key_pair (unsigned int size,
TALER_amount_hton (&dki.issue.properties.fee_withdraw, fee_withdraw);
TALER_amount_hton (&dki.issue.properties.fee_deposit, fee_deposit);
TALER_amount_hton (&dki.issue.properties.fee_refresh, fee_refresh);
+ TALER_amount_hton (&dki.issue.properties.fee_refund, fee_refund);
GNUNET_CRYPTO_rsa_public_key_hash (dkp->pub.rsa_public_key,
&dki.issue.properties.denom_hash);
if (GNUNET_OK !=
@@ -166,11 +169,11 @@ static struct TALER_Amount value;
static struct TALER_Amount fee_withdraw;
static struct TALER_Amount fee_deposit;
static struct TALER_Amount fee_refresh;
+static struct TALER_Amount fee_refund;
static struct TALER_Amount amount_with_fee;
static void
-free_refresh_commit_coins_array(struct TALER_EXCHANGEDB_RefreshCommitCoin
- *commit_coins,
+free_refresh_commit_coins_array(struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
unsigned int size)
{
unsigned int cnt;
@@ -205,7 +208,7 @@ test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session *session,
uint16_t cnc_index;
int ret;
- #define COIN_ENC_MAX_SIZE 512
+#define COIN_ENC_MAX_SIZE 512
ret = GNUNET_SYSERR;
ret_commit_coins = NULL;
commit_coins = GNUNET_new_array (MELT_NEW_COINS,
@@ -331,7 +334,8 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
&value,
&fee_withdraw,
&fee_deposit,
- &fee_refresh);
+ &fee_refresh,
+ &fee_refund);
/* create MELT_OLD_COINS number of refresh melts */
melts = GNUNET_new_array (MELT_OLD_COINS, struct TALER_EXCHANGEDB_RefreshMelt);
for (cnt=0; cnt < MELT_OLD_COINS; cnt++)
@@ -394,7 +398,8 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
&value,
&fee_withdraw,
&fee_deposit,
- &fee_refresh);
+ &fee_refresh,
+ &fee_refund);
new_denom_pubs[cnt]=new_dkp[cnt]->pub;
}
FAILIF (GNUNET_OK != plugin->insert_refresh_order (plugin->cls,
@@ -684,6 +689,9 @@ run (void *cls)
TALER_string_to_amount (CURRENCY ":0.000010",
&fee_refresh));
GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.000010",
+ &fee_refund));
+ GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.000010",
&amount_with_fee));
@@ -723,7 +731,8 @@ run (void *cls)
&value,
&fee_withdraw,
&fee_deposit,
- &fee_refresh);
+ &fee_refresh,
+ &fee_refund);
RND_BLK(&cbc.h_coin_envelope);
RND_BLK(&cbc.reserve_sig);
cbc.denom_pub = dkp->pub;
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index e52e94d9a..04f94e570 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -122,9 +122,14 @@ struct TALER_EXCHANGE_DenomPublicKey
struct TALER_Amount fee_deposit;
/**
- *The applicable fee to melt/refresh a coin of this denomination
+ * The applicable fee to melt/refresh a coin of this denomination
*/
struct TALER_Amount fee_refresh;
+
+ /**
+ * The applicable fee to refund a coin of this denomination
+ */
+ struct TALER_Amount fee_refund;
};
@@ -460,6 +465,85 @@ void
TALER_EXCHANGE_deposit_cancel (struct TALER_EXCHANGE_DepositHandle *deposit);
+/* ********************* /refund *********************** */
+
+/**
+ * @brief A Refund Handle
+ */
+struct TALER_EXCHANGE_RefundHandle;
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * deposit permission request to a exchange.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
+ * 0 if the exchange's reply is bogus (fails to follow the protocol)
+ * @param obj the received JSON reply, should be kept as proof (and, in particular,
+ * be forwarded to the customer)
+ */
+typedef void
+(*TALER_EXCHANGE_RefundResultCallback) (void *cls,
+ unsigned int http_status,
+ const json_t *obj);
+
+
+/**
+ * Submit a refund request to the exchange and get the exchange's
+ * response. This API is used by a merchant. Note that
+ * while we return the response verbatim 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). If
+ * the exchange's reply is not well-formed, we return an HTTP status code
+ * of zero to @a cb.
+ *
+ * The @a exchange must be ready to operate (i.e. have
+ * finished processing the /keys reply). If this check fails, we do
+ * NOT initiate the transaction with the exchange and instead return NULL.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param amount the amount to be refunded; must be larger than the refund fee
+ * (as that fee is still being subtracted), and smaller than the amount
+ * (with deposit fee) of the original deposit contribution of this coin
+ * @param refund_fee fee applicable to this coin for the refund
+ * @param h_contract hash of the contact of the merchant with the customer that is being refunded
+ * @param transaction_id transaction id for the transaction being refunded, must match @a h_contract
+ * @param coin_pub coin’s public key of the coin from the original deposit operation
+ * @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
+ * this is needed as we may first do a partial refund and later a full refund. If both
+ * refunds are also over the same amount, we need the @a rtransaction_id to make the disjoint
+ * refund requests different (as requests are idempotent and otherwise the 2nd refund might not work).
+ * @param merchant_priv the private key of the merchant, used to generate signature for refund request
+ * @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_RefundHandle *
+TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
+ const struct TALER_Amount *amount,
+ const struct TALER_Amount *refund_fee,
+ const struct GNUNET_HashCode *h_contract,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t rtransaction_id,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ TALER_EXCHANGE_RefundResultCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Cancel a refund permission request. This function cannot be used
+ * on a request handle if a response is already served for it. If
+ * this function is called, the refund may or may not have happened.
+ * However, it is fine to try to refund the coin a second time.
+ *
+ * @param refund the refund request handle
+ */
+void
+TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund);
+
+
/* ********************* /reserve/status *********************** */
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 3646981cd..e364d6b95 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -299,6 +299,67 @@ struct TALER_EXCHANGEDB_Deposit
/**
+ * @brief Specification for a /refund operation. The combination of
+ * the coin's public key, the merchant's public key and the
+ * transaction ID must be unique. While a coin can (theoretically) be
+ * deposited at the same merchant twice (with partial spending), the
+ * merchant must either use a different public key or a different
+ * transaction ID for the two transactions. The same goes for
+ * refunds, hence we also have a "rtransaction" ID which is disjoint
+ * from the transaction ID. The same coin must not be used twice at
+ * the same merchant for the same transaction or rtransaction ID.
+ */
+struct TALER_EXCHANGEDB_Refund
+{
+ /**
+ * Information about the coin that is being refunded.
+ */
+ struct TALER_CoinPublicInfo coin;
+
+ /**
+ * Public key of the merchant.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Signature from the merchant affirming the refund.
+ */
+ struct TALER_MerchantSignatureP merchant_sig;
+
+ /**
+ * Hash over the contract between merchant and customer
+ * (remains unknown to the Exchange).
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions, of the original transaction that is being
+ * refunded.
+ */
+ uint64_t transaction_id;
+
+ /**
+ * Merchant-generated REFUND transaction ID to detect duplicate
+ * refunds.
+ */
+ uint64_t rtransaction_id;
+
+ /**
+ * Fraction of the original deposit's value to be refunded, including
+ * refund fee (if any). The coin is identified by @e coin_pub.
+ */
+ struct TALER_Amount refund_amount;
+
+ /**
+ * Refund fee to be covered by the customer.
+ */
+ struct TALER_Amount refund_fee;
+
+};
+
+
+/**
* @brief Global information for a refreshing session. Includes
* dimensions of the operation, security parameters and
* client signatures from "/refresh/melt" and "/refresh/commit".
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index bd892e793..f1586aa8c 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -396,6 +396,64 @@ struct TALER_DepositConfirmationPS
/**
+ * @brief Format used to generate the signature on a request to refund
+ * a coin into the account of the customer.
+ */
+struct TALER_RefundRequestPS
+{
+ /**
+ * Purpose must be #TALER_SIGNATURE_MERCHANT_REFUND.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash over the contract which is being refunded.
+ */
+ struct GNUNET_HashCode h_contract GNUNET_PACKED;
+
+ /**
+ * Merchant-generated transaction ID of the orginal transaction.
+ */
+ uint64_t transaction_id GNUNET_PACKED;
+
+ /**
+ * The coin's public key. This is the value that must have been
+ * signed (blindly) by the Exchange.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * The Merchant's public key. Allows the merchant to later refund
+ * the transaction or to inquire about the wire transfer identifier.
+ */
+ struct TALER_MerchantPublicKeyP merchant;
+
+ /**
+ * Merchant-generated transaction ID for the refund.
+ */
+ uint64_t rtransaction_id GNUNET_PACKED;
+
+ /**
+ * Amount to be refunded, including refund fee charged by the
+ * exchange to the customer.
+ */
+ struct TALER_AmountNBO refund_amount;
+
+ /**
+ * Refund fee charged by the exchange. This must match the
+ * Exchange's denomination key's refund fee. If the client puts in
+ * an invalid refund fee (too high or too low) that does not match
+ * the Exchange's denomination key, the refund operation is invalid
+ * and will be rejected by the exchange. The @e amount_with_fee
+ * minus the @e refund_fee is the amount that will be credited to
+ * the original coin.
+ */
+ struct TALER_AmountNBO refund_fee;
+
+};
+
+
+/**
* @brief Message signed by a coin to indicate that the coin should be
* melted.
*/
@@ -640,6 +698,13 @@ struct TALER_DenominationKeyValidityPS
struct TALER_AmountNBO fee_refresh;
/**
+ * The fee the exchange charges when a coin of this type is refunded.
+ * (can be zero). Note that refund fees are charged to the customer;
+ * if a refund is given, the deposit fee is also refunded.
+ */
+ struct TALER_AmountNBO fee_refund;
+
+ /**
* Hash code of the denomination public key. (Used to avoid having
* the variable-size RSA key in this struct.)
*/
@@ -736,6 +801,12 @@ struct TALER_ExchangeKeyValidityPS
struct TALER_AmountNBO fee_refresh;
/**
+ * The fee the exchange charges when a coin of this type is refreshed.
+ * (can be zero).
+ */
+ struct TALER_AmountNBO fee_refund;
+
+ /**
* Hash code of the denomination public key. (Used to avoid having
* the variable-size RSA key in this struct.)
*/