merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 2e4b1eb7a968833300b8a582835ef97ff11e82c4
parent 66a1cb6f783d1fd2c3ae08cd4a0699084590d0e8
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Tue, 20 Oct 2015 16:08:47 +0200

Making the "non official" way of verifying a deposit
confirmation compile.

Diffstat:
Msrc/backend-lib/Makefile.am | 1+
Msrc/backend-lib/merchant_db.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backend-lib/merchant_db.h | 39+++++++++++++++++++++++++++++++++++++++
Msrc/backend-lib/taler-merchant-httpd_contract.c | 17-----------------
Asrc/backend-lib/taler-merchant-httpd_deposit.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backend-lib/taler_merchant_contract_lib.h | 3+--
Asrc/backend-lib/taler_merchant_deposit_lib.h | 28++++++++++++++++++++++++++++
Msrc/backend-lib/taler_merchant_lib.h | 1+
Msrc/backend/taler-merchant-httpd.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/include/merchant.h | 13+++++++++++++
10 files changed, 354 insertions(+), 84 deletions(-)

diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am @@ -10,6 +10,7 @@ include_HEADERS = \ libtalermerchant_la_SOURCES = \ taler-merchant-httpd_contract.c \ + taler-merchant-httpd_deposit.c \ taler_merchant_contract_lib.h \ merchant_db.c merchant_db.h \ merchant.h diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c @@ -150,6 +150,18 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); PQclear (res); + EXITIF (NULL == (res = PQprepare + (conn, + "get_contract_set", + "SELECT " + "contract_id, nounce, timestamp, edate, " + "refund_deadline FROM contracts " + "WHERE (" + "hash=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); EXITIF (NULL == (res = PQprepare (conn, @@ -460,3 +472,57 @@ MERCHANT_DB_get_contract_values (PGconn *conn, PQclear (res); return GNUNET_SYSERR; } + +/** +* Get a set of values representing a contract. This function is meant +* to obsolete the '_get_contract_values' version. +* @param h_contract the hashcode of this contract +* @param contract_handle where to store the results +* @raturn GNUNET_OK in case of success, GNUNET_SYSERR +* upon errors +* +*/ + +uint32_t +MERCHANT_DB_get_contract_handle (PGconn *conn, + const struct GNUNET_HashCode *h_contract, + struct MERCHANT_contract_handle *contract_handle) +{ + struct MERCHANT_contract_handle ch; + PGresult *res; + ExecStatusType status; + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), + TALER_PQ_query_param_end + }; + + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("nounce", &ch.nounce), + TALER_PQ_result_spec_absolute_time ("edate", &ch.edate), + TALER_PQ_result_spec_absolute_time ("timestamp", &ch.timestamp), + TALER_PQ_result_spec_absolute_time ("refund_deadline", &ch.refund_deadline), + TALER_PQ_result_spec_uint64 ("contract_id", &ch.contract_id), + TALER_PQ_result_spec_end + }; + + res = TALER_PQ_exec_prepared (conn, "get_contract_set", params); + + status = PQresultStatus (res); + EXITIF (PGRES_TUPLES_OK != status); + if (0 == PQntuples (res)) + { + TALER_LOG_DEBUG ("Contract not found"); + goto EXITIF_exit; + } + + EXITIF (1 != PQntuples (res)); + EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); + *contract_handle = ch; + PQclear (res); + return GNUNET_OK; + + EXITIF_exit: + PQclear (res); + return GNUNET_SYSERR; +} diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h @@ -26,6 +26,30 @@ #include <gnunet/gnunet_postgres_lib.h> #include <taler/taler_util.h> +/* Set of values that represent a contract. To be expanded on an + as-needed basis */ +struct MERCHANT_contract_handle +{ + /* The nounce used when hashing the wire details + for this contract */ + uint64_t nounce; + + /* The maximum time when the merchant expects the money tranfer + to his bank account to happen */ + struct GNUNET_TIME_Absolute edate; + + /* The time when this contract was generated */ + struct GNUNET_TIME_Absolute timestamp; + + /* The maximum time until which the merchant could issue a + refund to the customer */ + struct GNUNET_TIME_Absolute refund_deadline; + + /* The identification number for this contract */ + uint64_t contract_id; + +}; + /** * Connect to postgresql database * @@ -125,4 +149,19 @@ MERCHANT_DB_get_contract_values (PGconn *conn, #endif /* MERCHANT_DB_H */ +/** +* Get a set of values representing a contract. This function is meant +* to obsolete the '_get_contract_values' version. +* @param h_contract the hashcode of this contract +* @param contract_handle where to store the results +* @raturn GNUNET_OK in case of success, GNUNET_SYSERR +* upon errors +* +*/ + +uint32_t +MERCHANT_DB_get_contract_handle (PGconn *conn, + const struct GNUNET_HashCode *h_contract, + struct MERCHANT_contract_handle *contract_handle); + /* end of merchant-db.h */ diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c @@ -6,23 +6,6 @@ #include "merchant_db.h" #include "taler_merchant_contract_lib.h" - -/* TODO: make this file a library, and programmatically call the following - * functions */ - -/** - * Macro to round microseconds to seconds in GNUNET_TIME_* structs. - */ -#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000) - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - /** * Take the global wire details and return a JSON containing them, * compliantly with the Taler's API. diff --git a/src/backend-lib/taler-merchant-httpd_deposit.c b/src/backend-lib/taler-merchant-httpd_deposit.c @@ -0,0 +1,88 @@ +#include "platform.h" +#include <jansson.h> +#include <taler/taler_signatures.h> +#include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> +#include "merchant.h" +#include "merchant_db.h" +#include "taler_merchant_contract_lib.h" + +/** +* Verify the signature on a successful deposit permission +* @param h_contract the hashed stringification of this contract +* @param h_wire the hashed 'wire' object holdign the merchant bank's details +* @param timestamp the 32bit wide number representing the number of seconds +* since the Epoch +* @param refund the refund deadline for this deal, expressed in seconds as @a +* timestamp +* @param trans_id an id number for this deal +* @param amount_minus_fee what paid minus its deposit fee +* @param coin_pub the coin's public key +* @param sig the mint's signature +* @param mint_pub mint's key to verify this signature against +* @return GNUNET_OK if the verification succeeds, GNUNET_NO if not, +* GNUNET_SYSERR upon errors +*/ + +uint32_t +MERCHANT_verify_confirmation (const struct GNUNET_HashCode *h_contract, + const struct GNUNET_HashCode *h_wire, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute refund, + uint64_t trans_id, + const struct TALER_Amount *amount_minus_fee, + const struct TALER_CoinSpendPublicKeyP *coin, + const struct TALER_MerchantPublicKeyP *merchant, + const struct GNUNET_CRYPTO_EddsaSignature *sig, + const struct TALER_MintPublicKeyP *mint_pub) +{ + struct TALER_DepositConfirmationPS dc; + + dc.h_contract = *h_contract; + dc.h_wire = *h_wire; + + dc.merchant = *merchant; + dc.coin_pub = *coin; + + dc.timestamp = GNUNET_TIME_absolute_hton (timestamp); + dc.refund_deadline = GNUNET_TIME_absolute_hton (refund); + TALER_amount_hton (&dc.amount_without_fee, amount_minus_fee); + dc.transaction_id = GNUNET_htonll (trans_id); + + #ifdef DEBUG + char *hwire_enc; + char *hcontract_enc; + char *merchant_enc; + char *coinpub_enc; + + hwire_enc = GNUNET_STRINGS_data_to_string_alloc (h_wire, sizeof (struct GNUNET_HashCode)); + hcontract_enc = GNUNET_STRINGS_data_to_string_alloc (h_contract, sizeof (struct GNUNET_HashCode)); + merchant_enc = GNUNET_STRINGS_data_to_string_alloc (&merchant.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + coinpub_enc = GNUNET_STRINGS_data_to_string_alloc (&coin.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + + printf ("Signing Confirmation:\nH_wire: %s\nH_contract: %s\nmerchant_pub: %s\ncoin_pub: %s\n" + "timestamp: %llu,\nrefund: %llu,\namount: %s %llu.%lu,\ntrid: %llu\n", + hwire_enc, + hcontract_enc, + merchant_enc, + coinpub_enc, + timestamp_abs.abs_value_us, + refund_abs.abs_value_us, + amount_minus_fee->currency, + amount_minus_fee->value, + amount_minus_fee->fraction, + trans_id); + #endif + + dc.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT); + dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)); + + if (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT, + &dc.purpose, + sig, + &mint_pub->eddsa_pub)) + return GNUNET_NO; + return GNUNET_OK; +} + diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h @@ -1,6 +1,5 @@ /** - * Simplified version of the contract to be signed, meant to obsolete - * 'struct ContractNBO'. + * The contract sent by the merchant to the wallet */ struct Contract { diff --git a/src/backend-lib/taler_merchant_deposit_lib.h b/src/backend-lib/taler_merchant_deposit_lib.h @@ -0,0 +1,28 @@ +/** +* Verify the signature on a successful deposit permission +* @param h_contract the hashed stringification of this contract +* @param h_wire the hashed 'wire' object holdign the merchant bank's details +* @param timestamp the 32bit wide number representing the number of seconds +* since the Epoch +* @param refund the refund deadline for this deal, expressed in seconds as @a +* timestamp +* @param trans_id an id number for this deal +* @param amount_minus_fee what paid minus its deposit fee +* @param coin_pub the coin's public key +* @param sig the mint's signature +* @param mint_pub mint's key to verify this signature against +* @return GNUNET_OK if the verification succeeds, GNUNET_NO if not, +* GNUNET_SYSERR upon errors +*/ + +uint32_t +MERCHANT_verify_confirmation (const struct GNUNET_HashCode *h_contract, + const struct GNUNET_HashCode *h_wire, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute refund, + uint64_t trans_id, + const struct TALER_Amount *amount_minus_fee, + const struct TALER_CoinSpendPublicKeyP *coin, + const struct TALER_MerchantPublicKeyP *merchant, + const struct GNUNET_CRYPTO_EddsaSignature *sig, + const struct TALER_MintPublicKeyP *mint_pub); diff --git a/src/backend-lib/taler_merchant_lib.h b/src/backend-lib/taler_merchant_lib.h @@ -1 +1,2 @@ #include "taler_merchant_contract_lib.h" +#include "taler_merchant_deposit_lib.h" diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -15,10 +15,10 @@ */ /** -* @file merchant/backend/taler-merchant-httpd.c -* @brief HTTP serving layer mainly intended to communicate with the frontend -* @author Marcello Stanisci -*/ + * @file merchant/backend/taler-merchant-httpd.c + * @brief HTTP serving layer mainly intended to communicate with the frontend + * @author Marcello Stanisci + */ #include "platform.h" #include <microhttpd.h> @@ -152,15 +152,14 @@ struct Mint_Response /** -* Generate the 'hello world' response -* @param connection a MHD connection -* @param resp where to store the response for the calling function. -* Note that in its original implementation this parameter was preceeded -* by a '_'. Still not clear why. -* @return HTTP status code reflecting the operation outcome -* -*/ - + * Generate the 'hello world' response + * @param connection a MHD connection + * @param resp where to store the response for the calling function. + * Note that in its original implementation this parameter was preceeded + * by a '_'. Still not clear why. + * @return HTTP status code reflecting the operation outcome + * + */ static unsigned int generate_hello (struct MHD_Response **resp) { @@ -175,14 +174,13 @@ generate_hello (struct MHD_Response **resp) } /** -* Return the given message to the other end of connection -* @msg (0-terminated) message to show -* @param connection a MHD connection -* @param resp where to store the response for the calling function -* @return HTTP status code reflecting the operation outcome -* -*/ - + * Return the given message to the other end of connection + * @msg (0-terminated) message to show + * @param connection a MHD connection + * @param resp where to store the response for the calling function + * @return HTTP status code reflecting the operation outcome + * + */ static unsigned int generate_message (struct MHD_Response **resp, const char *msg) { @@ -196,24 +194,24 @@ generate_message (struct MHD_Response **resp, const char *msg) } /** -* Callback to pass to curl used to store a HTTP response -* in a custom memory location. -* See http://curl.haxx.se/libcurl/c/getinmemory.html for a -* detailed example -* -* @param contents the data gotten so far from the server -* @param size symbolic (arbitrarily chosen by libcurl) unit -* of bytes -* @param nmemb factor to multiply by @a size to get the real -* size of @a contents -* @param userdata a pointer to a memory location which remains -* the same across all the calls to this callback (i.e. it has -* to be grown at each invocation of this callback) -* @return number of written bytes -* See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html -* for official documentation -* -*/ + * Callback to pass to curl used to store a HTTP response + * in a custom memory location. + * See http://curl.haxx.se/libcurl/c/getinmemory.html for a + * detailed example + * + * @param contents the data gotten so far from the server + * @param size symbolic (arbitrarily chosen by libcurl) unit + * of bytes + * @param nmemb factor to multiply by @a size to get the real + * size of @a contents + * @param userdata a pointer to a memory location which remains + * the same across all the calls to this callback (i.e. it has + * to be grown at each invocation of this callback) + * @return number of written bytes + * See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html + * for official documentation + * + */ size_t get_response_in_memory (char *contents, size_t size, @@ -264,14 +262,12 @@ mhd_panic_cb (void *cls, #endif /** -* Manage a non 200 HTTP status. I.e. it shows a 'failure' page to -* the client -* @param connection the channel thorugh which send the message -* @status the HTTP status to examine -* @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error -* -*/ - + * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to + * the client + * @param connection the channel thorugh which send the message + * @status the HTTP status to examine + * @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error + */ static int failure_resp (struct MHD_Connection *connection, unsigned int status) { @@ -373,7 +369,6 @@ request</h3></center></body></html>"; * #MHD_NO if the socket must be closed due to a serios * error while handling the request */ - static int url_handler (void *cls, struct MHD_Connection *connection, @@ -389,6 +384,7 @@ url_handler (void *cls, unsigned long status; unsigned int no_destroy; struct GNUNET_CRYPTO_EddsaSignature c_sig; + struct GNUNET_CRYPTO_EddsaSignature deposit_confirm_sig; struct GNUNET_CRYPTO_EddsaPublicKey pub; #ifdef OBSOLETE struct ContractNBO contract; @@ -417,6 +413,8 @@ url_handler (void *cls, struct curl_slist *slist; char *contract_str; struct GNUNET_HashCode h_contract_str; + struct MERCHANT_contract_handle ch; + struct TALER_MintPublicKeyP mint_pub; uint64_t nounce; CURL *curl; @@ -546,8 +544,6 @@ url_handler (void *cls, slist = curl_slist_append (slist, "Content-type: application/json"); curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist); - /* FIXME the mint's URL is be retrieved from the partial deposit permission - (received by the wallet) */ curl_easy_setopt (curl, CURLOPT_URL, deposit_url); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_response_in_memory); curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) &mr); @@ -563,7 +559,7 @@ url_handler (void *cls, curl_slist_free_all(slist); if(curl_res != CURLE_OK) { - printf ("deposit rejected by mint\n"); + printf ("deposit not sent\n"); goto end; } else @@ -571,10 +567,67 @@ url_handler (void *cls, curl_easy_cleanup(curl); - /* Bounce back to the frontend what the mint said */ - //printf ("get %s\n", mr.ptr); - generate_message (&resp, mr.ptr); curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status); + + /* If the request was successful, the deposit confirmation + has to be verified*/ + + if (MHD_HTTP_OK != result) + /* Jump here to error mgmt, see issue #3800 (TODO) */ + + if (GNUNET_SYSERR == + MERCHANT_DB_get_contract_handle (db_conn, &h_contract_str, &ch)) + { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + root = json_loads (mr.ptr, 0, NULL); + j_tmp = json_object_get (root, "sig"); + TALER_json_to_data (j_tmp, + &deposit_confirm_sig, + sizeof (struct GNUNET_CRYPTO_EddsaSignature)); + j_tmp = json_object_get (root, "pub"); + + TALER_json_to_data (j_tmp, + &mint_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + + GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); + + + /* The following check could be done once the merchant is able to + retrieve the deposit fee for each coin it gets a payment with. + That happens because the signature made on a deposit confirmation + uses, among its values of interest, the subtraction of the deposit + fee from the used coin. That fee is never communicated by the wallet + to the merchant, so the only way for the merchant to get this + information is to fetch all the mint's keys, namely it needs to + invoke "/keys", and store what gotten in its DB */ + + #ifdef DENOMMGMT + if (GNUNET_NO == + MERCHANT_verify_confirmation (&h_contract_str, + &h_json_wire, + ch.timestamp, + ch.refund_deadline, + ch.contract_id, + amount_minus_fee, /* MISSING */ + coin_pub, /* MISSING */ + &pub, + &deposit_confirm_sig, + &mint_pub)) + { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + + } + + #endif + + /* Place here the successful message to issue to the frontend */ + status = MHD_HTTP_OK; + generate_message (&resp, "fullfillment page here"); GNUNET_free (mr.ptr); } @@ -658,7 +711,8 @@ url_handler (void *cls, goto end; j_h_json_wire = TALER_json_from_data ((void *) &h_json_wire, sizeof (struct GNUNET_HashCode)); - /* JSONify public key */ + + GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); if (NULL == (j_contract_add = json_pack ("{s:s, s:s, s:o, s:o, s:o}", @@ -680,14 +734,14 @@ url_handler (void *cls, } res = MERCHANT_handle_contract (root, - db_conn, - &contract, - now, - expiry, - edate, - refund, - &contract_str, - nounce); + db_conn, + &contract, + now, + expiry, + edate, + refund, + &contract_str, + nounce); if (GNUNET_SYSERR == res) { status = MHD_HTTP_INTERNAL_SERVER_ERROR; @@ -703,8 +757,6 @@ url_handler (void *cls, GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str); j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig); - GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); - eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); response = json_pack ("{s:o, s:o, s:o}", "contract", root, diff --git a/src/include/merchant.h b/src/include/merchant.h @@ -27,6 +27,19 @@ #include <gnunet/gnunet_crypto_lib.h> /** + * Macro to round microseconds to seconds in GNUNET_TIME_* structs. + */ +#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000) + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + +/** * A mint */ struct MERCHANT_MintInfo {