summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2015-11-04 16:51:39 +0100
committerMarcello Stanisci <marcello.stanisci@inria.fr>2015-11-04 16:51:39 +0100
commit79abf2bff00101af5a14f18bcb8877873df8b973 (patch)
tree89bbe53b48e300c245a2e831a9475d230cf2357b
parent39e4f50b0a0d921f4910d390aaebd93c6a173867 (diff)
downloadmerchant-79abf2bff00101af5a14f18bcb8877873df8b973.tar.gz
merchant-79abf2bff00101af5a14f18bcb8877873df8b973.tar.bz2
merchant-79abf2bff00101af5a14f18bcb8877873df8b973.zip
adding the call to /deposit (through mint-lib). To test
-rw-r--r--src/backend-lib/merchant_db.c111
-rw-r--r--src/backend-lib/merchant_db.h17
-rw-r--r--src/backend/taler-merchant-httpd_pay.c95
3 files changed, 208 insertions, 15 deletions
diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c
index ed89b1ac..415ff223 100644
--- a/src/backend-lib/merchant_db.c
+++ b/src/backend-lib/merchant_db.c
@@ -106,9 +106,9 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
"amount_fraction INT4 NOT NULL,"
"coin_sig BYTEA NOT NULL);"
"CREATE %1$s TABLE IF NOT EXISTS deposits ("
- "dep_perm TEXT NOT NULL"
- "transaction_id INT8 NOT NULL"
- "pending INT4 NOT NULL"
+ "dep_perm TEXT NOT NULL,"
+ "transaction_id INT8 PRIMARY KEY,"
+ "pending INT4 NOT NULL,"
"mint_url TEXT NOT NULL);",
tmp_str);
ret = GNUNET_POSTGRES_exec (conn, sql);
@@ -174,6 +174,18 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
"INSERT INTO deposits"
"(dep_perm, transaction_id, pending, mint_url) "
"VALUES ($1, $2, $3, $4);", 4, NULL)));
+
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
+ PQclear (res);
+
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "update_deposit_permission",
+ "UPDATE deposits "
+ "SET pending = $1 "
+ "WHERE transaction_id = $2", 2, NULL)));
+
EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
PQclear (res);
@@ -233,13 +245,73 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
return GNUNET_SYSERR;
}
+/**
+ * Update the pending column of a deposit permission
+ * @param conn handle to DB
+ * @param transaction_id identification number of the deposit to
+ * update
+ * @param pending true if still pending, false otherwise (i.e. the
+ * mint did respond something)
+ * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
+ */
+uint32_t
+MERCHANT_DB_update_deposit_permission (PGconn *conn,
+ uint64_t transaction_id,
+ unsigned int pending)
+{
+ PGresult *res;
+ ExecStatusType status;
+
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_uint32 (&pending),
+ };
+
+ res = TALER_PQ_exec_prepared (conn, "update_deposit_permission", params);
+ status = PQresultStatus (res);
+
+ if (PGRES_COMMAND_OK != status)
+ {
+
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+}
+/**
+ * Store a deposit permission in DB. To be mainly used if /deposit should
+ * be retried; also, the merchant can benefit from this information in case
+ * he needs to later investigate about some transaction_id.
+ * @param conn DB handle
+ * @param transaction_id identification number of this payment (which is the
+ * same id of the related contract)
+ * @param pending if true, this payment got to a persistent state
+ * @param which mint is to get this deposit permission
+ * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
+ */
+uint32_t
MERCHANT_DB_store_deposit_permission (PGconn *conn,
const char *deposit_permission,
uint64_t transaction_id,
unsigned int pending,
const char *mint_url)
{
+ PGresult *res;
+ ExecStatusType status;
+
struct TALER_PQ_QueryParam params[] = {
TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)),
TALER_PQ_query_param_uint64 (&transaction_id),
@@ -247,7 +319,40 @@ MERCHANT_DB_store_deposit_permission (PGconn *conn,
TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)),
TALER_PQ_query_param_end
};
+ res = TALER_PQ_exec_prepared (conn, "store_deposit_permission", params);
+ status = PQresultStatus (res);
+ if (PGRES_COMMAND_OK != status)
+ {
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ /* 40P01: deadlock, 40001: serialization failure */
+ if ( (0 == strcmp (sqlstate,
+ "23505")))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Inserting same transaction id twice\n");
+ /* Primary key violation */
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (res);
+ return GNUNET_OK;
}
/**
diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h
index 540bddbd..c748002e 100644
--- a/src/backend-lib/merchant_db.h
+++ b/src/backend-lib/merchant_db.h
@@ -164,4 +164,21 @@ MERCHANT_DB_get_contract_handle (PGconn *conn,
const struct GNUNET_HashCode *h_contract,
struct MERCHANT_contract_handle *contract_handle);
+/**
+ * Store a deposit permission in DB. To be mainly used if /deposit should
+ * be retried; also, the merchant can benefit from this information in case
+ * he needs to later investigate about some transaction_id.
+ * @param conn DB handle
+ * @param transaction_id identification number of this payment (which is the
+ * same id of the related contract)
+ * @param pending if true, this payment got to a persistent state
+ * @param which mint is to get this deposit permission
+ * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
+ */
+uint32_t
+MERCHANT_DB_store_deposit_permission (PGconn *conn,
+ const char *deposit_permission,
+ uint64_t transaction_id,
+ unsigned int pending,
+ const char *mint_url);
/* end of merchant-db.h */
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index 1c2a39a6..a08dd5e2 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -101,6 +101,26 @@ deposit_fee_from_coin_aggregate (struct MHD_Connection *connection,
return GNUNET_OK;
}
+
+/**
+ * Callback to handle a deposit permission's response. How does this behave if the mint
+ * goes offline during a call?
+ * @param cls closure (in our invocation it will be the transaction_id, so the cb can refer
+ * to the right DB row for this deposit permission)
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
+ * 0 if the mint's reply is bogus (fails to follow the protocol)
+ * @param proof the received JSON reply, should be kept as proof (and, in case of errors,
+ * be forwarded to the customer)
+ */
+void
+deposit_cb (void *cls, unsigned int http_status, json_t *proof)
+{
+ if (MHD_HTTP_OK == http_status)
+ ; /* set pending to false in DB and notify the frontend with "OK" */
+ else
+ ; /* set a timeout to retry */
+}
+
/**
* Accomplish this payment.
* @param rh context of the handler
@@ -124,26 +144,47 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
json_t *coins;
char *chosen_mint;
json_t *coin_aggregate;
+ json_t *wire_details;
unsigned int coins_cnt;
unsigned int mint_index; /*a cell in the global array*/
uint64_t transaction_id;
int res;
+ struct TALER_MINT_DepositHandle *dh;
struct TALER_Amount max_fee;
struct TALER_Amount acc_fee;
struct TALER_Amount coin_fee;
+ struct TALER_Amount amount;
struct GNUNET_TIME_Absolute edate;
struct GNUNET_TIME_Absolute timestamp;
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
+ struct GNUNET_TIME_Absolute refund_deadline;
+ struct TALER_MerchantPublicKeyP pubkey;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_DenominationPublicKey denom_pub;
+ struct TALER_DenominationSignature ub_sig;
+ struct TALER_CoinSpendSignatureP coin_sig;
+ struct GNUNET_HashCode h_contract;
struct TMH_PARSE_FieldSpecification spec[] = {
TMH_PARSE_member_array ("coins", &coins),
TMH_PARSE_member_string ("mint", &chosen_mint),
TMH_PARSE_member_amount ("max_fee", &max_fee),
TMH_PARSE_member_time_abs ("timestamp", &timestamp),
+ TMH_PARSE_member_time_abs ("refund_deadline", &refund_deadline),
TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
+ TMH_PARSE_member_fixed ("H_contract", &h_contract),
TMH_PARSE_MEMBER_END
};
+
+ struct TMH_PARSE_FieldSpecification coin_aggregate_spec[] = {
+ TMH_PARSE_member_amount ("f", &amount),
+ TMH_PARSE_member_fixed ("coin_pub", &coin_pub.eddsa_pub),
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &denom_pub),
+ TMH_PARSE_member_denomination_signature ("ub_sig", &ub_sig),
+ TMH_PARSE_member_fixed ("coin_sig", &coin_sig.eddsa_signature),
+ TMH_PARSE_MEMBER_END
+ };
+
res = TMH_PARSE_post_json (connection,
connection_cls,
upload_data,
@@ -242,30 +283,60 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
"malformed/non-existent 'coins' field");
/* adding our public key to deposit permission */
- GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey);
+ GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey.eddsa_pub);
json_object_set_new (root,
"merchant_pub",
TALER_json_from_data (&pubkey, sizeof (pubkey)));
+ wire_details = MERCHANT_get_wire_json (wire, salt);
json_array_foreach (coins, coins_cnt, coin_aggregate)
{
- /* melt single coin with deposit permission "template" */
+
+ /* 3 For each coin in DB
+
+ a. Generate a deposit permission
+ b. store it and its tid in DB
+ c. POST to the mint (see mint-lib for this)
+ (retry until getting a persisten state)
+ */
+
+
+ /* a */
if (-1 == json_object_update (root, coin_aggregate))
return TMH_RESPONSE_reply_internal_error (connection, "deposit permission not generated");
- /* store a stringification of it (paired with its transaction id)
- into DB */
+ /* b */
char *deposit_permission_str = json_dumps (root, JSON_COMPACT);
+ if (GNUNET_OK != MERCHANT_DB_store_deposit_permission (db_conn,
+ deposit_permission_str,
+ transaction_id,
+ 1,
+ mints[mint_index].hostname))
+ return TMH_RESPONSE_reply_internal_error (connection, "internal DB failure");
+ res = TMH_PARSE_json_data (connection,
+ coin_aggregate,
+ coin_aggregate_spec);
+ if (GNUNET_OK != res)
+ return res; /* may return GNUNET_NO */
+
+
+ dh = TALER_MINT_deposit (mints[mint_index].conn,
+ &amount,
+ wire_details,
+ &h_contract,
+ &coin_pub,
+ &ub_sig,
+ &denom_pub,
+ timestamp,
+ transaction_id,
+ &pubkey,
+ refund_deadline,
+ &coin_sig,
+ deposit_cb,
+ &transaction_id); /*may be destroyed by the time the cb gets called..*/
}
- /* 3 For each coin in DB
-
- a. Generate a deposit permission
- b. store it and its tid in DB
- c. POST to the mint (see mint-lib for this)
- (retry until getting a persisten state)
- */
/* 4 Return response code: success, or whatever data the
mint sent back regarding some bad coin */
}