aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2018-01-16 01:30:57 +0100
committerFlorian Dold <florian.dold@gmail.com>2018-01-16 01:30:57 +0100
commitc4fe61ac6d1a74320638eb4f40de7c18d30a4d8c (patch)
tree59b48019b249bf9fc01478bd63ed05649503036b /src
parent50c7fe8506a8f33ffa0f4633c1e693a4c8c9b961 (diff)
fix proposal test cases, fix use after free in refunds
Diffstat (limited to 'src')
-rw-r--r--src/backend/taler-merchant-httpd.c5
-rw-r--r--src/backend/taler-merchant-httpd.h2
-rw-r--r--src/backend/taler-merchant-httpd_proposal.c84
-rw-r--r--src/backend/taler-merchant-httpd_refund.c9
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c1
-rw-r--r--src/include/taler_merchant_service.h20
-rw-r--r--src/lib/merchant_api_proposal.c144
-rw-r--r--src/lib/test_merchant_api.c99
-rw-r--r--src/lib/test_merchant_api.conf5
-rw-r--r--src/merchant-tools/taler-merchant-generate-payments.c75
10 files changed, 349 insertions, 95 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 2bf927db..998d3b04 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -269,7 +269,8 @@ url_handler (void *cls,
&TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
};
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling request for URL `%s'\n",
+ "Handling request (%s) for URL `%s'\n",
+ method,
url);
for (unsigned int i=0;NULL != handlers[i].url;i++)
{
@@ -857,6 +858,8 @@ run (void *cls,
char *wireformat;
int fh;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "running taler-merchant-httpd\n");
+
result = GNUNET_SYSERR;
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index 1e917e7f..a1b0f845 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -72,7 +72,7 @@ struct IterateInstancesCls
/**
- * Information that defines a merchant "instance". That way, a single
+ * Information that defines a merchant "instance". Tha4673t way, a single
* backend can account for several merchants, as used to do in donation
* shops
*/
diff --git a/src/backend/taler-merchant-httpd_proposal.c b/src/backend/taler-merchant-httpd_proposal.c
index c79e2a81..01d3c5a5 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -135,8 +135,6 @@ proposal_put (struct MHD_Connection *connection,
{
int res;
struct MerchantInstance *mi;
- struct TALER_ProposalDataPS pdps;
- struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
struct TALER_Amount total;
const char *order_id;
json_t *products;
@@ -317,9 +315,6 @@ proposal_put (struct MHD_Connection *connection,
TALER_EC_CONTRACT_INSTANCE_UNKNOWN,
"Unknown instance given");
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Signing contract on behalf of instance '%s'\n",
- mi->id);
/* add fields to the contract that the backend should provide */
json_object_set (order,
"exchanges",
@@ -337,23 +332,31 @@ proposal_put (struct MHD_Connection *connection,
"merchant_pub",
GNUNET_JSON_from_data_auto (&mi->pubkey));
- /* create proposal signature */
- pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
- pdps.purpose.size = htonl (sizeof (pdps));
- if (GNUNET_OK !=
- TALER_JSON_hash (order,
- &pdps.hash))
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Inserting order '%s' for instance '%s'\n",
+ order_id,
+ mi->id);
+
+ json_t *dummy_contract_terms;
+ qs = db->find_orders (db->cls,
+ &dummy_contract_terms,
+ order_id,
+ &mi->pubkey);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
{
- GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- return TMH_RESPONSE_reply_internal_error (connection,
- TALER_EC_INTERNAL_LOGIC_ERROR,
- "Could not hash order");
+ if ( (GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
+ (GNUNET_DB_STATUS_HARD_ERROR == qs) )
+ {
+ return TMH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_PROPOSAL_STORE_DB_ERROR,
+ "db error: could not check for existing order");
+ }
+ return TMH_RESPONSE_reply_external_error (connection,
+ TALER_EC_PROPOSAL_STORE_DB_ERROR,
+ "proposal already exists");
}
- GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
- &pdps.purpose,
- &merchant_sig);
+
for (unsigned int i=0;i<MAX_RETRIES;i++)
{
@@ -377,9 +380,11 @@ proposal_put (struct MHD_Connection *connection,
"db error: could not store this proposal's data into db");
}
- res = TMH_RESPONSE_reply_json (connection,
- json_object (),
- MHD_HTTP_OK);
+ res = TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s}",
+ "order_id",
+ order_id);
GNUNET_JSON_parse_free (spec);
return res;
}
@@ -579,7 +584,9 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
}
}
- const char *stored_nonce = json_string_value (json_object_get(contract_terms, "nonce"));
+ GNUNET_assert (NULL != contract_terms);
+
+ const char *stored_nonce = json_string_value (json_object_get (contract_terms, "nonce"));
if (NULL == stored_nonce)
{
@@ -596,10 +603,33 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
"mismatched nonce");
}
- res = TMH_RESPONSE_reply_json (connection,
- contract_terms,
- MHD_HTTP_OK);
- json_decref (contract_terms);
+ struct TALER_ProposalDataPS pdps;
+ struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
+
+ /* create proposal signature */
+ pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
+ pdps.purpose.size = htonl (sizeof (pdps));
+ if (GNUNET_OK !=
+ TALER_JSON_hash (contract_terms,
+ &pdps.hash))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_INTERNAL_LOGIC_ERROR,
+ "Could not hash order");
+ }
+
+ GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
+ &pdps.purpose,
+ &merchant_sig);
+
+ res = TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{ s:o, s:o }",
+ "contract_terms",
+ contract_terms,
+ "sig",
+ GNUNET_JSON_from_data_auto (&merchant_sig));
return res;
}
diff --git a/src/backend/taler-merchant-httpd_refund.c b/src/backend/taler-merchant-httpd_refund.c
index 47ebda0f..8df1ea46 100644
--- a/src/backend/taler-merchant-httpd_refund.c
+++ b/src/backend/taler-merchant-httpd_refund.c
@@ -260,15 +260,16 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
* the information needed to generate the right response.
*/
- json_decref (contract_terms);
- json_decref (root);
- GNUNET_JSON_parse_free (spec);
-
confirmation.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND_OK);
confirmation.purpose.size = htonl (sizeof (struct TALER_MerchantRefundConfirmationPS));
GNUNET_CRYPTO_hash (order_id,
strlen (order_id),
&confirmation.h_order_id);
+
+ json_decref (contract_terms);
+ json_decref (root);
+ GNUNET_JSON_parse_free (spec);
+
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
&confirmation.purpose,
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index ee8a947a..66a35293 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -76,6 +76,7 @@ postgres_drop_tables (void *cls)
GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_pickups CASCADE;"),
GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserve_credits CASCADE;"),
GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserves CASCADE;"),
+ GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_orders CASCADE;"),
GNUNET_PQ_EXECUTE_STATEMENT_END
};
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
index 51db1354..63c396a7 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -147,18 +147,14 @@ struct TALER_MERCHANT_ProposalOperation;
* 0 if the backend's reply is bogus (fails to follow the protocol)
* @param ec taler-specific error code
* @param obj raw JSON reply, or error details if the request failed
- * @param contract_terms completed contract, NULL on error
- * @param sig merchant's signature over the contract, NULL on error
- * @param hash proposal data's hashcode, NULL on error
+ * @param order_id order id of the newly created order
*/
typedef void
(*TALER_MERCHANT_ProposalCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *obj,
- const json_t *contract_terms,
- const struct TALER_MerchantSignatureP *sig,
- const struct GNUNET_HashCode *hash);
+ const char *order_id);
/**
@@ -207,7 +203,10 @@ struct TALER_MERCHANT_ProposalLookupOperation;
typedef void
(*TALER_MERCHANT_ProposalLookupOperationCallback) (void *cls,
unsigned int http_status,
- const json_t *body);
+ const json_t *body,
+ const json_t *contract_terms,
+ const struct TALER_MerchantSignatureP *sig,
+ const struct GNUNET_HashCode *hash);
/**
@@ -216,7 +215,9 @@ typedef void
*
* @param ctx execution context
* @param backend_url base URL of the merchant backend
- * @param transaction_id transaction id used to perform the lookup
+ * @param order_id order id used to perform the lookup
+ * @param nonce nonce to use, only used when requesting the proposal the first time,
+ * can be NULL to omit the nonce (after the first request)
* @param plo_cb callback which will work the response gotten from the backend
* @param plo_cb_cls closure to pass to @a history_cb
* @return handle for this operation, NULL upon errors
@@ -224,8 +225,9 @@ typedef void
struct TALER_MERCHANT_ProposalLookupOperation *
TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx,
const char *backend_url,
- const char *transaction_id,
+ const char *order_id,
const char *instance,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *nonce,
TALER_MERCHANT_ProposalLookupOperationCallback plo_cb,
void *plo_cb_cls);
diff --git a/src/lib/merchant_api_proposal.c b/src/lib/merchant_api_proposal.c
index fa7e6b68..88d902b4 100644
--- a/src/lib/merchant_api_proposal.c
+++ b/src/lib/merchant_api_proposal.c
@@ -99,6 +99,16 @@ struct TALER_MERCHANT_ProposalLookupOperation
*/
struct GNUNET_CURL_Context *ctx;
+ /**
+ * Should we send the lookup operation with a nonce?
+ */
+ int has_nonce;
+
+ /**
+ * Nonce, only initialized if has_nonce is GNUNET_YES.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
};
@@ -116,29 +126,19 @@ handle_proposal_finished (void *cls,
const json_t *json)
{
struct TALER_MERCHANT_ProposalOperation *po = cls;
- json_t *contract_terms;
- const struct TALER_MerchantSignatureP *sigp;
- const struct GNUNET_HashCode *hashp;
- struct TALER_MerchantSignatureP sig;
- struct GNUNET_HashCode hash;
+ const char *order_id;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("order_id", &order_id),
+ GNUNET_JSON_spec_end()
+ };
po->job = NULL;
- contract_terms = NULL;
- sigp = NULL;
- hashp = NULL;
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("data", &contract_terms),
- GNUNET_JSON_spec_fixed_auto ("sig", &sig),
- GNUNET_JSON_spec_fixed_auto ("hash", &hash),
- GNUNET_JSON_spec_end()
- };
-
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
@@ -148,8 +148,6 @@ handle_proposal_finished (void *cls,
response_code = 0;
break;
}
- hashp = &hash;
- sigp = &sig;
}
break;
case MHD_HTTP_BAD_REQUEST:
@@ -183,11 +181,8 @@ handle_proposal_finished (void *cls,
response_code,
TALER_JSON_get_error_code (json),
json,
- contract_terms,
- sigp,
- hashp);
- if (NULL != contract_terms)
- json_decref (contract_terms);
+ order_id);
+ GNUNET_JSON_parse_free (spec);
TALER_MERCHANT_proposal_cancel (po);
}
@@ -267,6 +262,70 @@ handle_proposal_lookup_finished (void *cls,
const json_t *json)
{
struct TALER_MERCHANT_ProposalLookupOperation *plo = cls;
+ json_t *contract_terms;
+ struct TALER_MerchantSignatureP sig;
+ struct GNUNET_HashCode hash;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("contract_terms", &contract_terms),
+ GNUNET_JSON_spec_fixed_auto ("sig", &sig),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (MHD_HTTP_OK != response_code)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "proposal lookup failed with HTTP status code %u\n",
+ (unsigned int) response_code);
+ char *s = json_dumps (json, JSON_COMPACT);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "error json: %s\n",
+ s);
+ GNUNET_free_non_null (s);
+ GNUNET_break_op (0);
+ plo->cb (plo->cb_cls,
+ response_code,
+ json,
+ NULL,
+ NULL,
+ NULL);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "proposal lookup failed to parse JSON\n");
+ char *s = json_dumps (json, JSON_COMPACT);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "proposal json was: %s\n",
+ s);
+ GNUNET_free_non_null (s);
+ GNUNET_break_op (0);
+ plo->cb (plo->cb_cls,
+ 0,
+ json,
+ NULL,
+ NULL,
+ NULL);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_JSON_hash (contract_terms, &hash))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "proposal lookup failed to hash proposal JSON\n");
+ GNUNET_break (0);
+ plo->cb (plo->cb_cls,
+ 0,
+ json,
+ NULL,
+ NULL,
+ NULL);
+ return;
+ }
plo->job = NULL;
/**
@@ -275,7 +334,10 @@ handle_proposal_lookup_finished (void *cls,
*/
plo->cb (plo->cb_cls,
response_code,
- json);
+ json,
+ contract_terms,
+ &sig,
+ &hash);
TALER_MERCHANT_proposal_lookup_cancel (plo);
}
@@ -286,7 +348,8 @@ handle_proposal_lookup_finished (void *cls,
*
* @param ctx execution context
* @param backend_uri base URL of the merchant backend
- * @param transaction_id transaction id used to perform the lookup
+ * @param order_id order id used to perform the lookup
+ * @param nonce nonce used to perform the lookup
* @param plo_cb callback which will work the response gotten from the backend
* @param plo_cb_cls closure to pass to @a history_cb
* @return handle for this operation, NULL upon errors
@@ -296,6 +359,7 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx,
const char *backend_uri,
const char *order_id,
const char *instance,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *nonce,
TALER_MERCHANT_ProposalLookupOperationCallback plo_cb,
void *plo_cb_cls)
{
@@ -309,11 +373,29 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx,
plo->cb_cls = plo_cb_cls;
base = MAH_path_to_url_ (backend_uri,
"/proposal");
- GNUNET_asprintf (&plo->url,
- "%s?order_id=%s&instance=%s",
- base,
- order_id,
- instance);
+ if (NULL != nonce)
+ {
+ char *nonce_str;
+ plo->has_nonce = GNUNET_YES;
+ plo->nonce = *nonce;
+ nonce_str = GNUNET_STRINGS_data_to_string_alloc (nonce, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ GNUNET_assert (NULL != nonce_str);
+ GNUNET_asprintf (&plo->url,
+ "%s?order_id=%s&instance=%s&nonce=%s",
+ base,
+ order_id,
+ instance,
+ nonce_str);
+ GNUNET_free (nonce_str);
+ }
+ else
+ {
+ GNUNET_asprintf (&plo->url,
+ "%s?order_id=%s&instance=%s",
+ base,
+ order_id,
+ instance);
+ }
GNUNET_free (base);
eh = curl_easy_init ();
if (CURLE_OK != curl_easy_setopt (eh,
@@ -324,6 +406,10 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx,
return NULL;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "looking up proposal from %s\n",
+ plo->url);
+
if (NULL == (plo->job = GNUNET_CURL_job_add (ctx,
eh,
GNUNET_YES,
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index b09d78a3..ea119a12 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -500,6 +500,11 @@ struct Command
struct TALER_MERCHANT_ProposalOperation *po;
/**
+ * Handle to the active GET /proposal operation, or NULL.
+ */
+ struct TALER_MERCHANT_ProposalLookupOperation *plo;
+
+ /**
* Full contract in JSON, set by the /contract operation.
* FIXME: verify in the code that this bit is actually proposal
* data and not the whole proposal.
@@ -516,6 +521,12 @@ struct Command
*/
struct GNUNET_HashCode hash;
+ /**
+ * The nonce set by the customer looking up the contract
+ * the first time.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
} proposal;
/**
@@ -1538,6 +1549,39 @@ reserve_withdraw_cb (void *cls,
/**
+ * Callback for GET /proposal issued at backend.
+ * Used to initialize the proposal after it was created.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got
+ * @param json full response we got
+ */
+static void
+proposal_lookup_initial_cb (void *cls,
+ unsigned int http_status,
+ const json_t *json,
+ const json_t *contract_terms,
+ const struct TALER_MerchantSignatureP *sig,
+ const struct GNUNET_HashCode *hash)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ if (cmd->expected_response_code != http_status)
+ {
+ fail (is);
+ }
+
+ cmd->details.proposal.hash = *hash;
+ cmd->details.proposal.merchant_sig = *sig;
+ cmd->details.proposal.contract_terms = json_deep_copy (contract_terms);
+
+ cmd->details.proposal.plo = NULL;
+ next_command (is);
+}
+
+
+/**
* Callback that works POST /proposal's output.
*
* @param cls closure
@@ -1556,9 +1600,7 @@ proposal_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
const json_t *obj,
- const json_t *contract_terms,
- const struct TALER_MerchantSignatureP *sig,
- const struct GNUNET_HashCode *hash)
+ const char *order_id)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
@@ -1567,22 +1609,32 @@ proposal_cb (void *cls,
switch (http_status)
{
case MHD_HTTP_OK:
- cmd->details.proposal.contract_terms = json_incref ((json_t *) contract_terms);
- cmd->details.proposal.merchant_sig = *sig;
- cmd->details.proposal.hash = *hash;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Hashed proposal is `%s'\n",
- GNUNET_h2s (hash));
break;
- default:
+ default: {
+ char *s = json_dumps (obj, JSON_COMPACT);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected status code from /proposal: %u. Step %u\n",
+ "Unexpected status code from /proposal: %u. Step %u, response: %s\n",
http_status,
- is->ip);
+ is->ip,
+ s);
+ GNUNET_free_non_null (s);
fail (is);
+ }
return;
}
- next_command (is);
+
+ if (NULL == (cmd->details.proposal.plo
+ = TALER_MERCHANT_proposal_lookup (ctx,
+ MERCHANT_URL,
+ order_id,
+ instance,
+ &cmd->details.proposal.nonce,
+ proposal_lookup_initial_cb,
+ is)))
+ {
+ GNUNET_break (0);
+ fail (is);
+ }
}
@@ -2011,7 +2063,10 @@ track_transfer_cb (void *cls,
static void
proposal_lookup_cb (void *cls,
unsigned int http_status,
- const json_t *json)
+ const json_t *json,
+ const json_t *contract_terms,
+ const struct TALER_MerchantSignatureP *sig,
+ const struct GNUNET_HashCode *hash)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
@@ -2421,12 +2476,20 @@ cleanup_state (struct InterpreterState *is)
if (NULL != cmd->details.proposal.po)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Command %u (%s) did not complete\n",
+ "Command %u (%s) did not complete (proposal put)\n",
i,
cmd->label);
TALER_MERCHANT_proposal_cancel (cmd->details.proposal.po);
cmd->details.proposal.po = NULL;
}
+ if (NULL != cmd->details.proposal.plo)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete (proposal lookup)\n",
+ i,
+ cmd->label);
+ TALER_MERCHANT_proposal_lookup_cancel (cmd->details.proposal.plo);
+ }
if (NULL != cmd->details.proposal.contract_terms)
{
json_decref (cmd->details.proposal.contract_terms);
@@ -2861,11 +2924,13 @@ interpreter_run (void *cls)
order_id = json_string_value (json_object_get (ref->details.proposal.contract_terms,
"order_id"));
+
if (NULL == (cmd->details.proposal_lookup.plo
= TALER_MERCHANT_proposal_lookup (ctx,
MERCHANT_URL,
order_id,
instance,
+ &ref->details.proposal.nonce,
proposal_lookup_cb,
is)))
{
@@ -3034,6 +3099,10 @@ interpreter_run (void *cls)
GNUNET_assert (NULL != (order = json_loads (cmd->details.proposal.order,
JSON_REJECT_DUPLICATES,
&error)));
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &cmd->details.proposal.nonce,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+
if (NULL != instance)
{
json_t *merchant;
diff --git a/src/lib/test_merchant_api.conf b/src/lib/test_merchant_api.conf
index c4a4eb7f..67ef46bf 100644
--- a/src/lib/test_merchant_api.conf
+++ b/src/lib/test_merchant_api.conf
@@ -77,22 +77,27 @@ MASTER_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
[merchant-instance-default]
KEYFILE = test_merchant.priv
+NAME = Kudos Inc.
[merchant-instance-wireformat-default]
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json
[merchant-instance-tor]
KEYFILE = tor_merchant.priv
+NAME = The Tor Project
[merchant-instance-tip]
KEYFILE = reserve_tip.priv
TIP_EXCHANGE = http://localhost:8084/
TIP_RESERVE_PRIV_FILENAME = reserve_key.priv
+NAME = Test Tipping Merchant
[merchant-instance-dtip]
KEYFILE = reserve_dtip.priv
TIP_EXCHANGE = http://localhost:8084/
TIP_RESERVE_PRIV_FILENAME = reserve_dkey.priv
+NAME = Test Tipping Merchant 2
+
[merchant-instance-wireformat-tor]
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json
diff --git a/src/merchant-tools/taler-merchant-generate-payments.c b/src/merchant-tools/taler-merchant-generate-payments.c
index 887b9db4..7d40473f 100644
--- a/src/merchant-tools/taler-merchant-generate-payments.c
+++ b/src/merchant-tools/taler-merchant-generate-payments.c
@@ -387,6 +387,11 @@ struct Command
struct TALER_MERCHANT_ProposalOperation *po;
/**
+ * Handle to the active GET /proposal operation, or NULL
+ */
+ struct TALER_MERCHANT_ProposalLookupOperation *plo;
+
+ /**
* Full contract in JSON, set by the /contract operation.
* FIXME: verify in the code that this bit is actually proposal
* data and not the whole proposal.
@@ -519,7 +524,7 @@ next_command (struct InterpreterState *is)
/**
- * Callback that works PUT /proposal's output.
+ * Callback that processes GET /proposal's output.
*
* @param cls closure
* @param http_status HTTP response code, 200 indicates success;
@@ -533,18 +538,17 @@ next_command (struct InterpreterState *is)
* @param h_contract hash of the contract, NULL on error
*/
static void
-proposal_cb (void *cls,
- unsigned int http_status,
- enum TALER_ErrorCode ec,
- const json_t *obj,
- const json_t *contract_terms,
- const struct TALER_MerchantSignatureP *sig,
- const struct GNUNET_HashCode *hash)
+proposal_lookup_cb (void *cls,
+ unsigned int http_status,
+ const json_t *obj,
+ const json_t *contract_terms,
+ const struct TALER_MerchantSignatureP *sig,
+ const struct GNUNET_HashCode *hash)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
- cmd->details.proposal.po = NULL;
+ cmd->details.proposal.plo = NULL;
switch (http_status)
{
case MHD_HTTP_OK:
@@ -568,6 +572,59 @@ proposal_cb (void *cls,
next_command (is);
}
+/**
+ * Callback that works PUT /proposal's output.
+ *
+ * @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 ec taler-specific error code
+ * @param obj the full received JSON reply, or
+ * error details if the request failed
+ */
+static void
+proposal_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const json_t *obj,
+ const char *order_id)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.proposal.po = NULL;
+ switch (http_status)
+ {
+ case MHD_HTTP_OK: {
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+ struct GNUNET_CRYPTO_EddsaPublicKey pub;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
+ GNUNET_free (priv);
+
+ cmd->details.proposal.plo
+ = TALER_MERCHANT_proposal_lookup (ctx,
+ merchant_url,
+ order_id,
+ instance,
+ &pub,
+ &proposal_lookup_cb,
+ is);
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "unexpected status code from /proposal: %u. Step %u\n",
+ http_status,
+ is->ip);
+ json_dumpf (obj, stderr, 0);
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+}
+
/**
* Function called with the result of a /pay operation.