aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/merchant_api_post_order_pay.c220
-rw-r--r--src/testing/test_kyc_api.c28
-rw-r--r--src/testing/test_kyc_api.conf2
-rw-r--r--src/testing/test_merchant_api-cs.conf2
-rw-r--r--src/testing/test_merchant_api-rsa.conf2
-rw-r--r--src/testing/test_merchant_api.c24
-rw-r--r--src/testing/test_template.conf4
7 files changed, 203 insertions, 79 deletions
diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c
index bd6fca6f..4310838f 100644
--- a/src/lib/merchant_api_post_order_pay.c
+++ b/src/lib/merchant_api_post_order_pay.c
@@ -91,6 +91,29 @@ struct TALER_MERCHANT_OrderPayHandle
struct TALER_MerchantPublicKeyP merchant_pub;
/**
+ * JSON with the full reply, used during async
+ * processing.
+ */
+ json_t *full_reply;
+
+ /**
+ * Pointer into @e coins array for the coin that
+ * created a conflict (that we are checking).
+ */
+ const struct TALER_MERCHANT_PaidCoin *error_pc;
+
+ /**
+ * Coin history that proves a conflict.
+ */
+ json_t *error_history;
+
+ /**
+ * Handle to the exchange that issued a problematic
+ * coin (if any).
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
* Number of @e coins we are paying with.
*/
unsigned int num_coins;
@@ -109,38 +132,28 @@ struct TALER_MERCHANT_OrderPayHandle
* Now we need to check the provided cryptographic proof that the
* coin was actually already spent!
*
- * @param pc handle of the original coin we paid with
- * @param json cryptographic proof of coin's transaction
- * history as was returned by the exchange/merchant
- * @return #GNUNET_OK if proof checks out
+ * @param oph operation handle
+ * @param keys key data from the exchange
+ * @return #GNUNET_OK if conflict is valid
*/
static enum GNUNET_GenericReturnValue
-check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
- json_t *json)
+check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
+ const struct TALER_EXCHANGE_Keys *keys)
{
-#if FIXME
struct TALER_Amount spent;
struct TALER_Amount spent_plus_contrib;
struct TALER_DenominationHashP h_denom_pub_pc;
const struct TALER_EXCHANGE_DenomPublicKey *dpk;
- const struct TALER_EXCHANGE_Keys *keys;
- struct TALER_EXCHANGE_Handle *exchange;
- exchange = TALER_EXCHANGE_connect (ctx,
- pc->exchange_url,
- &cert_cb,
- ctx,
- TALER_EXCHANGE_OPTION_END);
- keys = TALER_EXCHANGE_get_keys (exchange);
- TALER_denom_pub_hash (&pc->denom_pub,
+ TALER_denom_pub_hash (&oph->error_pc->denom_pub,
&h_denom_pub_pc);
dpk = TALER_EXCHANGE_get_denomination_key_by_hash (
keys,
&h_denom_pub_pc);
if (GNUNET_OK !=
TALER_EXCHANGE_verify_coin_history (dpk,
- &pc->coin_pub,
- json,
+ &oph->error_pc->coin_pub,
+ oph->error_history,
&spent))
{
/* Exchange's history fails to verify */
@@ -150,13 +163,13 @@ check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
if (0 >
TALER_amount_add (&spent_plus_contrib,
&spent,
- &pc->amount_with_fee))
+ &oph->error_pc->amount_with_fee))
{
/* We got an integer overflow? Bad application! */
GNUNET_break (0);
return GNUNET_SYSERR;
}
- if (-1 != TALER_amount_cmp (&pc->denom_value,
+ if (-1 != TALER_amount_cmp (&oph->error_pc->denom_value,
&spent_plus_contrib))
{
/* according to our calculations, the transaction should
@@ -165,7 +178,6 @@ check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
-#endif
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Accepting proof of double-spending (or coin public key re-use)\n");
return GNUNET_OK;
@@ -173,6 +185,91 @@ check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
/**
+ * We got the fee structure from the exchange. Now
+ * validate the conflict error.
+ *
+ * @param cls a `struct TALER_MERCHANT_OrderPayHandle`
+ * @param ehr reply from the exchange
+ * @param keys the key structure
+ * @param compat protocol compatibility indication
+ */
+static void
+cert_cb (void *cls,
+ const struct TALER_EXCHANGE_HttpResponse *ehr,
+ const struct TALER_EXCHANGE_Keys *keys,
+ enum TALER_EXCHANGE_VersionCompatibility compat)
+{
+ struct TALER_MERCHANT_OrderPayHandle *oph = cls;
+
+ if (TALER_EXCHANGE_VC_INCOMPATIBLE & compat)
+ {
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = MHD_HTTP_CONFLICT,
+ .exchange_http_status = 0,
+ .ec = TALER_EC_WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
+ .reply = oph->full_reply,
+ .exchange_reply = ehr->reply,
+ .hint = "could not check error: incompatible exchange version"
+ };
+
+ oph->pay_cb (oph->pay_cb_cls,
+ &hr,
+ NULL);
+ TALER_MERCHANT_order_pay_cancel (oph);
+ return;
+ }
+ if ( (MHD_HTTP_OK != ehr->http_status) ||
+ (NULL == keys) )
+ {
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = MHD_HTTP_CONFLICT,
+ .exchange_http_status = ehr->http_status,
+ .ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ .reply = oph->full_reply,
+ .exchange_reply = ehr->reply,
+ .hint = "failed to download /keys from the exchange"
+ };
+
+ oph->pay_cb (oph->pay_cb_cls,
+ &hr,
+ NULL);
+ TALER_MERCHANT_order_pay_cancel (oph);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ check_conflict (oph,
+ keys))
+ {
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = 0,
+ .ec = TALER_EC_GENERIC_INVALID_RESPONSE,
+ .reply = oph->full_reply
+ };
+
+ oph->pay_cb (oph->pay_cb_cls,
+ &hr,
+ NULL);
+ TALER_MERCHANT_order_pay_cancel (oph);
+ return;
+ }
+
+ {
+ struct TALER_MERCHANT_HttpResponse hr = {
+ .http_status = MHD_HTTP_CONFLICT,
+ .ec = TALER_JSON_get_error_code (oph->full_reply),
+ .reply = oph->full_reply
+ };
+
+ oph->pay_cb (oph->pay_cb_cls,
+ &hr,
+ NULL);
+ TALER_MERCHANT_order_pay_cancel (oph);
+ }
+}
+
+
+/**
* We got a 409 response back from the exchange (or the merchant).
* Now we need to check the provided cryptograophic proof that the
* coin was actually already spent!
@@ -180,22 +277,26 @@ check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
* @param oph handle of the original pay operation
* @param json cryptograophic proof returned by the
* exchange/merchant
- * @return #GNUNET_OK if proof checks out
+ * @return #GNUNET_OK if proof checks out,
+ * #GNUNET_SYSERR if it is wrong,
+ * #GNUNET_NO if checking continues asynchronously
*/
static enum GNUNET_GenericReturnValue
-check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
+parse_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
const json_t *json)
{
- json_t *history;
json_t *ereply;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("exchange_reply", &ereply),
- GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
+ GNUNET_JSON_spec_json ("exchange_reply",
+ &ereply),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub",
+ &coin_pub),
GNUNET_JSON_spec_end ()
};
struct GNUNET_JSON_Specification hspec[] = {
- GNUNET_JSON_spec_json ("history", &history),
+ GNUNET_JSON_spec_json ("history",
+ &oph->error_history),
GNUNET_JSON_spec_end ()
};
@@ -224,12 +325,14 @@ check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
GNUNET_memcmp (&oph->coins[i].coin_pub,
&coin_pub))
{
- enum GNUNET_GenericReturnValue ret;
-
- ret = check_coin_history (&oph->coins[i],
- history);
- GNUNET_JSON_parse_free (hspec);
- return ret;
+ oph->error_pc = &oph->coins[i];
+ oph->full_reply = json_incref ((json_t *) json);
+ oph->exchange = TALER_EXCHANGE_connect (oph->ctx,
+ oph->error_pc->exchange_url,
+ &cert_cb,
+ oph,
+ TALER_EXCHANGE_OPTION_END);
+ return GNUNET_NO;
}
}
GNUNET_break_op (0); /* complaint is not about any of the coins
@@ -338,15 +441,6 @@ handle_pay_finished (void *cls,
happen, we should pass the JSON reply to the
application */
break;
- case MHD_HTTP_PRECONDITION_FAILED:
- TALER_MERCHANT_parse_error_details_ (json,
- response_code,
- &hr);
- /* Nothing really to verify, the merchant is blaming us for failing to
- satisfy some constraint (likely it does not like our exchange because
- of some disagreement on the PKI). We should pass the JSON reply to the
- application */
- break;
case MHD_HTTP_REQUEST_TIMEOUT:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
@@ -355,15 +449,27 @@ handle_pay_finished (void *cls,
Pass on to application. */
break;
case MHD_HTTP_CONFLICT:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
- if (GNUNET_OK != check_conflict (oph,
- json))
{
- GNUNET_break_op (0);
- response_code = 0;
+ enum GNUNET_GenericReturnValue ret;
+
+ hr.ec = TALER_JSON_get_error_code (json);
+ hr.hint = TALER_JSON_get_error_hint (json);
+ ret = parse_conflict (oph,
+ json);
+ switch (ret)
+ {
+ case GNUNET_OK:
+ break;
+ case GNUNET_NO:
+ /* handled asynchronously! */
+ return; /* ! */
+ case GNUNET_SYSERR:
+ GNUNET_break_op (0);
+ response_code = 0;
+ break;
+ }
+ break;
}
- break;
case MHD_HTTP_GONE:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
@@ -371,6 +477,15 @@ handle_pay_finished (void *cls,
denomination key of a coin involved has expired.
Might be a disagreement in timestamps? Still, pass on to application. */
break;
+ case MHD_HTTP_PRECONDITION_FAILED:
+ TALER_MERCHANT_parse_error_details_ (json,
+ response_code,
+ &hr);
+ /* Nothing really to verify, the merchant is blaming us for failing to
+ satisfy some constraint (likely it does not like our exchange because
+ of some disagreement on the PKI). We should pass the JSON reply to the
+ application */
+ break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
hr.ec = TALER_JSON_get_error_code (json);
hr.hint = TALER_JSON_get_error_hint (json);
@@ -670,7 +785,14 @@ TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
GNUNET_CURL_job_cancel (oph->job);
oph->job = NULL;
}
+ if (NULL != oph->exchange)
+ {
+ TALER_EXCHANGE_disconnect (oph->exchange);
+ oph->exchange = NULL;
+ }
TALER_curl_easy_post_finished (&oph->post_ctx);
+ json_decref (oph->error_history);
+ json_decref (oph->full_reply);
GNUNET_free (oph->coins);
GNUNET_free (oph->url);
GNUNET_free (oph);
diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c
index c1bc0d56..490aa6e4 100644
--- a/src/testing/test_kyc_api.c
+++ b/src/testing/test_kyc_api.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
@@ -17,7 +17,7 @@
<http://www.gnu.org/licenses/>
*/
/**
- * @file test_merchant_api.c
+ * @file test_kyc_api.c
* @brief testcase to test exchange's HTTP API interface
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
@@ -43,7 +43,7 @@
*/
#define CONFIG_FILE "test_kyc_api.conf"
-#define PAYTO_I1 "payto://x-taler-bank/localhost/3"
+#define PAYTO_I1 "payto://x-taler-bank/localhost/3?receiver-name=3"
/**
* Exchange base URL. Could also be taken from config.
@@ -272,7 +272,7 @@ run (void *cls,
MHD_HTTP_NO_CONTENT,
false),
TALER_TESTING_cmd_wire_add ("add-wire-account",
- "payto://x-taler-bank/localhost/2",
+ "payto://x-taler-bank/localhost/2?receiver-name=2",
MHD_HTTP_NO_CONTENT,
false),
TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
@@ -305,7 +305,7 @@ int
main (int argc,
char *const *argv)
{
- unsigned int ret;
+ enum GNUNET_GenericReturnValue ret;
/* These environment variables get in the way... */
unsetenv ("XDG_DATA_HOME");
@@ -319,14 +319,19 @@ main (int argc,
&bc))
return 77;
- payer_payto = ("payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME);
- exchange_payto = ("payto://x-taler-bank/localhost/" EXCHANGE_ACCOUNT_NAME);
- merchant_payto = ("payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME);
+ payer_payto =
+ ("payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME "?receiver-name="
+ USER_ACCOUNT_NAME);
+ exchange_payto =
+ ("payto://x-taler-bank/localhost/" EXCHANGE_ACCOUNT_NAME "?receiver-name="
+ EXCHANGE_ACCOUNT_NAME);
+ merchant_payto =
+ ("payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME "?receiver-name="
+ MERCHANT_ACCOUNT_NAME);
if (NULL ==
(merchant_url = TALER_TESTING_prepare_merchant (CONFIG_FILE)))
return 77;
-
GNUNET_asprintf (&merchant_url_i1a,
"%sinstances/i1a/",
merchant_url);
@@ -341,23 +346,18 @@ main (int argc,
return 1;
case GNUNET_NO:
return 77;
-
case GNUNET_OK:
-
if (NULL == (merchantd =
TALER_TESTING_run_merchant (CONFIG_FILE,
merchant_url)))
return 1;
-
ret = TALER_TESTING_setup_with_exchange (&run,
NULL,
CONFIG_FILE);
-
GNUNET_OS_process_kill (merchantd, SIGTERM);
GNUNET_OS_process_wait (merchantd);
GNUNET_OS_process_destroy (merchantd);
GNUNET_free (merchant_url);
-
if (GNUNET_OK != ret)
return 1;
break;
diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf
index eea5538c..e28cd641 100644
--- a/src/testing/test_kyc_api.conf
+++ b/src/testing/test_kyc_api.conf
@@ -132,7 +132,7 @@ CONFIG = postgres:///talercheck
# Account of the EXCHANGE
[exchange-account-exchange]
# What is the exchange's bank account (with the "Taler Bank" demo system)?
-PAYTO_URI = "payto://x-taler-bank/localhost/2"
+PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
diff --git a/src/testing/test_merchant_api-cs.conf b/src/testing/test_merchant_api-cs.conf
index 006fb644..502d807a 100644
--- a/src/testing/test_merchant_api-cs.conf
+++ b/src/testing/test_merchant_api-cs.conf
@@ -114,7 +114,7 @@ CONFIG = postgres:///talercheck
# Account of the EXCHANGE
[exchange-account-exchange]
# What is the exchange's bank account (with the "Taler Bank" demo system)?
-PAYTO_URI = "payto://x-taler-bank/localhost/2"
+PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
diff --git a/src/testing/test_merchant_api-rsa.conf b/src/testing/test_merchant_api-rsa.conf
index b0f2b49e..5246fc40 100644
--- a/src/testing/test_merchant_api-rsa.conf
+++ b/src/testing/test_merchant_api-rsa.conf
@@ -114,7 +114,7 @@ CONFIG = postgres:///talercheck
# Account of the EXCHANGE
[exchange-account-exchange]
# What is the exchange's bank account (with the "Taler Bank" demo system)?
-PAYTO_URI = "payto://x-taler-bank/localhost/2"
+PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
index 8fc0f4ad..27129066 100644
--- a/src/testing/test_merchant_api.c
+++ b/src/testing/test_merchant_api.c
@@ -59,7 +59,7 @@
*/
static char *config_file;
-#define PAYTO_I1 "payto://x-taler-bank/localhost/3"
+#define PAYTO_I1 "payto://x-taler-bank/localhost/3?receiver-name=3"
/**
* Exchange base URL. Could also be taken from config.
@@ -1278,7 +1278,7 @@ run (void *cls,
MHD_HTTP_NO_CONTENT,
false),
TALER_TESTING_cmd_wire_add ("add-wire-account",
- "payto://x-taler-bank/localhost/2",
+ "payto://x-taler-bank/localhost/2?receiver-name=2",
MHD_HTTP_NO_CONTENT,
false),
TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
@@ -1458,7 +1458,7 @@ run (void *cls,
TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2-non-idem",
merchant_url,
"i2",
- "payto://other-method/",
+ "payto://other-method/?receiver-name=X",
"EUR",
MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_merchant_delete_instance ("instance-delete-i2",
@@ -1640,12 +1640,11 @@ main (int argc,
char *const *argv)
{
char *cipher;
- unsigned int ret;
+ enum GNUNET_GenericReturnValue ret;
/* These environment variables get in the way... */
unsetenv ("XDG_DATA_HOME");
unsetenv ("XDG_CONFIG_HOME");
-
GNUNET_log_setup (argv[0],
"DEBUG",
NULL);
@@ -1661,19 +1660,23 @@ main (int argc,
&bc))
return 77;
- payer_payto = ("payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME);
- exchange_payto = ("payto://x-taler-bank/localhost/" EXCHANGE_ACCOUNT_NAME);
- merchant_payto = ("payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME);
+ payer_payto =
+ ("payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME "?receiver-name="
+ USER_ACCOUNT_NAME);
+ exchange_payto =
+ ("payto://x-taler-bank/localhost/" EXCHANGE_ACCOUNT_NAME "?receiver-name="
+ EXCHANGE_ACCOUNT_NAME);
+ merchant_payto =
+ ("payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME "?receiver-name="
+ MERCHANT_ACCOUNT_NAME);
if (NULL ==
(merchant_url = TALER_TESTING_prepare_merchant (config_file)))
return 77;
-
GNUNET_asprintf (&merchant_url_i1a,
"%sinstances/i1a/",
merchant_url);
TALER_TESTING_cleanup_files (config_file);
-
switch (TALER_TESTING_prepare_exchange (config_file,
GNUNET_YES,
&ec))
@@ -1697,7 +1700,6 @@ main (int argc,
GNUNET_OS_process_wait (merchantd);
GNUNET_OS_process_destroy (merchantd);
GNUNET_free (merchant_url);
-
if (GNUNET_OK != ret)
return 1;
break;
diff --git a/src/testing/test_template.conf b/src/testing/test_template.conf
index 20fee4b8..e9b94761 100644
--- a/src/testing/test_template.conf
+++ b/src/testing/test_template.conf
@@ -63,7 +63,7 @@ MAX_DEBT = TESTKUDOS:50.0
MAX_DEBT_BANK = TESTKUDOS:100000.0
HTTP_PORT = 8082
SUGGESTED_EXCHANGE = http://localhost:8081/
-SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/localhost/2
+SUGGESTED_EXCHANGE_PAYTO = "payto://x-taler-bank/localhost/2?receiver-name=2"
ALLOW_REGISTRATIONS = YES
SERVE = http
@@ -72,7 +72,7 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
LEGAL_RESERVE_EXPIRATION_TIME = 7 years
[exchange-account-1]
-PAYTO_URI = payto://x-taler-bank/localhost/Exchange
+PAYTO_URI = "payto://x-taler-bank/localhost/Exchange?receiver-name=Exchange"
enable_debit = yes
enable_credit = yes