diff options
author | Marcello Stanisci <marcello.stanisci@inria.fr> | 2015-11-04 16:51:39 +0100 |
---|---|---|
committer | Marcello Stanisci <marcello.stanisci@inria.fr> | 2015-11-04 16:51:39 +0100 |
commit | 79abf2bff00101af5a14f18bcb8877873df8b973 (patch) | |
tree | 89bbe53b48e300c245a2e831a9475d230cf2357b | |
parent | 39e4f50b0a0d921f4910d390aaebd93c6a173867 (diff) | |
download | merchant-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.c | 111 | ||||
-rw-r--r-- | src/backend-lib/merchant_db.h | 17 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 95 |
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", ×tamp), + 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 */ } |