summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-05-05 10:22:32 +0200
committerChristian Grothoff <christian@grothoff.org>2016-05-05 10:22:32 +0200
commit404b586176af67b20cae6ba5f918c78987fd6cf6 (patch)
tree1f699a2b9a4778b7cd9190fbe1160f0820b3b754
parent7bd9e5fb6fbf5ca4bb4dda457b2be53f94781656 (diff)
downloadmerchant-404b586176af67b20cae6ba5f918c78987fd6cf6.tar.gz
merchant-404b586176af67b20cae6ba5f918c78987fd6cf6.tar.bz2
merchant-404b586176af67b20cae6ba5f918c78987fd6cf6.zip
fixing issues around testcase
-rw-r--r--.gitignore1
-rw-r--r--src/lib/Makefile.am1
-rw-r--r--src/lib/merchant_api_contract.c6
-rw-r--r--src/lib/merchant_api_pay.c23
-rw-r--r--src/lib/test_merchant_api.c295
5 files changed, 183 insertions, 143 deletions
diff --git a/.gitignore b/.gitignore
index 84d6bd6f..3d26eb43 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,5 +27,6 @@ GTAGS
*.swp
src/backend/taler-merchant-httpd
src/lib/test_merchant_api
+src/lib/test_merchant_api_home/.local/share/taler/exchange/live-keys/
taler_merchant_config.h
taler_merchant_config.h.in
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 438b5a7a..ab79c308 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -48,6 +48,7 @@ test_merchant_api_LDADD = \
-ltalerexchange \
-ltalerjson \
-ltalerutil \
+ -lgnunetjson \
-lgnunetcurl \
-lgnunetutil \
-ljansson
diff --git a/src/lib/merchant_api_contract.c b/src/lib/merchant_api_contract.c
index 073d4463..1938afa6 100644
--- a/src/lib/merchant_api_contract.c
+++ b/src/lib/merchant_api_contract.c
@@ -177,6 +177,7 @@ TALER_MERCHANT_contract_sign (struct GNUNET_CURL_Context *ctx,
void *contract_cb_cls)
{
struct TALER_MERCHANT_ContractOperation *co;
+ json_t *req;
CURL *eh;
co = GNUNET_new (struct TALER_MERCHANT_ContractOperation);
@@ -185,10 +186,13 @@ TALER_MERCHANT_contract_sign (struct GNUNET_CURL_Context *ctx,
co->cb_cls = contract_cb_cls;
co->url = GNUNET_strdup (backend_uri);
+ req = json_pack ("{s:O}",
+ "contract", (json_t *) contract);
eh = curl_easy_init ();
GNUNET_assert (NULL != (co->json_enc =
- json_dumps (contract,
+ json_dumps (req,
JSON_COMPACT)));
+ json_decref (req);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c
index ba287aca..8e016da8 100644
--- a/src/lib/merchant_api_pay.c
+++ b/src/lib/merchant_api_pay.c
@@ -278,6 +278,13 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
struct TALER_Amount total_amount;
unsigned int i;
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (amount,
+ max_fee))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
if ( (0 != execution_deadline.abs_value_us) &&
(execution_deadline.abs_value_us < refund_deadline.abs_value_us) )
{
@@ -368,6 +375,14 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
json_decref (j_coins);
return NULL;
}
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&new_amount,
+ &total_amount))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
if (1 ==
TALER_amount_cmp (&new_amount,
&total_amount))
@@ -384,6 +399,14 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
{
/* Full fee covered by merchant, but our total
must at least cover the total contract amount */
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (amount,
+ &total_amount))
+ {
+ GNUNET_break (0);
+ json_decref (j_coins);
+ return NULL;
+ }
if (1 ==
TALER_amount_cmp (amount,
&total_amount))
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index 87bf51ee..d4c5825e 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -332,14 +332,9 @@ struct Command
{
/**
- * Amount to pay (total for the entire contract).
+ * Reference to the contract.
*/
- const char *total_amount;
-
- /**
- * Maximum fee covered by merchant.
- */
- const char *max_fee;
+ const char *contract_ref;
/**
* Reference to a reserve_withdraw operation for a coin to
@@ -368,27 +363,6 @@ struct Command
const char *amount_without_fee;
/**
- * JSON string describing the merchant's "wire details".
- */
- const char *wire_details;
-
- /**
- * JSON string describing the contract between the two parties.
- */
- const char *contract;
-
- /**
- * Transaction ID to use.
- */
- uint64_t transaction_id;
-
- /**
- * Relative time (to add to 'now') to compute the refund deadline.
- * Zero for no refunds.
- */
- struct GNUNET_TIME_Relative refund_deadline;
-
- /**
* Deposit handle while operation is running.
*/
struct TALER_MERCHANT_Pay *ph;
@@ -437,6 +411,9 @@ struct InterpreterState
static void
fail (struct InterpreterState *is)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Interpreter failed at step %u\n",
+ is->ip);
result = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
}
@@ -754,6 +731,54 @@ reserve_withdraw_cb (void *cls,
/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * /contract request to a merchant.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, 200 indicates success;
+ * 0 if the backend's reply is bogus (fails to follow the protocol)
+ * @param obj the full received JSON reply, or
+ * error details if the request failed
+ * @param contract completed contract, NULL on error
+ * @param sig merchant's signature over the contract, NULL on error
+ * @param h_contract hash of the contract, NULL on error
+ */
+static void
+contract_cb (void *cls,
+ unsigned int http_status,
+ const json_t *obj,
+ const json_t *contract,
+ const struct TALER_MerchantSignatureP *sig,
+ const struct GNUNET_HashCode *h_contract)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.contract.co = NULL;
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ cmd->details.contract.contract = json_incref ((json_t *) contract);
+ cmd->details.contract.merchant_sig = *sig;
+ cmd->details.contract.h_contract = *h_contract;
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "/contract responded with unexpected status code %u in step %u\n",
+ http_status,
+ is->ip);
+ json_dumpf (obj, stderr, 0);
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
* Function called with the result of a /pay operation.
*
* @param cls closure with the interpreter state
@@ -1016,85 +1041,89 @@ interpreter_run (void *cls)
return;
case OC_CONTRACT:
{
+ json_t *proposal;
+ json_error_t error;
+ /* parse wire details */
+ proposal = json_loads (cmd->details.contract.proposal,
+ JSON_REJECT_DUPLICATES,
+ &error);
+ if (NULL == proposal)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse proposal `%s' at command #%u: %s at %u\n",
+ cmd->details.contract.proposal,
+ is->ip,
+ error.text,
+ (unsigned int) error.column);
+ fail (is);
+ return;
+ }
+ cmd->details.contract.co
+ = TALER_MERCHANT_contract_sign (ctx,
+ MERCHANT_URI "contract",
+ proposal,
+ &contract_cb,
+ is);
+ if (NULL == cmd->details.contract.co)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ return;
}
case OC_PAY:
{
struct TALER_MERCHANT_PayCoin pc;
- struct TALER_Amount amount;
- struct TALER_Amount max_fee;
- json_t *wire;
- json_t *contract;
- struct GNUNET_HashCode h_wire;
- struct GNUNET_HashCode h_contract;
+ uint64_t transaction_id;
struct GNUNET_TIME_Absolute refund_deadline;
struct GNUNET_TIME_Absolute timestamp;
- struct TALER_MerchantSignatureP merchant_sig;
+ struct GNUNET_HashCode h_wire;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ struct GNUNET_HashCode h_contract;
+ struct TALER_Amount total_amount;
+ struct TALER_Amount max_fee;
+ const char *error_name;
+ unsigned int error_line;
/* get amount */
- if (GNUNET_OK !=
- TALER_string_to_amount (cmd->details.pay.total_amount,
- &amount))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse total amount `%s' at %u\n",
- cmd->details.pay.total_amount,
- is->ip);
- fail (is);
- return;
- }
-
- /* get max_fee */
- if (GNUNET_OK !=
- TALER_string_to_amount (cmd->details.pay.max_fee,
- &max_fee))
+ ref = find_command (is,
+ cmd->details.pay.contract_ref);
+ GNUNET_assert (NULL != ref->details.contract.contract);
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse max_fee `%s' at %u\n",
- cmd->details.pay.max_fee,
- is->ip);
- fail (is);
- return;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("transaction_id", &transaction_id),
+ GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline),
+ GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
+ GNUNET_JSON_spec_fixed_auto ("H_wire", &h_wire),
+ TALER_JSON_spec_amount ("amount", &total_amount),
+ TALER_JSON_spec_amount ("max_fee", &max_fee),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (ref->details.contract.contract,
+ spec,
+ &error_name,
+ &error_line))
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Parser failed on %s:%u\n",
+ error_name,
+ error_line);
+ fail (is);
+ return;
+ }
}
- /* parse wire details */
- wire = json_loads (cmd->details.pay.wire_details,
- JSON_REJECT_DUPLICATES,
- NULL);
- if (NULL == wire)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse wire details `%s' at %u\n",
- cmd->details.pay.wire_details,
- is->ip);
- fail (is);
- return;
- }
- TALER_JSON_hash (wire,
- &h_wire);
- json_decref (wire);
-
- /* parse contract */
- contract = json_loads (cmd->details.pay.contract,
- JSON_REJECT_DUPLICATES,
- NULL);
- if (NULL == contract)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse contract details `%s' at instruction %u\n",
- cmd->details.pay.contract,
- is->ip);
- fail (is);
- return;
- }
- TALER_JSON_hash (contract,
+ TALER_JSON_hash (ref->details.contract.contract,
&h_contract);
- json_decref (contract);
/* initialize 'pc' (FIXME: to do in a loop later...) */
{
- const struct Command *ref;
-
memset (&pc, 0, sizeof (pc));
ref = find_command (is,
cmd->details.pay.coin_ref);
@@ -1135,24 +1164,15 @@ interpreter_run (void *cls)
}
}
- if (0 == cmd->details.pay.refund_deadline.rel_value_us)
- refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS; /* no refunds */
- else
- refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.pay.refund_deadline);
- GNUNET_TIME_round_abs (&refund_deadline);
- timestamp = GNUNET_TIME_absolute_get ();
- GNUNET_TIME_round_abs (&timestamp);
- memset (&merchant_sig, 0, sizeof (merchant_sig)); // FIXME: init properly!
- GNUNET_break (0);
cmd->details.pay.ph
= TALER_MERCHANT_pay_wallet (ctx,
MERCHANT_URI "pay",
&h_contract,
- cmd->details.pay.transaction_id,
- &amount,
+ transaction_id,
+ &total_amount,
&max_fee,
&merchant_pub,
- &merchant_sig,
+ &cmd->details.contract.merchant_sig,
timestamp,
refund_deadline,
&h_wire,
@@ -1266,6 +1286,22 @@ do_shutdown (void *cls)
cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL;
}
break;
+ case OC_CONTRACT:
+ if (NULL != cmd->details.contract.co)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_MERCHANT_contract_sign_cancel (cmd->details.contract.co);
+ cmd->details.contract.co = NULL;
+ }
+ if (NULL != cmd->details.contract.contract)
+ {
+ json_decref (cmd->details.contract.contract);
+ cmd->details.contract.contract = NULL;
+ }
+ break;
case OC_PAY:
if (NULL != cmd->details.pay.ph)
{
@@ -1376,57 +1412,32 @@ run (void *cls)
.expected_response_code = MHD_HTTP_OK,
.details.reserve_status.reserve_reference = "create-reserve-1",
.details.reserve_status.expected_balance = "EUR:0" },
- /* Try to pay with the 5 EUR coin (in full) */
+ /* Create contract */
+ { .oc = OC_CONTRACT,
+ .label = "create-contract-1",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.contract.proposal = "{ \"max_fee\":{\"currency\":\"EUR\", \"value\":0, \"fraction\":500000}, \"transaction_id\":1, \"timestamp\":\"\\/Date(42)\\/\", \"refund_deadline\":\"\\/Date(0)\\/\", \"amount\":{\"currency\":\"EUR\", \"value\":5, \"fraction\":0}, \"items\":[ {\"name\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" },
{ .oc = OC_PAY,
.label = "deposit-simple",
.expected_response_code = MHD_HTTP_OK,
- .details.pay.total_amount = "EUR:5",
- .details.pay.max_fee = "EUR:0.5",
+ .details.pay.contract_ref = "create-contract-1",
.details.pay.coin_ref = "withdraw-coin-1",
.details.pay.amount_with_fee = "EUR:5",
- .details.pay.amount_without_fee = "EUR:4.99",
- .details.pay.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":62 }",
- .details.pay.contract = "{ \"items\":[ {\"name\":\"ice cream\", \"value\":1} ] }",
- .details.pay.transaction_id = 1 },
+ .details.pay.amount_without_fee = "EUR:4.99" },
+ /* Create another contract */
+ { .oc = OC_CONTRACT,
+ .label = "create-contract-2",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.contract.proposal = "{ \"max_fee\":\"{EUR:0.5}\", \"transaction_id\":2, \"timestamp\":\"\\/date(42)\\/\", \"total_amount\":\"{EUR:5}\", \"items\":[ {\"name\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" },
- /* Try to double-spend the 5 EUR coin with different wire details */
- { .oc = OC_PAY,
- .label = "deposit-double-1",
- .expected_response_code = MHD_HTTP_FORBIDDEN,
- .details.pay.total_amount = "EUR:5",
- .details.pay.max_fee = "EUR:0.5",
- .details.pay.coin_ref = "withdraw-coin-1",
- .details.pay.amount_with_fee = "EUR:5",
- .details.pay.amount_without_fee = "EUR:4.99",
- .details.pay.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":62 }",
- .details.pay.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":1} ] }",
- .details.pay.transaction_id = 1 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
transaction ID) */
{ .oc = OC_PAY,
.label = "deposit-double-2",
.expected_response_code = MHD_HTTP_FORBIDDEN,
- .details.pay.total_amount = "EUR:5",
- .details.pay.max_fee = "EUR:0.5",
- .details.pay.coin_ref = "withdraw-coin-1",
- .details.pay.amount_with_fee = "EUR:5",
- .details.pay.amount_without_fee = "EUR:4.99",
- .details.pay.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":62 }",
- .details.pay.contract = "{ \"items\":[ {\"name\":\"ice cream\", \"value\":1} ] }",
- .details.pay.transaction_id = 2 },
- /* Try to double-spend the 5 EUR coin at the same merchant (but different
- contract) */
- { .oc = OC_PAY,
- .label = "deposit-double-3",
- .expected_response_code = MHD_HTTP_FORBIDDEN,
- .details.pay.total_amount = "EUR:5",
- .details.pay.max_fee = "EUR:0.5",
- .details.pay.coin_ref = "withdraw-coin-1",
+ .details.pay.contract_ref = "create-contract-2",
.details.pay.amount_with_fee = "EUR:5",
- .details.pay.amount_without_fee = "EUR:4.99",
- .details.pay.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":62 }",
- .details.pay.contract = "{ \"items\":[ {\"name\":\"ice cream\", \"value\":2} ] }",
- .details.pay.transaction_id = 1 },
+ .details.pay.amount_without_fee = "EUR:4.99" },
{ .oc = OC_END }
};