merchant

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

commit 1e04bb2610bd142aebeb0fd040e649045ec785df
parent 5453309a27e2d3621225ac10785707b02459b51e
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Wed, 23 Sep 2015 17:16:11 +0200

adding further columns to the "contracts" table: e.g. "edate",
"nounce", "timestamp". Plus, adding a function for querying the DB
in order to get the contract (nonce,edate) by looking for its hashed
stringification. Plus adjusting accordingly the testcase.

Diffstat:
Msrc/backend-lib/merchant_db.c | 127++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/backend-lib/merchant_db.h | 51+++++++++++++++++++++++++++++++++++++--------------
Msrc/backend-lib/taler-merchant-httpd_contract.c | 142++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/backend-lib/taler_merchant_contract_lib.h | 51++++++++++++++++++++++++++++++++++++++-------------
Msrc/backend/taler-merchant-httpd.c | 221++++++++++++++++++++++++++++---------------------------------------------------
Msrc/tests/merchant-contract-test.c | 32+++++++++++++++++---------------
6 files changed, 344 insertions(+), 280 deletions(-)

diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c @@ -94,7 +94,9 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL," "description TEXT NOT NULL," "nounce INT8 NOT NULL," + "timestamp INT8 NOT NULL," "expiry INT8 NOT NULL," + "edate INT8 NOT NULL," "product INT8 NOT NULL);" "CREATE %1$s TABLE IF NOT EXISTS checkouts (" "coin_pub BYTEA PRIMARY KEY," @@ -120,13 +122,33 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) (conn, "contract_create", "INSERT INTO contracts" - "(contract_id, hash, amount, amount_fraction, amount_currency," - "description, nounce, expiry, product) VALUES" - "($1, $2, $3, $4, $5, $6, $7, $8, $9)", - 9, NULL))); + "(contract_id, hash, timestamp, expiry, edate, amount, amount_fraction, amount_currency," + "description, nounce, product) VALUES" + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", + 11, NULL))); EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); PQclear (res); + + /* Query aimed to get the contract's nounce and edate which will be + both used for regenerating a 'wire' JSON object to insert into the + deposit permission. Implicitly, this query will tell whether a contract + was created or not */ + + EXITIF (NULL == (res = PQprepare + (conn, + "get_contract_hash", + "SELECT (" + "nounce, edate" + ") FROM contracts " + "WHERE (" + "hash=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare (conn, "get_contract_product", @@ -184,23 +206,28 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) /** - * Inserts a contract record into the database and if successfull returns the - * serial number of the inserted row. - * - * @param conn the database connection - * @param expiry the time when the contract will expire - * @param amount the taler amount corresponding to the contract - * @param hash of the stringified JSON corresponding to this contract - * @param c_id contract's id - * @param desc descripition of the contract - * @param nounce a random 64-bit nounce - * @param product description to identify a product - * @return GNUNET_OK on success, GNUNET_SYSERR upon error - */ +* Inserts a contract record into the database and if successfull returns the +* serial number of the inserted row. +* +* @param conn the database connection +* @param timestamp the timestamp of this contract +* @param expiry the time when the contract will expire +* @param edate when the merchant wants to receive the wire transfer corresponding +* to this deal (this value is also a field inside the 'wire' JSON format) +* @param amount the taler amount corresponding to the contract +* @param hash of the stringified JSON corresponding to this contract +* @param c_id contract's id +* @param desc descripition of the contract +* @param nounce a random 64-bit nounce +* @param product description to identify a product +* @return GNUNET_OK on success, GNUNET_SYSERR upon error +*/ uint32_t MERCHANT_DB_contract_create (PGconn *conn, - const struct GNUNET_TIME_Absolute *expiry, + const struct GNUNET_TIME_Absolute timestamp, + const struct GNUNET_TIME_Absolute expiry, + struct GNUNET_TIME_Absolute edate, const struct TALER_Amount *amount, const struct GNUNET_HashCode *h_contract, uint64_t c_id, @@ -217,18 +244,18 @@ MERCHANT_DB_contract_create (PGconn *conn, #endif ExecStatusType status; - /* ported. To be tested/compiled */ struct TALER_PQ_QueryParam params[] = { TALER_PQ_query_param_uint64 (&c_id), TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), + TALER_PQ_query_param_absolute_time (&timestamp), + TALER_PQ_query_param_absolute_time (&expiry), + TALER_PQ_query_param_absolute_time (&edate), TALER_PQ_query_param_amount (amount), - /* a *string* is being put in the following statement, - though the API talks about a *blob*. Will this be liked by - the DB ? */ - // the following inserts a string as a blob. Will Taler provide a param-from-string helper? + /* A *string* is being put in the following statement, + though the column is declared as *blob*. Will this be + liked by the DB ? */ TALER_PQ_query_param_fixed_size (desc, strlen (desc)), TALER_PQ_query_param_uint64 (&nounce), - TALER_PQ_query_param_absolute_time (expiry), TALER_PQ_query_param_uint64 (&product), TALER_PQ_query_param_end }; @@ -345,3 +372,55 @@ MERCHANT_DB_get_checkout_product (PGconn *conn, return -1; } /* end of merchant-db.c */ + + +/** +* The query gets a contract's nounce and edate used to reproduce +* a 'wire' JSON object. This function is also useful to check whether +* a claimed contract existed or not. +* @param conn handle to the DB +* @param h_contract the parameter for the row to match against +* @param nounce where to store the found nounce +* @param edate where to store the found edate +* @return GNUNET_OK on success, GNUNET_SYSERR upon errors +* +*/ + +uint32_t +MERCHANT_DB_get_contract_values (PGconn *conn, + const struct GNUNET_HashCode *h_contract, + uint64_t *nounce, + struct GNUNET_TIME_Absolute *edate) +{ + 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", nounce), + TALER_PQ_result_spec_absolute_time ("edate", edate), + TALER_PQ_result_spec_end + }; + + res = TALER_PQ_exec_prepared (conn, "get_checkout_product", 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)); + 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 @@ -58,23 +58,28 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp); /** - * Inserts a contract record into the database and if successfull returns the - * serial number of the inserted row. - * - * @param conn the database connection - * @param expiry the time when the contract will expire - * @param amount the taler amount corresponding to the contract - * @param hash of the stringified JSON corresponding to this contract - * @param c_id this contract's identification number - * @param desc descripition of the contract - * @param nounce a random 64-bit nounce - * @param product description to identify a product - * @return GNUNET_OK on success, GNUNET_SYSERR upon error - */ +* Inserts a contract record into the database and if successfull returns the +* serial number of the inserted row. +* +* @param conn the database connection +* @param timestamp the timestamp of this contract +* @param expiry the time when the contract will expire +* @param edate when the merchant wants to receive the wire transfer corresponding +* to this deal (this value is also a field inside the 'wire' JSON format) +* @param amount the taler amount corresponding to the contract +* @param hash of the stringified JSON corresponding to this contract +* @param c_id contract's id +* @param desc descripition of the contract +* @param nounce a random 64-bit nounce +* @param product description to identify a product +* @return GNUNET_OK on success, GNUNET_SYSERR upon error +*/ uint32_t MERCHANT_DB_contract_create (PGconn *conn, - const struct GNUNET_TIME_Absolute *expiry, + const struct GNUNET_TIME_Absolute timestamp, + const struct GNUNET_TIME_Absolute expiry, + struct GNUNET_TIME_Absolute edate, const struct TALER_Amount *amount, const struct GNUNET_HashCode *h_contract, uint64_t c_id, @@ -98,6 +103,24 @@ long long MERCHANT_DB_get_checkout_product (PGconn *conn, struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub); +/** +* The query gets a contract's nounce and edate used to reproduce +* a 'wire' JSON object. This function is also useful to check whether +* a claimed contract existed or not. +* @param conn handle to the DB +* @param h_contract the parameter for the row to match against +* @param nounce where to store the found nounce +* @param edate where to store the found edate +* @return GNUNET_OK on success, GNUNET_SYSERR upon errors +* +*/ + +uint32_t +MERCHANT_DB_get_contract_values (PGconn *conn, + const struct GNUNET_HashCode *h_contract, + uint64_t *nounce, + struct GNUNET_TIME_Absolute *edate); + #endif /* MERCHANT_DB_H */ /* 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 @@ -49,14 +49,57 @@ hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire) } /** + * Take the global wire details and return a JSON containing them, + * compliantly with the Taler's API. + * @param wire the merchant's wire details + * @param nounce the nounce for hashing the wire details with + * @param edate when the beneficiary wants this transfer to take place + * @return JSON representation of the wire details, NULL upon errors + */ + +json_t * +MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, + uint64_t nounce, + const struct GNUNET_TIME_Absolute edate) + +{ + + json_t *root; + json_t *j_edate; + json_t *j_nounce; + + nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + j_nounce = json_integer (nounce); + j_edate = TALER_json_from_abs (edate); + + if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o}", + "type", "SEPA", + "IBAN", wire->iban, + "name", wire->name, + "BIC", wire->bic, + "edate", j_edate, + "r", json_integer_value (j_nounce)))) + return NULL; + + return root; +} + + + +/** * Take from the frontend the (partly) generated contract and fill * the missing values in it; for example, the SEPA details. * Moreover, it stores the contract in the DB. * @param j_contract parsed contract, originated by the frontend. It will be * hold the new values. * @param db_conn the handle to the local DB -* @param wire merchant's bank's details * @param contract where to store the (subset of the) contract to be (still) signed +* @param timestamp contract's timestamp (shall be generated by the merchant) +* @param expiry the time when the contract will expire +* @param edate when the merchant wants to receive the wire transfer corresponding +* to this deal (this value is also a field inside the 'wire' JSON format) +* @param nounce the nounce used to hash the wire details +* @param contract_str where to store the (stringified) contract * @return GNUNET_OK on success; GNUNET_SYSERR upon errors */ @@ -65,97 +108,56 @@ hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire) * still allocated */ uint32_t -MERCHANT_handle_contract (json_t *j_contract, +MERCHANT_handle_contract (const json_t *j_contract, PGconn *db_conn, - const struct MERCHANT_WIREFORMAT_Sepa *wire, - struct Contract *contract) + struct Contract *contract, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute expiry, + struct GNUNET_TIME_Absolute edate, + uint64_t nounce, + const char *contract_str) { - json_t *j_tmp; - json_t *j_details; - json_t *j_timestamp; - json_t *jh_wire; json_t *j_amount; - json_t *j_mints; - json_t *j_max_fee; + json_int_t j_product_id; json_int_t j_trans_id; - uint64_t nounce; - json_t *j_product_id; - json_t *j_items_tmp; - char *a; - #define DaEBUG - #ifdef DEBUG - char *str; - #endif - struct GNUNET_HashCode h_wire; - struct GNUNET_TIME_Absolute timestamp; struct TALER_Amount amount; - struct TALER_AmountNBO amount_nbo; - nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - // timing mgmt - timestamp = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), - GNUNET_TIME_UNIT_DAYS); - ROUND_TO_SECS (timestamp, abs_value_us); - j_timestamp = TALER_json_from_abs (timestamp); - // wireformat mgmt - h_wire = hash_wireformat (nounce, wire); - jh_wire = TALER_json_from_data (&h_wire, sizeof (struct GNUNET_HashCode)); - /* adding the generated values in this JSON */ - if (NULL == (j_tmp = json_pack ("{s:o, s:o}", - "h_wire", jh_wire, - "timestamp", j_timestamp))) - return GNUNET_SYSERR; - if (-1 == json_object_update (j_contract, j_tmp)) + + /* Extracting values useful for DB work. Only gettable from the JSON + since they are generated by the frontend */ + if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:{s:I}}}", + "amount", &j_amount, + "trans_id", &j_trans_id, + "details", "items", + "product_id", &j_product_id)) + { + printf ("no unpacking\n"); return GNUNET_SYSERR; + } + /* needed for DB work */ - j_amount = json_object_get (j_contract, "amount"); TALER_json_to_amount (j_amount, &amount); // produces a WARNING.. - a = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); - - #ifdef DEBUG - str = json_dumps (j_amount, JSON_COMPACT | JSON_PRESERVE_ORDER); - printf ("amount : \n%s", str); - return GNUNET_SYSERR; - #endif - - /* TODO - - Add a further field to the 'contract' table, indicating the timestamp - of this contract being finalized - - */ - - GNUNET_CRYPTO_hash (a, strlen (a) + 1, &contract->h_contract_details); + contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); + GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, + &contract->h_contract_details); contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); contract->purpose.size = htonl (sizeof (struct Contract)); // DB mgmt if (GNUNET_SYSERR == MERCHANT_DB_contract_create (db_conn, - &timestamp, + timestamp, + expiry, + edate, &amount, &contract->h_contract_details, (uint64_t) j_trans_id, // safe? - a, + contract_str, nounce, - json_integer_value (j_product_id))) + (uint64_t) j_product_id)) return GNUNET_SYSERR; - free (a); - - #ifdef OBSOLETE - contract->h_wire = h_wire; - TALER_amount_hton (&amount_nbo, &amount); - contract->amount = amount_nbo; - contract->t = GNUNET_TIME_absolute_hton (timestamp); - contract->m = GNUNET_htonll ((uint64_t) j_trans_id); // safe? - #endif - - #ifdef OBSOLETE - contract->purpose.size = htonl (sizeof (struct ContractNBO)); - #endif - return GNUNET_OK; } diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h @@ -57,24 +57,49 @@ struct ContractNBO GNUNET_NETWORK_STRUCT_END + +/** + * Take the global wire details and return a JSON containing them, + * compliantly with the Taler's API. + * @param wire the merchant's wire details + * @param nounce the nounce for hashing the wire details with + * @param edate when the beneficiary wants this transfer to take place + * @return JSON representation of the wire details, NULL upon errors + */ + +json_t * +MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, + uint64_t nounce, + struct GNUNET_TIME_Absolute edate); + /** * Take from the frontend the (partly) generated contract and fill -* the missing values in it; for example, the SEPA-aware values. -* Moreover, it stores the contract in the DB and does the signature of it. -* @param contract parsed contract, originated by the frontend +* the missing values in it; for example, the SEPA details. +* Moreover, it stores the contract in the DB. +* @param j_contract parsed contract, originated by the frontend. It will be +* hold the new values. * @param db_conn the handle to the local DB -* @param kpriv merchant's private key -* @param wire merchant's bank's details -* @param sig where to store the signature -* @return GNUNET_OK if successful, GNUNET_SYSERR upon errors -* +* @param contract where to store the (subset of the) contract to be (still) signed +* @param timestamp contract's timestamp (shall be generated by the merchant) +* @param expiry the time when the contract will expire +* @param edate when the merchant wants to receive the wire transfer corresponding +* to this deal (this value is also a field inside the 'wire' JSON format) +* @param nounce the nounce used to hash the wire details +* @param contract_str where to store the hashed (stringified) contract +* @return GNUNET_OK on success; GNUNET_SYSERR upon errors */ -/* TODO: this handles a simplified version (for debugging purposes) - of the contract. To expand to the full fledged version */ +/** +* TODO: inspect reference counting and, accordingly, free those json_t*(s) +* still allocated */ uint32_t -MERCHANT_handle_contract (json_t *j_contract, +MERCHANT_handle_contract (const json_t *j_contract, PGconn *db_conn, - const struct MERCHANT_WIREFORMAT_Sepa *wire, - struct Contract *contract); + struct Contract *contract, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute expiry, + struct GNUNET_TIME_Absolute edate, + uint64_t nounce, + const char *contract_str); + diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -90,11 +90,6 @@ static struct GNUNET_SCHEDULER_Task *shutdown_task; static struct MERCHANT_WIREFORMAT_Sepa *wire; /** - * Hash of the wireformat - */ -static struct GNUNET_HashCode h_wire; - -/** * Should we do a dry run where temporary tables are used for storing the data. */ static int dry; @@ -282,38 +277,6 @@ request</h3></center></body></html>"; return GNUNET_SYSERR; } -/** - * Take the global wire details and return a JSON containing them, - * compliantly with the Taler's API. - * @param edate when the beneficiary wants this transfer to take place - * @return JSON representation of the wire details, NULL upon errors - */ - -static json_t * -get_wire_json (struct GNUNET_TIME_Absolute edate) -{ - - json_t *root; - json_t *j_edate; - json_t *j_nounce; - uint64_t nounce; - - nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - j_nounce = json_integer (nounce); - - j_edate = TALER_json_from_abs (edate); - if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o}", - "type", "SEPA", - "IBAN", wire->iban, - "name", wire->name, - "BIC", wire->bic, - "edate", j_edate, - "r", json_integer_value (j_nounce)))) - return NULL; - - return root; -} - /** * A client has requested the given url using the given method @@ -378,7 +341,6 @@ url_handler (void *cls, #endif struct MHD_Response *resp; json_t *root; - json_t *j_contract_complete; json_t *j_sig_enc; json_t *eddsa_pub_enc; json_t *response; @@ -389,9 +351,7 @@ url_handler (void *cls, char *str; /* to debug JSONs */ char *deposit_body; json_t *root_tmp; - json_t *j_refund_deadline; - json_t *j_amount_tmp; - json_t *j_depperm; + json_t *j_contract_add; json_t *j_details_tmp; json_t *j_max_fee_tmp; json_int_t j_int_trans_id_tmp; @@ -402,12 +362,15 @@ url_handler (void *cls, char *denom_key; char *contract_sig; char *h_contract; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute expiry; + struct GNUNET_TIME_Absolute edate; struct GNUNET_HashCode h_json_wire; struct TALER_Amount amount_tmp; json_t *j_h_json_wire; struct curl_slist *slist; - - struct GNUNET_TIME_Absolute now; + char *contract_str; + struct GNUNET_HashCode h_contract_str; CURL *curl; CURLcode curl_res; @@ -455,110 +418,28 @@ url_handler (void *cls, if ((GNUNET_NO == res) || (NULL == root)) return MHD_YES; - /* The frontend should supply a JSON in the format described in - http://link-to-specs : .. In practice, it just forwards what it - received from the wallet. - + /* + Roughly, the backend must add to this JSON: 1. The merchant's public key - 2. A timestamp (?) - 3. wire (see mint's specs) - 4. h_wire - 5. refund deadline (?) + 2. wire - (?) : may the frontend will handle dates ? - - */ - - #ifdef DEBUG - /* NOTE: there is no need to thoroughly unpack this JSON, since - the backend must just *add* fields to it */ - if (-1 == (json_unpack (root, - "{s:s, s:I, s:s, s:s, s:s}", - "ub_sig", &ub_sig, - "coin_pub", &coin_pub, - "denom_pub", &denom_key, - "H_contract", &h_contract, - "sig", &contract_sig))) - { - printf ("no unpack\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - printf ("Got this deposit permission:\nub_sig: %s\ncoin_pub: %s\ndenom_key: %s\nsig: %s\n", - ub_sig, coin_pub, denom_key, contract_sig); - - return MHD_NO; - #endif - - /* TODO: Check if there is a row in 'contracts' table corresponding to this - contract ('s hash). This query has to return the trans_id and the amount for - this contract - faked values for now. See bug #XXXX */ - - /* FIXME fake trans_id */ - trans_id_tmp = (int64_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - - if (trans_id_tmp < 0) - j_trans_id_tmp = json_integer ((-1) * trans_id_tmp); - else - j_trans_id_tmp = json_integer (trans_id_tmp); + TODO: Check if there is a row in 'contracts' table corresponding + to this contract ('s hash). If so, the corresponding 'nounce' and + 'edate' have to be retrieved */ + + j_wire = - /* FIXME fake amount to redeem */ - TALER_amount_get_zero ("EUR", &amount_tmp); - amount_tmp.value = 5; - j_amount_tmp = TALER_json_from_amount (&amount_tmp); /* Encoding merchant's key */ GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - /* Timestamping 'now' */ - now = GNUNET_TIME_absolute_get (); - - /* getting the SEPA-aware JSON */ - if (NULL == (j_wire = get_wire_json (GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS)))) - goto end; - - /* hash it*/ - if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire)) - goto end; - - j_h_json_wire = TALER_json_from_data (&h_json_wire, sizeof (struct GNUNET_HashCode)); - /* refund deadline */ - j_refund_deadline = TALER_json_from_abs (GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS)); - - /* pack it!*/ - eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - - if (NULL == (j_depperm = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:I, s:o}", - "merchant_pub", eddsa_pub_enc, - "timestamp", TALER_json_from_abs (now), - "wire", j_wire, - "H_wire", j_h_json_wire, - "refund_deadline", j_refund_deadline, - "transaction_id", json_integer_value (j_trans_id_tmp), - "f", j_amount_tmp))) - { - printf ("BAD depperm packaging\n"); - goto end; - } - - - /* melt to what received from the wallet */ - if (-1 == json_object_update (j_depperm, root)) - { - printf ("depperm response not built\n"); - goto end; - } - - #define DEBUGG - #ifdef DEBUG - str = json_dumps (j_depperm, JSON_INDENT(2) | JSON_PRESERVE_ORDER); - printf ("Depperm is: \n%s", str); - return MHD_NO; - #endif + /* Crafting the wire JSON */ + if (NULL == (j_tmp = json_pack ("{s:o, s:I}", + "merchant_pub", eddsa_pub_enc, + "wire", j_wire))) /* POST to mint's "/deposit" */ curl = curl_easy_init (); @@ -656,16 +537,62 @@ url_handler (void *cls, } - if (NULL == (j_contract_complete = MERCHANT_handle_contract (root, - db_conn, - wire, - &contract))) + + + /* timestamp */ + now = GNUNET_TIME_absolute_get (); + /* expiry */ + expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + /* edate */ + edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + + /* getting the SEPA-aware JSON */ + /* nounce for hashing the wire object */ + nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + /* get wire object */ + if (NULL == + (j_wire = MERCHANT_get_wire_json (wire, + nounce, + edate))) + goto end; + /* hash wire objcet */ + if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire)) + goto end; + j_h_json_wire = TALER_json_from_data (&h_json_wire, sizeof (struct GNUNET_HashCode)); + /* get key JSON entry */ + eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); + + if (NULL == (j_contract_add = json_pack ("{s:o, s:o, s:o}", + "merchant_pub", eddsa_pub_enc, + "H_wire", j_h_json_wire, + "timestamp", TALER_json_from_abs (now)))) + { + printf ("BAD contract enhancement\n"); + goto end; + } + + /* melt to what received from the wallet */ + if (-1 == json_object_update (j_contract_add, root)) + { + printf ("depperm response not built\n"); + goto end; + } + + if (GNUNET_SYSERR == MERCHANT_handle_contract (root, + db_conn, + &contract, + now, + expiry, + edate, + nounce, + contract_str)) { status = MHD_HTTP_INTERNAL_SERVER_ERROR; goto end; } GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig); + GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str); /** * @@ -680,10 +607,16 @@ url_handler (void *cls, 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", j_contract_complete, + + response = json_pack ("{s:o, s:o, s:o, s:o}", + "contract", root, "sig", j_sig_enc, - "eddsa_pub", eddsa_pub_enc); + "eddsa_pub", eddsa_pub_enc, + "h_contract", + TALER_json_from_data (h_contract_str, sizeof (struct GNUNET_HashCode))); + + GNUNET_free (contract_str); + TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); return MHD_YES; diff --git a/src/tests/merchant-contract-test.c b/src/tests/merchant-contract-test.c @@ -73,6 +73,7 @@ run (void *cls, char *const *args, const char *cfgfile, { json_t *j_fake_contract; json_t *j_root; + json_t *j_wire; json_t *j_details; json_t *j_mints; json_t *j_item; @@ -90,7 +91,9 @@ run (void *cls, char *const *args, const char *cfgfile, json_t *j_merchant_zipcode; json_t *j_lnames; json_t *j_deldate; + json_t *j_expiry; char *str; + char *contract_tmp_str; char *desc; struct TALER_Amount amount; int64_t t_id; @@ -101,6 +104,8 @@ run (void *cls, char *const *args, const char *cfgfile, struct Contract contract; #endif struct GNUNET_TIME_Absolute deldate; + struct GNUNET_TIME_Absolute now; + uint64_t nounce; db_conn = NULL; keyfile = NULL; @@ -201,8 +206,6 @@ run (void *cls, char *const *args, const char *cfgfile, } /* End of 'item' object definition */ - - printf ("[j_item address: %p]\n", j_item); /* Delivery date: OPTIONAL FIELD */ deldate = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), @@ -264,23 +267,22 @@ run (void *cls, char *const *args, const char *cfgfile, return; #endif + nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + now = GNUNET_TIME_absolute_get (); + + j_wire = MERCHANT_get_wire_json (wire, nounce, now); if (GNUNET_SYSERR == MERCHANT_handle_contract (j_fake_contract, db_conn, - wire, - &contract)) - { + &contract, + now, + now, + now, + nounce, + contract_tmp_str)) printf ("errors in contract handling\n"); - return; - } - - #if 1 - str = json_dumps (j_fake_contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER); - printf ("%s\n", str); - return; - #endif - - + else + printf ("handling contract fine\n"); }