From ae89e08fd7d59cd81c69221c723c64c05dfacf3f Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 30 Sep 2015 09:00:21 +0200 Subject: adding error code for primary key violation --- src/backend-lib/merchant_db.c | 44 ++- src/backend-lib/taler-merchant-httpd_contract.c | 73 ++--- src/backend-lib/taler_merchant_contract_lib.h | 51 +--- src/backend/taler-merchant-httpd.c | 25 +- src/tests/Makefile.am | 10 +- src/tests/merchant-contract-test.c | 343 ----------------------- src/tests/test_contract.c | 347 ++++++++++++++++++++++++ 7 files changed, 430 insertions(+), 463 deletions(-) delete mode 100644 src/tests/merchant-contract-test.c create mode 100644 src/tests/test_contract.c diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c index 6c67eb9d..befb2c48 100644 --- a/src/backend-lib/merchant_db.c +++ b/src/backend-lib/merchant_db.c @@ -208,22 +208,25 @@ 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. +* Insert a contract record into the database and if successfull +* return 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 refund deadline until which the merchant can return the paid amount +* @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 refund deadline until which the merchant can return the paid +* amount * @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 +* @return GNUNET_OK on success, GNUNET_NO if attempting to insert an +* already inserted @a c_id, GNUNET_SYSERR for other errors. */ uint32_t @@ -268,7 +271,34 @@ MERCHANT_DB_contract_create (PGconn *conn, /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */ res = TALER_PQ_exec_prepared (conn, "contract_create", params); status = PQresultStatus (res); - EXITIF (PGRES_COMMAND_OK != status); + + 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"))) + { + /* Primary key violation */ + PQclear (res); + return GNUNET_NO; + } + 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/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c index 1f9d095a..5a937e66 100644 --- a/src/backend-lib/taler-merchant-httpd_contract.c +++ b/src/backend-lib/taler-merchant-httpd_contract.c @@ -23,31 +23,6 @@ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ } while (0) -/** -* Generate the hash containing the information (= a nounce + merchant's IBAN) to -* redeem money from mint in a subsequent /deposit operation -* @param nounce the nounce -* @param wire the merchant's wire details -* @return the hash to be included in the contract's blob -* -*/ - -static struct GNUNET_HashCode -hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire) -{ - struct GNUNET_HashContext *hc; - struct GNUNET_HashCode hash; - - hc = GNUNET_CRYPTO_hash_context_start (); - GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban)); - GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name)); - GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic)); - nounce = GNUNET_htonll (nounce); - GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce)); - GNUNET_CRYPTO_hash_context_finish (hc, &hash); - return hash; -} - /** * Take the global wire details and return a JSON containing them, * compliantly with the Taler's API. @@ -99,22 +74,24 @@ MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, * to this deal (this value is also a field inside the 'wire' JSON format) * @param refund deadline until which the merchant can return the paid amount * @param nounce the nounce used to hash the wire details -* @param contract_str where to store -* @return pointer to the (stringified) contract; NULL upon errors +* @param a will be pointed to the (allocated) stringified 0-terminated contract +* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the +* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors. */ /** * TODO: inspect reference counting and, accordingly, free those json_t*(s) * still allocated */ -char * -MERCHANT_handle_contract (const json_t *j_contract, +uint32_t +MERCHANT_handle_contract (json_t *j_contract, PGconn *db_conn, struct Contract *contract, struct GNUNET_TIME_Absolute timestamp, struct GNUNET_TIME_Absolute expiry, struct GNUNET_TIME_Absolute edate, struct GNUNET_TIME_Absolute refund, + char **a, uint64_t nounce) { json_t *j_amount; @@ -135,39 +112,27 @@ MERCHANT_handle_contract (const json_t *j_contract, &j_product_id)) { printf ("no unpacking\n"); - return NULL; + return GNUNET_SYSERR; } - /* DB will store the amount -- WARNING: this call produces a - 'protocol violation' in json.c */ - - #if 0 - char *str = json_dumps (j_amount, JSON_INDENT(2) | JSON_PRESERVE_ORDER); - printf ("extracted amount : %s\n", str); - #endif - - TALER_json_to_amount (j_amount, &amount); contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); + *a = contract_str; 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, - expiry, - edate, - refund, - &amount, - &contract->h_contract_details, - (uint64_t) j_trans_id, // safe? - contract_str, - nounce, - (uint64_t) j_product_id)) - return NULL; - return contract_str; + return MERCHANT_DB_contract_create (db_conn, + timestamp, + expiry, + edate, + refund, + &amount, + &contract->h_contract_details, + (uint64_t) j_trans_id, // safe? + contract_str, + nounce, + (uint64_t) j_product_id); } - - diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h index cc02f70d..692c849d 100644 --- a/src/backend-lib/taler_merchant_contract_lib.h +++ b/src/backend-lib/taler_merchant_contract_lib.h @@ -17,47 +17,6 @@ struct Contract }; -GNUNET_NETWORK_STRUCT_BEGIN - -struct ContractNBO -{ - /** - * Purpose header for the signature over contract - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * The transaction identifier. NOTE: it was m[13]. TODO: - * change the API accordingly! - */ - uint64_t m; - - /** - * Expiry time - */ - struct GNUNET_TIME_AbsoluteNBO t; - - /** - * The invoice amount - */ - struct TALER_AmountNBO amount; - - /** - * The hash of the merchant's wire details (bank account information), with a nounce - */ - struct GNUNET_HashCode h_wire; - - /** - * Hash of the JSON contract in UTF-8 including 0-termination, - * using JSON_COMPACT encoding with sorted fields. - */ - struct GNUNET_HashCode h_contract_details; - -}; - -GNUNET_NETWORK_STRUCT_END - - /** * Take the global wire details and return a JSON containing them, * compliantly with the Taler's API. @@ -86,20 +45,22 @@ MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, * to this deal (this value is also a field inside the 'wire' JSON format) * @param refund deadline until which the merchant can return the paid amount * @param nounce the nounce used to hash the wire details -* @param contract_str where to store -* @return pointer to the (stringified) contract; NULL upon errors +* @param a will be pointed to the (allocated) stringified 0-terminated contract +* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the +* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors. */ /** * TODO: inspect reference counting and, accordingly, free those json_t*(s) * still allocated */ -char * -MERCHANT_handle_contract (const json_t *j_contract, +uint32_t +MERCHANT_handle_contract (json_t *j_contract, PGconn *db_conn, struct Contract *contract, struct GNUNET_TIME_Absolute timestamp, struct GNUNET_TIME_Absolute expiry, struct GNUNET_TIME_Absolute edate, struct GNUNET_TIME_Absolute refund, + char **a, uint64_t nounce); diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 15f72586..bd74405a 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -366,7 +366,7 @@ url_handler (void *cls, CURL *curl; CURLcode curl_res; - int res = GNUNET_SYSERR; + uint32_t res = GNUNET_SYSERR; #define URL_HELLO "/hello" #define URL_CONTRACT "/contract" @@ -588,18 +588,25 @@ url_handler (void *cls, goto end; } - if (NULL == (contract_str = MERCHANT_handle_contract (root, - db_conn, - &contract, - now, - expiry, - edate, - refund, - nounce))) + res = MERCHANT_handle_contract (root, + db_conn, + &contract, + now, + expiry, + edate, + refund, + &contract_str, + nounce); + if (GNUNET_SYSERR == res) { status = MHD_HTTP_INTERNAL_SERVER_ERROR; goto end; } + if (GNUNET_NO == res) + { + status = MHD_HTTP_METHOD_NOT_ACCEPTABLE; + goto end; + } GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig); GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str); diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index dc88fc08..8dd350e7 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -2,17 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/backend-lib/ bin_PROGRAMS = \ - merchant-contract-test + test-contract #merchant_contract_test_CFLAGS = \ # -Wl,--verbose -merchant_contract_test_SOURCES = \ - merchant-contract-test.c \ +test_contract_SOURCES = \ + test_contract.c \ merchant.c \ ../backend-lib/merchant_db.c ../backend-lib/merchant_db.h -merchant_contract_test_LDADD = \ +test_contract_LDADD = \ $(LIBGCRYPT_LIBS) \ -ltalerutil \ -ltalermerchant \ @@ -22,5 +22,5 @@ merchant_contract_test_LDADD = \ -ltalerpq \ -lgnunetpostgres \ -lpq \ - -ltalermerchant \ + $(top_srcdir)/src/backend-lib/libtalermerchant.la \ -lpthread diff --git a/src/tests/merchant-contract-test.c b/src/tests/merchant-contract-test.c deleted file mode 100644 index 20f8d2ba..00000000 --- a/src/tests/merchant-contract-test.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** -* @file merchant/tests/merchant-non-http-test.c -* @brief test for various merchant's capabilities -* @author Marcello Stanisci -*/ - -#include "platform.h" -#include -#include -#include -#include "merchant_db.h" -#include - -PGconn *db_conn; - -static int dry; -struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; -char *keyfile; -static int result; -static struct MERCHANT_WIREFORMAT_Sepa *wire; -static struct GNUNET_SCHEDULER_Task *shutdown_task; - -extern -struct MERCHANT_WIREFORMAT_Sepa * -TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); - -/** - * Shutdown task (magically invoked when the application is being - * quit) - * - * @param cls NULL - * @param tc scheduler task context - */ -static void -do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - - if (NULL != db_conn) - { - MERCHANT_DB_disconnect (db_conn); - db_conn = NULL; - } -} - -extern uint32_t -MERCHANT_DB_get_contract_values (PGconn *conn, - const struct GNUNET_HashCode *h_contract, - uint64_t *nounce, - struct GNUNET_TIME_Absolute *edate); - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param config configuration - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *config) - -{ - json_t *j_fake_contract; - json_t *j_wire; - json_t *j_details; - json_t *j_mints; - json_t *j_item; - json_t *j_amount; - json_t *j_tax_amount; - json_t *j_item_price; - json_t *j_max_fee; - json_t *j_teatax; - json_t *j_id; // trans id - json_t *j_pid; // product id - json_t *j_quantity; - json_t *j_delloc; - json_t *j_merchant; - json_t *j_merchant_jurisdiction; - json_t *j_merchant_zipcode; - json_t *j_lnames; - json_t *j_deldate; - char *contract_tmp_str; - char *desc; - struct TALER_Amount amount; - int64_t t_id; - int64_t p_id; - #ifdef OBSOLETE - struct ContractNBO contract; - #else - struct Contract contract; - #endif - struct GNUNET_TIME_Absolute deldate; - struct GNUNET_TIME_Absolute edate; - struct GNUNET_TIME_Absolute now; - uint64_t nounce; - struct GNUNET_HashCode h_contract_str; - - db_conn = NULL; - keyfile = NULL; - privkey = NULL; - wire = NULL; - - - db_conn = MERCHANT_DB_connect (config); - if (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_NO)) - { - printf ("no db init'd\n"); - result = GNUNET_SYSERR; - } - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config, - "merchant", - "KEYFILE", - &keyfile)) - { - printf ("no keyfile entry in cfg file\n"); - result = GNUNET_SYSERR; - } - - privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile); - wire = TALER_MERCHANT_parse_wireformat_sepa (config); - shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &do_shutdown, NULL); - - /** - * 'Root' object of the contract, leaving some holes to bi filled - * up by the merchant library. - * - */ - - /* Amount */ - TALER_amount_get_zero ("EUR", &amount); - j_amount = TALER_json_from_amount (&amount); - - /* Transaction ID*/ - t_id = (int32_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); - - if (t_id < 0) - j_id = json_integer ((-1) * t_id); - else - j_id = json_integer (t_id); - - /** - * Holes: - * - * - 'h_wire' - * - 'timestamp' - * - */ - - /** - * - * Preparing the 'details' sub-object: an array of 'item' objects - * plus some juridical and delivery-aware informations - * - */ - - /** - * - * Preparing a 'item' sub-object - */ - - /* Description */ - desc = "Fake purchase"; - - j_max_fee = TALER_json_from_amount (&amount); - /* Quantity: OPTIONAL FIELD */ - j_quantity = json_integer (3); - - /* item price: OPTIONAL FIELD*/ - j_item_price = TALER_json_from_amount (&amount); - - /* Product ID */ - p_id = (int32_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); - - if (p_id < 0) - j_pid = json_integer ((-1) * p_id); - else - j_pid = json_integer (p_id); - - /* Taxes: array of "tax_name" : {tax amount} */ - j_tax_amount = TALER_json_from_amount (&amount); - j_teatax = json_pack ("{s:o}", - "teatax", j_tax_amount); - - if (NULL == (j_item = json_pack ("{s:s, s:I, s:o, s:[o]}", - "description", desc, - "quantity", json_integer_value (j_quantity), - "itemprice", j_item_price, - "taxes", j_teatax))) - { - printf ("error in packing [j_item: %p]\n", j_item); - return; - } - - /* End of 'item' object definition */ - - /* Delivery date: OPTIONAL FIELD */ - now = GNUNET_TIME_absolute_get (); - TALER_round_abs_time (&now); -// deldate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); - j_deldate = TALER_json_from_abs (now); - - /* Delivery location: OPTIONAL FIELD */ - j_delloc = json_string ("MALTK"); /* just a 'tag' which points to some well defined location */ - - - /* Merchant jurisdiction: OPTIONAL FIELD (with its fields from 3rd to the last being optional) - * for another optional field */ - j_merchant_zipcode = json_integer (9468); - j_merchant_jurisdiction = json_pack ("{s:s, s:s, s:s, s:s, s:s, s:I}", - "country", "Test Country", - "city", "Test City", - "state", "NA", - "region", "NA", - "province", "NA", - "ZIP code", json_integer_value (j_merchant_zipcode)); - - /* Merchant details */ - j_merchant = json_pack ("{s:s, s:s, s:o}", - "address", "UALMP", - "name", "test merchant", - "jurisdiction", j_merchant_jurisdiction); - - - /* L-names mapping */ - j_lnames = json_pack ("[{s:s}, {s:s}]", - "MALTK", "Test Address 1", - "UALMP", "Second Test Address"); - - - - j_details = json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o}", - "product_id", json_integer_value (j_pid), - "items", j_item, - "delivery date", j_deldate, - "delivery location", j_delloc, - "merchant", j_merchant, - "L-names", j_lnames); - - /* Faking out the mints' list */ - j_mints = json_pack ("[{s:s}]", - "demo.taler.net", - "Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0"); - - j_fake_contract = json_pack ("{s:o, s:o, s:I, s:o, s:o}", - "amount", j_amount, - "max fee", j_max_fee, - "trans_id", json_integer_value (j_id), - "mints", j_mints, - "details", j_details); - #if 0 - str = json_dumps (j_fake_contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER); - printf ("%s\n", str); - return; - #endif - - nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - - j_wire = MERCHANT_get_wire_json (wire, nounce, now); - - if (NULL == (contract_tmp_str = MERCHANT_handle_contract (j_fake_contract, - db_conn, - &contract, - now, - now, - now, - now, - nounce))) - - printf ("errors in contract handling\n"); - else - printf ("handling contract fine\n"); - - - /* try to get from DB the generated contract - - printf ("contract string : %s\n", contract_tmp_str); - return; - - */ - - GNUNET_CRYPTO_hash (contract_tmp_str, strlen (contract_tmp_str) + 1, &h_contract_str); - if (GNUNET_SYSERR == MERCHANT_DB_get_contract_values (db_conn, &h_contract_str, &nounce, &edate)) - printf ("no hash found\n"); - else - { - - char *late = GNUNET_STRINGS_absolute_time_to_string (edate); - printf ("hash found!, nounce is : %llu\n", nounce); - printf ("hash found!, time is : %s\n", late); - } - -} - - -/** - * The main function of the test tool - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - {'t', "temp", NULL, - gettext_noop ("Use temporary database tables"), GNUNET_NO, - &GNUNET_GETOPT_set_one, &dry}, - GNUNET_GETOPT_OPTION_END - }; - - - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, argv, - "merchant-contract-test", - "Test for contracts mgmt", - options, &run, NULL)) - return 3; - return (GNUNET_OK == result) ? 0 : 1; - - - -} diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c new file mode 100644 index 00000000..47b3b0e6 --- /dev/null +++ b/src/tests/test_contract.c @@ -0,0 +1,347 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** +* @file merchant/tests/merchant-non-http-test.c +* @brief test for various merchant's capabilities +* @author Marcello Stanisci +*/ + +#include "platform.h" +#include +#include +#include +#include "merchant.h" +#include "merchant_db.h" +#include + +PGconn *db_conn; + +static int dry; +struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; +char *keyfile; +static int result; +static struct MERCHANT_WIREFORMAT_Sepa *wire; +static struct GNUNET_SCHEDULER_Task *shutdown_task; + +extern +struct MERCHANT_WIREFORMAT_Sepa * +TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Shutdown task (magically invoked when the application is being + * quit) + * + * @param cls NULL + * @param tc scheduler task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + + if (NULL != db_conn) + { + MERCHANT_DB_disconnect (db_conn); + db_conn = NULL; + } +} + +extern uint32_t +MERCHANT_DB_get_contract_values (PGconn *conn, + const struct GNUNET_HashCode *h_contract, + uint64_t *nounce, + struct GNUNET_TIME_Absolute *edate); + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param config configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) + +{ + json_t *j_fake_contract; + json_t *j_wire; + json_t *j_details; + json_t *j_mints; + json_t *j_item; + json_t *j_amount; + json_t *j_tax_amount; + json_t *j_item_price; + json_t *j_max_fee; + json_t *j_teatax; + json_t *j_id; // trans id + json_t *j_pid; // product id + json_t *j_quantity; + json_t *j_delloc; + json_t *j_merchant; + json_t *j_merchant_jurisdiction; + json_t *j_merchant_zipcode; + json_t *j_lnames; + json_t *j_deldate; + char *contract_tmp_str; + char *desc; + struct TALER_Amount amount; + int64_t t_id; + int64_t p_id; + #ifdef OBSOLETE + struct ContractNBO contract; + #else + struct Contract contract; + #endif + struct GNUNET_TIME_Absolute edate; + struct GNUNET_TIME_Absolute now; + uint64_t nounce; + struct GNUNET_HashCode h_contract_str; + char *aa; + char *fancy_time; + uint32_t ret; + + db_conn = NULL; + keyfile = NULL; + privkey = NULL; + wire = NULL; + + + db_conn = MERCHANT_DB_connect (config); + if (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_NO)) + { + printf ("no db init'd\n"); + result = GNUNET_SYSERR; + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config, + "merchant", + "KEYFILE", + &keyfile)) + { + printf ("no keyfile entry in cfg file\n"); + result = GNUNET_SYSERR; + } + + privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile); + wire = TALER_MERCHANT_parse_wireformat_sepa (config); + shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &do_shutdown, NULL); + + /** + * 'Root' object of the contract, leaving some holes to bi filled + * up by the merchant library. + * + */ + + /* Amount */ + TALER_amount_get_zero ("EUR", &amount); + j_amount = TALER_json_from_amount (&amount); + + /* Transaction ID*/ + //t_id = (int32_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + t_id = 321; + + if (t_id < 0) + j_id = json_integer ((-1) * t_id); + else + j_id = json_integer (t_id); + + /** + * Holes: + * + * - 'h_wire' + * - 'timestamp' + * + */ + + /** + * + * Preparing the 'details' sub-object: an array of 'item' objects + * plus some juridical and delivery-aware informations + * + */ + + /** + * + * Preparing a 'item' sub-object + */ + + /* Description */ + desc = "Fake purchase"; + + j_max_fee = TALER_json_from_amount (&amount); + /* Quantity: OPTIONAL FIELD */ + j_quantity = json_integer (3); + + /* item price: OPTIONAL FIELD*/ + j_item_price = TALER_json_from_amount (&amount); + + /* Product ID */ + p_id = (int32_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + + if (p_id < 0) + j_pid = json_integer ((-1) * p_id); + else + j_pid = json_integer (p_id); + + /* Taxes: array of "tax_name" : {tax amount} */ + j_tax_amount = TALER_json_from_amount (&amount); + j_teatax = json_pack ("{s:o}", + "teatax", j_tax_amount); + + if (NULL == (j_item = json_pack ("{s:s, s:I, s:o, s:[o]}", + "description", desc, + "quantity", json_integer_value (j_quantity), + "itemprice", j_item_price, + "taxes", j_teatax))) + { + printf ("error in packing [j_item: %p]\n", j_item); + return; + } + + /* End of 'item' object definition */ + + /* Delivery date: OPTIONAL FIELD */ + now = GNUNET_TIME_absolute_get (); + TALER_round_abs_time (&now); +// deldate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + j_deldate = TALER_json_from_abs (now); + + /* Delivery location: OPTIONAL FIELD */ + j_delloc = json_string ("MALTK"); /* just a 'tag' which points to some well defined location */ + + + /* Merchant jurisdiction: OPTIONAL FIELD (with its fields from 3rd to the last being optional) + * for another optional field */ + j_merchant_zipcode = json_integer (9468); + j_merchant_jurisdiction = json_pack ("{s:s, s:s, s:s, s:s, s:s, s:I}", + "country", "Test Country", + "city", "Test City", + "state", "NA", + "region", "NA", + "province", "NA", + "ZIP code", json_integer_value (j_merchant_zipcode)); + + /* Merchant details */ + j_merchant = json_pack ("{s:s, s:s, s:o}", + "address", "UALMP", + "name", "test merchant", + "jurisdiction", j_merchant_jurisdiction); + + + /* L-names mapping */ + j_lnames = json_pack ("[{s:s}, {s:s}]", + "MALTK", "Test Address 1", + "UALMP", "Second Test Address"); + + + + j_details = json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o}", + "product_id", json_integer_value (j_pid), + "items", j_item, + "delivery date", j_deldate, + "delivery location", j_delloc, + "merchant", j_merchant, + "L-names", j_lnames); + + /* Faking out the mints' list */ + j_mints = json_pack ("[{s:s}]", + "demo.taler.net", + "Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0"); + + j_fake_contract = json_pack ("{s:o, s:o, s:I, s:o, s:o}", + "amount", j_amount, + "max fee", j_max_fee, + "trans_id", json_integer_value (j_id), + "mints", j_mints, + "details", j_details); + #if 0 + str = json_dumps (j_fake_contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + printf ("%s\n", str); + return; + #endif + + nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + + j_wire = MERCHANT_get_wire_json (wire, nounce, now); + + ret = MERCHANT_handle_contract (j_fake_contract, + db_conn, + &contract, + now, + now, + now, + now, + &aa, + nounce); + if (ret == GNUNET_NO) + { + printf ("Failed, contract already in DB\n"); + return; + } + else + printf ("handling contract fine\n"); + + + printf ("contract string : %s\n", aa); + + GNUNET_CRYPTO_hash (aa, strlen (aa) + 1, &h_contract_str); + if (GNUNET_SYSERR == MERCHANT_DB_get_contract_values (db_conn, &h_contract_str, &nounce, &edate)) + printf ("no hash found\n"); + else + { + + fancy_time = GNUNET_STRINGS_absolute_time_to_string (edate); + printf ("hash found!, nounce is : %llu\n", nounce); + printf ("hash found!, time is : %s\n", fancy_time); + } + + return; +} + + +/** + * The main function of the test tool + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'t', "temp", NULL, + gettext_noop ("Use temporary database tables"), GNUNET_NO, + &GNUNET_GETOPT_set_one, &dry}, + GNUNET_GETOPT_OPTION_END + }; + + + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, + "merchant-contract-test", + "Test for contracts mgmt", + options, &run, NULL)) + return 3; + return (GNUNET_OK == result) ? 0 : 1; + + + +} -- cgit v1.2.3