diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-05-05 10:22:32 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-05-05 10:22:32 +0200 |
commit | 404b586176af67b20cae6ba5f918c78987fd6cf6 (patch) | |
tree | 1f699a2b9a4778b7cd9190fbe1160f0820b3b754 | |
parent | 7bd9e5fb6fbf5ca4bb4dda457b2be53f94781656 (diff) | |
download | merchant-404b586176af67b20cae6ba5f918c78987fd6cf6.tar.gz merchant-404b586176af67b20cae6ba5f918c78987fd6cf6.tar.bz2 merchant-404b586176af67b20cae6ba5f918c78987fd6cf6.zip |
fixing issues around testcase
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/lib/merchant_api_contract.c | 6 | ||||
-rw-r--r-- | src/lib/merchant_api_pay.c | 23 | ||||
-rw-r--r-- | src/lib/test_merchant_api.c | 295 |
5 files changed, 183 insertions, 143 deletions
@@ -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", ×tamp), + 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 (×tamp); - 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 } }; |