diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/merchant.conf | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 19 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 8 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-orders-ID-claim.c | 14 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-delete-orders-ID.c | 14 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 638 | ||||
-rw-r--r-- | src/backenddb/test_merchantdb.c | 970 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 33 |
8 files changed, 294 insertions, 1406 deletions
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf index 4aac2dab..6a0cc53e 100644 --- a/src/backend/merchant.conf +++ b/src/backend/merchant.conf @@ -17,6 +17,10 @@ PORT = 9966 # if left empty. Only used if "SERVE" is 'tcp'. # BIND_TO = +# How long do we keep contract / payment information around after the +# purchase (for tax records and other legal reasons). +LEGAL_PRESERVATION = 11 years + # Which unix domain path should we bind to? Only used if "SERVE" is 'unix'. UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 7406d1fc..f11d18ca 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -74,6 +74,13 @@ struct TALER_MERCHANTDB_Plugin *TMH_db; struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map; /** + * How long do we need to keep information on paid contracts on file for tax + * or other legal reasons? Used to block deletions for younger transaction + * data. + */ +struct GNUNET_TIME_Relative TMH_legal_expiration; + +/** * The port we are running on */ static uint16_t port; @@ -1189,6 +1196,18 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + "merchant", + "LEGAL_PRESERVATION", + &TMH_legal_expiration)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "merchant", + "LEGAL_PRESERVATION"); + GNUNET_SCHEDULER_shutdown (); + return; + } if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg, "merchant", diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h index a6867e24..ceb7dfe2 100644 --- a/src/backend/taler-merchant-httpd.h +++ b/src/backend/taler-merchant-httpd.h @@ -353,6 +353,14 @@ extern struct TALER_MERCHANTDB_Plugin *TMH_db; extern struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map; /** + * How long do we need to keep information on paid contracts on file for tax + * or other legal reasons? Used to block deletions for younger transaction + * data. + */ +extern struct GNUNET_TIME_Relative TMH_legal_expiration; + + +/** * Kick MHD to run now, to be called after MHD_resume_connection(). * Basically, we need to explicitly resume MHD's event loop whenever * we made progress serving a request. This function re-schedules diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-claim.c b/src/backend/taler-merchant-httpd_post-orders-ID-claim.c index e58a73c5..6b3a9229 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-claim.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-claim.c @@ -31,7 +31,7 @@ /** - * How often do we retry the simple INSERT database transaction? + * How often do we retry the database transaction? */ #define MAX_RETRIES 3 @@ -103,7 +103,17 @@ claim_order (const char *instance_id, *contract_terms = NULL; return qs; } - // FIXME: should we remove the ORDER from the order table here? + qs = TMH_db->delete_order (TMH_db->cls, + instance_id, + order_id); + if (0 >= qs) + { + GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); + json_decref (*contract_terms); + *contract_terms = NULL; + return qs; + } qs = TMH_db->commit (TMH_db->cls); if (0 > qs) return qs; diff --git a/src/backend/taler-merchant-httpd_private-delete-orders-ID.c b/src/backend/taler-merchant-httpd_private-delete-orders-ID.c index f699bdc0..73994327 100644 --- a/src/backend/taler-merchant-httpd_private-delete-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-delete-orders-ID.c @@ -40,13 +40,14 @@ TMH_private_delete_orders_ID (const struct TMH_RequestHandler *rh, enum GNUNET_DB_QueryStatus qs; GNUNET_assert (NULL != mi); - // FIXME: do we delete ORDERS or (claimed) contract_terms? - // FIXME: what SHOULD be the semantics here? - // NOTE: We MAY need the delete_order() DB API to - // clean up the order table when claiming orders... qs = TMH_db->delete_order (TMH_db->cls, mi->settings.id, hc->infix); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + qs = TMH_db->delete_contract_terms (TMH_db->cls, + mi->settings.id, + hc->infix, + TMH_legal_expiration); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -66,6 +67,11 @@ TMH_private_delete_orders_ID (const struct TMH_RequestHandler *rh, hc->infix, NULL); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + qs = TMH_db->lookup_contract_terms (TMH_db->cls, + mi->settings.id, + hc->infix, + NULL); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_ORDERS_DELETE_NO_SUCH_ORDER, diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index bb43fd4a..f02d0900 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -26,6 +26,7 @@ #include <taler/taler_util.h> #include <taler/taler_pq_lib.h> #include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> #include "taler_merchantdb_plugin.h" /** @@ -1351,28 +1352,25 @@ postgres_insert_order_lock (void *cls, } -/* ********************* OLD API ************************** */ - /** - * Retrieve proposal data given its proposal data's hashcode + * Retrieve contract terms given its @a order_id * * @param cls closure - * @param contract_terms where to store the retrieved proposal data - * @param h_contract_terms proposal data's hashcode that will be used to - * perform the lookup + * @param instance_id instance's identifier + * @param order_id order_id used to lookup. + * @param[out] contract_terms where to store the result, NULL to only check for existence * @return transaction status */ static enum GNUNET_DB_QueryStatus -postgres_find_contract_terms_from_hash ( - void *cls, - json_t **contract_terms, - const struct GNUNET_HashCode *h_contract_terms, - const struct TALER_MerchantPublicKeyP *merchant_pub) +postgres_lookup_contract_terms (void *cls, + const char *instance_id, + const char *order_id, + json_t **contract_terms) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -1381,73 +1379,149 @@ postgres_find_contract_terms_from_hash ( GNUNET_PQ_result_spec_end }; + *contract_terms = NULL; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "find_contract_terms_from_hash", + "lookup_contract_terms", params, rs); } /** - * Retrieve proposal data given its proposal data's hashcode + * Store contract terms given its @a order_id. Note that some attributes are + * expected to be calculated inside of the function, like the hash of the + * contract terms (to be hashed), the creation_time and pay_deadline (to be + * obtained from the merchant_orders table). The "session_id" should be + * initially set to the empty string. The "fulfillment_url" and "refund_deadline" + * must be extracted from @a contract_terms. * * @param cls closure - * @param contract_terms where to store the retrieved proposal data - * @param h_contract_terms proposal data's hashcode that will be used to - * perform the lookup - * @return transaction status + * @param instance_id instance's identifier + * @param order_id order_id used to store + * @param contract_terms contract to store + * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms + * is malformed */ static enum GNUNET_DB_QueryStatus -postgres_find_paid_contract_terms_from_hash (void *cls, - json_t **contract_terms, - const struct - GNUNET_HashCode *h_contract_terms, - const struct - TALER_MerchantPublicKeyP * - merchant_pub) +postgres_insert_contract_terms (void *cls, + const char *instance_id, + const char *order_id, + json_t *contract_terms) { struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Absolute pay_deadline; + struct GNUNET_TIME_Absolute refund_deadline; + const char *fulfillment_url; + struct GNUNET_HashCode h_contract_terms; + + if (GNUNET_OK != + TALER_JSON_hash (contract_terms, + &h_contract_terms)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("fulfillment_url", + &fulfillment_url), + GNUNET_JSON_spec_absolute_time ("pay_deadline", + &pay_deadline), + GNUNET_JSON_spec_absolute_time ("refund_deadline", + &refund_deadline), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (NULL, + contract_terms, + spec); + if (GNUNET_OK != res) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + } + + check_connection (pg); + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (order_id), + TALER_PQ_query_param_json (contract_terms), + GNUNET_PQ_query_param_auto_from_type (&h_contract_terms), + GNUNET_PQ_query_param_absolute_time (&pay_deadline), + GNUNET_PQ_query_param_absolute_time (&refund_deadline), + GNUNET_PQ_query_param_string (fulfillment_url), + GNUNET_PQ_query_param_end + }; + + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_contract_terms", + params); + } +} + + +/** + * Delete information about a contract. Note that the transaction must + * enforce that the contract is not awaiting payment anymore AND was not + * paid, or is past the legal expiration. + * + * @param cls closure + * @param instance_id instance to delete order of + * @param order_id order to delete + * @param legal_expiration how long do we need to keep (paid) contracts on + * file for legal reasons (i.e. taxation) + * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + * if locks prevent deletion OR order unknown + */ +static enum GNUNET_DB_QueryStatus +postgres_delete_contract_terms (void *cls, + const char *instance_id, + const char *order_id, + struct GNUNET_TIME_Relative legal_expiration) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (order_id), + GNUNET_PQ_query_param_relative_time (&legal_expiration), + GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_json ("contract_terms", - contract_terms), - GNUNET_PQ_result_spec_end - }; - /* no preflight check here, runs in its own transaction from - caller (in /pay case) */ check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "find_paid_contract_terms_from_hash", - params, - rs); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "delete_contract_terms", + params); } +/* ********************* OLD API ************************** */ + /** - * Retrieve proposal data given its order id. Ignores if the - * proposal has been paid or not. + * Retrieve proposal data given its proposal data's hashcode * * @param cls closure - * @param[out] contract_terms where to store the retrieved contract terms - * @param order id order id used to perform the lookup + * @param contract_terms where to store the retrieved proposal data + * @param h_contract_terms proposal data's hashcode that will be used to + * perform the lookup * @return transaction status */ static enum GNUNET_DB_QueryStatus -postgres_find_contract_terms (void *cls, - json_t **contract_terms, - const char *order_id, - const struct - TALER_MerchantPublicKeyP *merchant_pub) +postgres_find_contract_terms_from_hash ( + void *cls, + json_t **contract_terms, + const struct GNUNET_HashCode *h_contract_terms, + const struct TALER_MerchantPublicKeyP *merchant_pub) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (order_id), + GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; @@ -1457,64 +1531,51 @@ postgres_find_contract_terms (void *cls, GNUNET_PQ_result_spec_end }; - *contract_terms = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finding contract term, order_id: '%s', merchant_pub: '%s'.\n", - order_id, - TALER_B2S (merchant_pub)); check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "find_contract_terms", + "find_contract_terms_from_hash", params, rs); } /** - * Insert proposal data and its hashcode into db + * Retrieve proposal data given its proposal data's hashcode * * @param cls closure - * @param order_id identificator of the proposal being stored - * @param merchant_pub merchant's public key - * @param timestamp timestamp of this proposal data - * @param contract_terms proposal data to store + * @param contract_terms where to store the retrieved proposal data + * @param h_contract_terms proposal data's hashcode that will be used to + * perform the lookup * @return transaction status */ static enum GNUNET_DB_QueryStatus -postgres_insert_contract_terms (void *cls, - const char *order_id, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - struct GNUNET_TIME_Absolute timestamp, - const json_t *contract_terms) +postgres_find_paid_contract_terms_from_hash (void *cls, + json_t **contract_terms, + const struct + GNUNET_HashCode *h_contract_terms, + const struct + TALER_MerchantPublicKeyP * + merchant_pub) { struct PostgresClosure *pg = cls; - struct GNUNET_HashCode h_contract_terms; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (order_id), + GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_absolute_time (×tamp), - TALER_PQ_query_param_json (contract_terms), - GNUNET_PQ_query_param_auto_from_type (&h_contract_terms), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_json ("contract_terms", + contract_terms), + GNUNET_PQ_result_spec_end + }; - if (GNUNET_OK != - TALER_JSON_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "inserting contract terms: order_id: %s, merchant_pub: %s, h_contract_terms: %s.\n", - order_id, - TALER_B2S (merchant_pub), - GNUNET_h2s (&h_contract_terms)); + /* no preflight check here, runs in its own transaction from + caller (in /pay case) */ check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_contract_terms", - params); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_paid_contract_terms_from_hash", + params, + rs); } @@ -1765,205 +1826,6 @@ postgres_store_transfer_to_proof (void *cls, /** - * Lookup for a proposal, respecting the signature used by the - * /history's db methods. - * - * @param cls db plugin handle - * @param order_id order id used to search for the proposal data - * @param merchant_pub public key of the merchant using this method - * @param cb the callback - * @param cb_cls closure to pass to the callback - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_find_contract_terms_history (void *cls, - const char *order_id, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - TALER_MERCHANTDB_ProposalDataCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - json_t *contract_terms; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_json ("contract_terms", - &contract_terms), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "find_contract_terms_history", - params, - rs); - if (qs <= 0) - return qs; - if (NULL != cb) - cb (cb_cls, - order_id, - 0, - contract_terms); - GNUNET_PQ_cleanup_result (rs); - return qs; -} - - -/** - * Closure for #find_contracts_cb(). - */ -struct FindContractsContext -{ - /** - * Function to call on each result. - */ - TALER_MERCHANTDB_ProposalDataCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Transaction status code to set. - */ - enum GNUNET_DB_QueryStatus qs; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls of type `struct FindContractsContext *` - * @param result the postgres result - * @param num_result the number of results in @a result - */ -static void -find_contracts_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct FindContractsContext *fcctx = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - char *order_id; - json_t *contract_terms; - uint64_t row_id; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("order_id", - &order_id), - TALER_PQ_result_spec_json ("contract_terms", - &contract_terms), - GNUNET_PQ_result_spec_uint64 ("row_id", - &row_id), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - fcctx->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - fcctx->qs = i + 1; - fcctx->cb (fcctx->cb_cls, - order_id, - row_id, - contract_terms); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Return proposals whose timestamp are older than `date`. - * Among those proposals, only those ones being between the - * start-th and (start-nrows)-th record are returned. The rows - * are sorted having the youngest first. - * - * @param cls our plugin handle. - * @param date only results older than this date are returned. - * @param merchant_pub instance's public key; only rows related to this - * instance are returned. - * @param start only rows with serial id less than start are returned. - * In other words, you lower `start` to get older records. The tipical - * usage is to firstly call `find_contract_terms_by_date`, so that you get - * the `nrows` youngest records. The oldest of those records will tell you - * from which timestamp and `start` you can query the DB in order to get - * furtherly older records, and so on. Alternatively, you can use always - * the same timestamp and just go behind in history by tuning `start`. - * @param nrows only nrows rows are returned. - * @param past if set to #GNUNET_YES, retrieves rows older than `date`. - * @param ascending if #GNUNET_YES, results will be sorted in chronological order. - * This is typically used to show live updates on the merchant's backoffice - * Web interface. - * @param cb function to call with transaction data, can be NULL. - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_find_contract_terms_by_date_and_range (void *cls, - struct GNUNET_TIME_Absolute - date, - const struct - TALER_MerchantPublicKeyP * - merchant_pub, - uint64_t start, - uint64_t nrows, - int past, - unsigned int ascending, - TALER_MERCHANTDB_ProposalDataCallback - cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_absolute_time (&date), - GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_uint64 (&start), - GNUNET_PQ_query_param_uint64 (&nrows), - GNUNET_PQ_query_param_end - }; - const char *stmt; - enum GNUNET_DB_QueryStatus qs; - struct FindContractsContext fcctx = { - .cb = cb, - .cb_cls = cb_cls - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DB serving /history with date %s\n", - GNUNET_STRINGS_absolute_time_to_string (date)); - stmt = - (GNUNET_YES == past) - ? ( (GNUNET_YES == ascending) - ? "find_contract_terms_by_date_and_range_past_asc" - : "find_contract_terms_by_date_and_range_past") - : ( (GNUNET_YES == ascending) - ? "find_contract_terms_by_date_and_range_asc" - : "find_contract_terms_by_date_and_range"); - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, - params, - &find_contracts_cb, - &fcctx); - if (0 >= qs) - return qs; - return fcctx.qs; -} - - -/** * Closure for #find_tip_authorizations_cb(). */ struct GetAuthorizedTipAmountContext @@ -2104,53 +1966,6 @@ postgres_get_authorized_tip_amount (void *cls, /** - * Return proposals whose timestamp are older than `date`. - * The rows are sorted having the youngest first. - * - * @param cls our plugin handle. - * @param date only results older than this date are returned. - * @param merchant_pub instance's public key; only rows related to this - * instance are returned. - * @param nrows at most nrows rows are returned. - * @param cb function to call with transaction data, can be NULL. - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_find_contract_terms_by_date (void *cls, - struct GNUNET_TIME_Absolute date, - const struct - TALER_MerchantPublicKeyP *merchant_pub, - uint64_t nrows, - TALER_MERCHANTDB_ProposalDataCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_absolute_time (&date), - GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_uint64 (&nrows), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - struct FindContractsContext fcctx = { - .cb = cb, - .cb_cls = cb_cls - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "find_contract_terms_by_date", - params, - &find_contracts_cb, - &fcctx); - if (0 >= qs) - return qs; - return fcctx.qs; -} - - -/** * Closure for #find_payments_cb(). */ struct FindPaymentsContext @@ -5136,7 +4951,60 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) " FROM merchant_order_locks" " WHERE product_serial=tmp.product_serial)", 4), + /* for postgres_lookup_contract_terms() */ + GNUNET_PQ_make_prepare ("lookup_contract_terms", + "SELECT contract_terms" + " FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)", + 2), + /* for postgres_insert_contract_terms() */ + GNUNET_PQ_make_prepare ("insert_contract_terms", + "INSERT INTO merchant_contract_terms" + "(order_serial" + ",merchant_serial" + ",order_id" + ",contract_terms" + ",h_contract_terms" + ",creation_time" + ",pay_deadline" + ",refund_deadline" + ",fulfillment_url)" + "SELECT" + " order_serial" + ",merchant_serial" + ",order_id" + ",$3" /* contract_terms */ + ",$4" /* h_contract_terms */ + ",creation_time" + ",$5" /* pay_deadline */ + ",$6" /* refund_deadline */ + ",$7" /* fulfillment_url */ + "FROM merchant_orders" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)", + 7), + /* for postgres_delete_contract_terms() */ + GNUNET_PQ_make_prepare ("delete_contract_terms", + "DELETE FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND ( ( (pay_deadline < $4) AND" + " (NOT paid) ) OR" + " (creation_time + $3 > $4) )", + 4), + /* OLD API: */ + #if 0 GNUNET_PQ_make_prepare ("insert_deposit", "INSERT INTO merchant_deposits" @@ -5253,7 +5121,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) " AND merchant_pub=$2" " AND paid=TRUE", 2), - GNUNET_PQ_make_prepare ("find_refunds", "SELECT" " refund_amount_val" @@ -5261,23 +5128,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) " FROM merchant_refunds" " WHERE coin_pub=$1", 1), - GNUNET_PQ_make_prepare ("find_contract_terms_history", - "SELECT" - " contract_terms" - " FROM merchant_contract_terms" - " WHERE" - " order_id=$1" - " AND merchant_pub=$2" - " AND paid=TRUE", - 2), - GNUNET_PQ_make_prepare ("find_contract_terms", - "SELECT" - " contract_terms" - " FROM merchant_contract_terms" - " WHERE" - " order_id=$1" - " AND merchant_pub=$2", - 2), GNUNET_PQ_make_prepare ("find_session_info", "SELECT" " order_id" @@ -5287,19 +5137,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) " AND session_id=$2" " AND merchant_pub=$3", 2), - GNUNET_PQ_make_prepare ("find_contract_terms_by_date", - "SELECT" - " contract_terms" - ",order_id" - ",row_id" - " FROM merchant_contract_terms" - " WHERE" - " timestamp<$1" - " AND merchant_pub=$2" - " AND paid=TRUE" - " ORDER BY row_id DESC, timestamp DESC" - " LIMIT $3", - 3), GNUNET_PQ_make_prepare ("find_refunds_from_contract_terms_hash", "SELECT" " coin_pub" @@ -5337,62 +5174,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) " VALUES " "($1, $2, $3, $4, $5, $6)", 6), - GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_asc", - "SELECT" - " contract_terms" - ",order_id" - ",row_id" - " FROM merchant_contract_terms" - " WHERE" - " timestamp>$1" - " AND merchant_pub=$2" - " AND row_id>$3" - " AND paid=TRUE" - " ORDER BY row_id ASC, timestamp ASC" - " LIMIT $4", - 4), - GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range", - "SELECT" - " contract_terms" - ",order_id" - ",row_id" - " FROM merchant_contract_terms" - " WHERE" - " timestamp>$1" - " AND merchant_pub=$2" - " AND row_id>$3" - " AND paid=TRUE" - " ORDER BY row_id DESC, timestamp DESC" - " LIMIT $4", - 4), - GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_past_asc", - "SELECT" - " contract_terms" - ",order_id" - ",row_id" - " FROM merchant_contract_terms" - " WHERE" - " timestamp<$1" - " AND merchant_pub=$2" - " AND row_id<$3" - " AND paid=TRUE" - " ORDER BY row_id ASC, timestamp ASC" - " LIMIT $4", - 4), - GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_past", - "SELECT" - " contract_terms" - ",order_id" - ",row_id" - " FROM merchant_contract_terms" - " WHERE" - " timestamp<$1" - " AND merchant_pub=$2" - " AND row_id<$3" - " AND paid=TRUE" - " ORDER BY row_id DESC, timestamp DESC" - " LIMIT $4", - 4), GNUNET_PQ_make_prepare ("find_deposits", "SELECT" " coin_pub" @@ -5634,7 +5415,14 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->insert_order = &postgres_insert_order; plugin->unlock_inventory = &postgres_unlock_inventory; plugin->insert_order_lock = &postgres_insert_order_lock; - /* old API: */ + plugin->lookup_contract_terms = &postgres_lookup_contract_terms; + plugin->insert_contract_terms = &postgres_insert_contract_terms; + plugin->delete_contract_terms = &postgres_delete_contract_terms; + /* OLD API: */ + plugin->find_contract_terms_from_hash = + &postgres_find_contract_terms_from_hash; + plugin->find_paid_contract_terms_from_hash = + &postgres_find_paid_contract_terms_from_hash; plugin->store_deposit = &postgres_store_deposit; plugin->store_coin_to_transfer = &postgres_store_coin_to_transfer; plugin->store_transfer_to_proof = &postgres_store_transfer_to_proof; @@ -5645,17 +5433,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->find_transfers_by_hash = &postgres_find_transfers_by_hash; plugin->find_deposits_by_wtid = &postgres_find_deposits_by_wtid; plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid; - plugin->insert_contract_terms = &postgres_insert_contract_terms; - plugin->find_contract_terms = &postgres_find_contract_terms; - plugin->find_contract_terms_history = &postgres_find_contract_terms_history; - plugin->find_contract_terms_by_date = &postgres_find_contract_terms_by_date; plugin->get_authorized_tip_amount = &postgres_get_authorized_tip_amount; - plugin->find_contract_terms_by_date_and_range = - &postgres_find_contract_terms_by_date_and_range; - plugin->find_contract_terms_from_hash = - &postgres_find_contract_terms_from_hash; - plugin->find_paid_contract_terms_from_hash = - &postgres_find_paid_contract_terms_from_hash; plugin->get_refunds_from_contract_terms_hash = &postgres_get_refunds_from_contract_terms_hash; plugin->lookup_wire_fee = &postgres_lookup_wire_fee; diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index 20d02472..fdcf879d 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014-2017 Taler Systems SA + (C) 2014-2017, 2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -19,38 +19,13 @@ * @author Marcello Stanisci * @author Christian Grothoff */ - #include "platform.h" #include <taler/taler_util.h> #include <taler/taler_json_lib.h> #include "taler_merchantdb_lib.h" -#include <jansson.h> - - -#define FAILIF(cond) \ - do { \ - if (! (cond)) { break;} \ - GNUNET_break (0); \ - return; \ - } while (0) - -#define RND_BLK(ptr) \ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) /** - * Currency we use for the coins. - */ -#define CURRENCY "EUR" - -/** - * URL we use for the exchange in the database. - * Note that an exchange does not actually have - * to run at this address. - */ -#define EXCHANGE_URL "http://localhost:8888/" - -/** * Global return value for the test. Initially -1, set to 0 upon * completion. Other values indicate some kind of error. */ @@ -61,937 +36,6 @@ static int result; */ static struct TALER_MERCHANTDB_Plugin *plugin; -/** - * Hash of the wire transfer address. Set to some random value. - */ -static struct GNUNET_HashCode h_wire; - -/** - * Transaction ID. - */ -const char *order_id; - -/** - * Transaction ID used to test the db query - * `find_contract_terms_by_date_and_range_future` - */ -const char *order_id_future; - -/** - * Proposal's hash - */ -struct GNUNET_HashCode h_contract_terms; - -/** - * Proposal's hash. - */ -struct GNUNET_HashCode h_contract_terms_future; - -/** - * Time of the transaction. - */ -static struct GNUNET_TIME_Absolute timestamp; - -/** - * Delta aimed to test the "by_date" query on transactions. - */ -static struct GNUNET_TIME_Relative delta; - -/** - * Deadline until which refunds are allowed. - */ -static struct GNUNET_TIME_Absolute refund_deadline; - -/** - * Total amount, including deposit fee. - */ -static struct TALER_Amount amount_with_fee; - -/** - * Deposit fee for the coin. - */ -static struct TALER_Amount deposit_fee; - -/** - * Wire fee of the exchange. - */ -static struct TALER_Amount wire_fee; - -/** - * Refund fee for the coin. - */ -static struct TALER_Amount refund_fee; - -/** - * Amount to be refunded. - */ -static struct TALER_Amount refund_amount; - -/** - * Amount to be refunded. Used to trigger error about - * subsequent refund amount being lesser than the previous - * ones. - */ -static struct TALER_Amount little_refund_amount; - - -/** - * Amount to be refunded in a call which is subsequent - * to the good one, expected to succeed. - */ -static struct TALER_Amount right_second_refund_amount; - -/** - * Refund amount meant to raise an error because the - * contract's coins aren't enough to pay it back - */ -static struct TALER_Amount too_big_refund_amount; - -/** - * Public key of the coin. Set to some random value. - */ -static struct TALER_CoinSpendPublicKeyP coin_pub; - -/** - * Public key of the exchange. Set to some random value. - */ -static struct TALER_ExchangePublicKeyP signkey_pub; - -/** - * Public Key of the merchant. Set to some random value. - * Used as merchant instances now do store their keys. - */ -static struct TALER_MerchantPublicKeyP merchant_pub; - -/** - * Wire transfer identifier. Set to some random value. - */ -static struct TALER_WireTransferIdentifierRawP wtid; - -/** - * "Proof" of deposit from the exchange. Set to some valid JSON. - */ -static json_t *deposit_proof; - -/** - * "Proof" of wire transfer from the exchange. Set to some valid JSON. - */ -static json_t *transfer_proof; - -/** - * A mock contract, not need to be well-formed - */ -static json_t *contract; - -/** - * Mock proposal data, not need to be well-formed - */ -static json_t *contract_terms; - -/** - * Mock proposal data, not need to be well-formed - */ -static json_t *contract_terms_future; - - -/** - * Function called with information about a refund. - * - * @param cls closure - * @param coin_pub public coin from which the refund comes from - * @param exchange_url URL of the exchange that issued the @a coin_pub - * @param rtransaction_id identificator of the refund - * @param reason human-readable explanation of the refund - * @param refund_amount refund amount which is being taken from @a coin_pub - * @param refund_fee cost of this refund operation - */ -static void -refund_cb (void *cls, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const char *exchange_url, - uint64_t rtransaction_id, - const char *reason, - const struct TALER_Amount *refund_amount, - const struct TALER_Amount *refund_fee) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "refund_cb\n"); - /* FIXME, more logic here? */ -} - - -/** - * Callback for `find_contract_terms_by_date`. - * - * @param cls closure - * @param order_id order id - * @param row_id row id in db - * @param contract_terms proposal data - */ -static void -pd_cb (void *cls, - const char *order_id, - uint64_t row_id, - const json_t *contract_terms) -{ - return; -} - - -#define CHECK(a) do { if (! (a)) { GNUNET_break (0); result = 3; } } while (0) - - -/** - * Function called with information about a coin that was deposited. - * - * @param cls closure - * @param transaction_id of the contract - * @param acoin_pub public key of the coin - * @param aexchange_url exchange associated with @a acoin_pub in DB - * @param aamount_with_fee amount the exchange will deposit for this coin - * @param adeposit_fee fee the exchange will charge for this coin - * @param adeposit_fee fee the exchange will charge for refunding this coin - * @param exchange_proof proof from exchange that coin was accepted - */ -static void -deposit_cb (void *cls, - const struct GNUNET_HashCode *ah_contract_terms, - const struct TALER_CoinSpendPublicKeyP *acoin_pub, - const char *aexchange_url, - const struct TALER_Amount *aamount_with_fee, - const struct TALER_Amount *adeposit_fee, - const struct TALER_Amount *arefund_fee, - const struct TALER_Amount *awire_fee, - const json_t *aexchange_proof) -{ - CHECK (0 == GNUNET_memcmp (ah_contract_terms, - &h_contract_terms)); - CHECK (0 == GNUNET_memcmp (acoin_pub, - &coin_pub)); - CHECK (0 == strcmp (aexchange_url, - EXCHANGE_URL)); - CHECK (0 == TALER_amount_cmp (aamount_with_fee, - &amount_with_fee)); - CHECK (0 == TALER_amount_cmp (adeposit_fee, - &deposit_fee)); - CHECK (0 == TALER_amount_cmp (awire_fee, - &wire_fee)); - CHECK (1 == json_equal ((json_t *) aexchange_proof, - deposit_proof)); -} - - -/** - * Information about the wire transfer corresponding to - * a deposit operation. Note that it is in theory possible - * that we have a @a transaction_id and @a coin_pub in the - * result that do not match a deposit that we know about, - * for example because someone else deposited funds into - * our account. - * - * @param cls closure - * @param transaction_id ID of the contract - * @param coin_pub public key of the coin - * @param wtid identifier of the wire transfer in which the exchange - * send us the money for the coin deposit - * @param execution_time when was the @a wtid transfer executed - * @param exchange_proof proof from exchange about what the deposit was for - * NULL if we have not asked for this signature - */ -static void -transfer_cb (void *cls, - const struct GNUNET_HashCode *ah_contract_terms, - const struct TALER_CoinSpendPublicKeyP *acoin_pub, - const struct TALER_WireTransferIdentifierRawP *awtid, - struct GNUNET_TIME_Absolute execution_time, - const json_t *exchange_proof) -{ - CHECK (0 == GNUNET_memcmp (ah_contract_terms, - &h_contract_terms)); - - CHECK (0 == GNUNET_memcmp (acoin_pub, - &coin_pub)); - CHECK (0 == GNUNET_memcmp (awtid, - &wtid)); - CHECK (1 == json_equal ((json_t *) exchange_proof, - transfer_proof)); -} - - -/** - * Function called with information about a wire transfer identifier. - * - * @param cls closure - * @param proof proof from exchange about what the wire transfer was for - */ -static void -proof_cb (void *cls, - const json_t *proof) -{ - CHECK (1 == json_equal ((json_t *) proof, - transfer_proof)); -} - - -#undef CHECK - - -/** - * Test the wire fee storage. - * - * @return #GNUNET_OK on success - */ -static int -test_wire_fee () -{ - struct TALER_MasterPublicKeyP exchange_pub; - struct GNUNET_HashCode h_wire_method; - struct GNUNET_TIME_Absolute contract_date; - struct TALER_Amount wire_fee1; - struct TALER_Amount closing_fee1; - struct TALER_Amount wire_fee2; - struct TALER_Amount closing_fee2; - struct TALER_Amount wire_fee3; - struct TALER_Amount closing_fee3; - struct GNUNET_TIME_Absolute date1; - struct GNUNET_TIME_Absolute date2; - struct GNUNET_TIME_Absolute date3; - struct GNUNET_TIME_Absolute start_date; - struct GNUNET_TIME_Absolute end_date; - struct TALER_MasterSignatureP exchange_sig; - struct TALER_MasterSignatureP exchange_sig2; - - RND_BLK (&exchange_pub); - RND_BLK (&h_wire_method); - RND_BLK (&exchange_sig); - date1 = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&date1); - date2 = GNUNET_TIME_absolute_add (date1, - GNUNET_TIME_UNIT_DAYS); - date3 = GNUNET_TIME_absolute_add (date2, - GNUNET_TIME_UNIT_DAYS); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":5", - &closing_fee1)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":4", - &wire_fee1)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":3", - &closing_fee2)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":2", - &wire_fee2)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_wire_fee_by_exchange (plugin->cls, - &exchange_pub, - &h_wire_method, - &wire_fee1, - &closing_fee1, - date1, - date2, - &exchange_sig)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_wire_fee_by_exchange (plugin->cls, - &exchange_pub, - &h_wire_method, - &wire_fee2, - &closing_fee2, - date2, - date3, - &exchange_sig)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - contract_date = date2; /* test inclusive/exclusive range */ - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->lookup_wire_fee (plugin->cls, - &exchange_pub, - &h_wire_method, - contract_date, - &wire_fee3, - &closing_fee3, - &start_date, - &end_date, - &exchange_sig2)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if ( (start_date.abs_value_us != date2.abs_value_us) || - (end_date.abs_value_us != date3.abs_value_us) || - (0 != GNUNET_memcmp (&exchange_sig, - &exchange_sig2)) || - (0 != TALER_amount_cmp (&wire_fee2, - &wire_fee3)) || - (0 != TALER_amount_cmp (&closing_fee2, - &closing_fee3)) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - contract_date = GNUNET_TIME_absolute_add (date1, - GNUNET_TIME_UNIT_SECONDS); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->lookup_wire_fee (plugin->cls, - &exchange_pub, - &h_wire_method, - contract_date, - &wire_fee3, - &closing_fee3, - &start_date, - &end_date, - &exchange_sig2)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if ( (start_date.abs_value_us != date1.abs_value_us) || - (end_date.abs_value_us != date2.abs_value_us) || - (0 != GNUNET_memcmp (&exchange_sig, - &exchange_sig2)) || - (0 != TALER_amount_cmp (&wire_fee1, - &wire_fee3)) || - (0 != TALER_amount_cmp (&closing_fee1, - &closing_fee3)) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - contract_date = date3; /* outside of valid range! */ - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->lookup_wire_fee (plugin->cls, - &exchange_pub, - &h_wire_method, - contract_date, - &wire_fee3, - &closing_fee3, - &start_date, - &end_date, - &exchange_sig2)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Test APIs related to tipping. - * - * @return #GNUNET_OK upon success - */ -static int -test_tipping () -{ - struct TALER_ReservePrivateKeyP tip_reserve_priv; - struct TALER_ReservePrivateKeyP pres; - struct GNUNET_HashCode tip_id; - struct GNUNET_HashCode tip_credit_uuid; - struct GNUNET_HashCode pickup_id; - struct GNUNET_TIME_Absolute tip_expiration; - struct GNUNET_TIME_Absolute reserve_expiration; - struct TALER_Amount total; - struct TALER_Amount amount; - struct TALER_Amount inc; - char *url; - - RND_BLK (&tip_reserve_priv); - if (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS != - plugin->authorize_tip_TR (plugin->cls, - "testing tips reserve unknown", - json_object (), - &amount, - &tip_reserve_priv, - "http://localhost:8081/", - &tip_expiration, - &tip_id)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - RND_BLK (&tip_credit_uuid); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":5", - &total)); - /* Pick short expiration, but long enough to - run 2 DB interactions even on very slow systems. */ - reserve_expiration = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, - 2)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->enable_tip_reserve_TR (plugin->cls, - &tip_reserve_priv, - &tip_credit_uuid, - &total, - reserve_expiration)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* check idempotency */ - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->enable_tip_reserve_TR (plugin->cls, - &tip_reserve_priv, - &tip_credit_uuid, - &total, - reserve_expiration)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* Make sure it has expired, so at this point the value is back at ZERO! */ - sleep (3); - if (TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED != - plugin->authorize_tip_TR (plugin->cls, - "testing tips too late", - json_object (), - &amount, - &tip_reserve_priv, - "http://localhost:8081/", - &tip_expiration, - &tip_id)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - /* Re-add some funds again */ - RND_BLK (&tip_credit_uuid); - reserve_expiration = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, - 2)); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->enable_tip_reserve_TR (plugin->cls, - &tip_reserve_priv, - &tip_credit_uuid, - &total, - reserve_expiration)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* top it up by adding more with a fresh UUID - and even longer expiration time (until end of test) */ - RND_BLK (&tip_credit_uuid); - reserve_expiration = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->enable_tip_reserve_TR (plugin->cls, - &tip_reserve_priv, - &tip_credit_uuid, - &total, - reserve_expiration)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - /* Now authorize some tips */ - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":4", - &amount)); - if (TALER_EC_NONE != - plugin->authorize_tip_TR (plugin->cls, - "testing tips", - json_object (), - &amount, - &tip_reserve_priv, - "http://localhost:8081/", - &tip_expiration, - &tip_id)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (tip_expiration.abs_value_us != reserve_expiration.abs_value_us) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->lookup_tip_by_id (plugin->cls, - &tip_id, - &url, - NULL, NULL, NULL, NULL)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 != strcmp ("http://localhost:8081/", - url)) - { - GNUNET_free (url); - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_free (url); - if (TALER_EC_NONE != - plugin->authorize_tip_TR (plugin->cls, - "testing tips more", - json_object (), - &amount, - &tip_reserve_priv, - "http://localhost:8081/", - &tip_expiration, - &tip_id)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (tip_expiration.abs_value_us != reserve_expiration.abs_value_us) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - /* Let's try to pick up the authorized tip in 2 increments */ - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":2", - &inc)); - RND_BLK (&pickup_id); - if (TALER_EC_NONE != - plugin->pickup_tip_TR (plugin->cls, - &inc, - &tip_id, - &pickup_id, - &pres)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 != GNUNET_memcmp (&pres, - &tip_reserve_priv)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - RND_BLK (&pickup_id); - if (TALER_EC_NONE != - plugin->pickup_tip_TR (plugin->cls, - &inc, - &tip_id, - &pickup_id, - &pres)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 != GNUNET_memcmp (&pres, - &tip_reserve_priv)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - /* Third attempt should fail, as we've picked up 4/4 in amount */ - RND_BLK (&pickup_id); - if (TALER_EC_TIP_PICKUP_NO_FUNDS != - plugin->pickup_tip_TR (plugin->cls, - &inc, - &tip_id, - &pickup_id, - &pres)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - /* We authorized 8 out of 10, so going for another 4 should fail with insufficient funds */ - if (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS != - plugin->authorize_tip_TR (plugin->cls, - "testing tips insufficient funds", - json_object (), - &amount, - &tip_reserve_priv, - "http://localhost:8081/", - &tip_expiration, - &tip_id)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - /* Test that picking up with random (unauthorized) tip_id fails as well */ - RND_BLK (&tip_id); - RND_BLK (&pickup_id); - if (TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN != - plugin->pickup_tip_TR (plugin->cls, - &inc, - &tip_id, - &pickup_id, - &pres)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - return GNUNET_OK; -} - - -static void -test (struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_TIME_Absolute fake_now; - json_t *out; - - /* Prepare data for 'store_payment()' */ - RND_BLK (&h_wire); - RND_BLK (&h_contract_terms); - order_id = "test_ID"; - order_id_future = "test_ID_future"; - RND_BLK (&signkey_pub); - RND_BLK (&merchant_pub); - RND_BLK (&wtid); - timestamp = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (×tamp); - delta = GNUNET_TIME_UNIT_MINUTES; - fake_now = GNUNET_TIME_absolute_add (timestamp, delta); - refund_deadline = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&refund_deadline); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":5", - &amount_with_fee)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &deposit_fee)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000001", - &wire_fee)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.000010", - &refund_fee)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":2", - &refund_amount)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":1", - &little_refund_amount)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":3", - &right_second_refund_amount)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":30", - &too_big_refund_amount)); - RND_BLK (&coin_pub); - deposit_proof = json_object (); - GNUNET_assert (0 == - json_object_set_new (deposit_proof, - "x-taler-bank", - json_string ("backenddb test A"))); - transfer_proof = json_object (); - GNUNET_assert (0 == - json_object_set_new (transfer_proof, - "x-taler-bank", - json_string ("backenddb test B"))); - contract = json_object (); - contract_terms = json_object (); - GNUNET_assert (0 == - json_object_set_new (contract_terms, - "order", - json_string ("1"))); - - contract_terms_future = json_object (); - GNUNET_assert (0 == - json_object_set_new (contract_terms_future, - "order", - json_string ("2"))); - - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (contract_terms, - &h_contract_terms)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_contract_terms (plugin->cls, - order_id, - &merchant_pub, - timestamp, - contract_terms)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->find_paid_contract_terms_from_hash (plugin->cls, - &out, - &h_contract_terms, - &merchant_pub)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->mark_proposal_paid (plugin->cls, - &h_contract_terms, - &merchant_pub)); - - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->mark_proposal_paid (plugin->cls, - &h_contract_terms, - &merchant_pub)); - - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_contract_terms_history (plugin->cls, - order_id, - &merchant_pub, - &pd_cb, - NULL)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_paid_contract_terms_from_hash (plugin->cls, - &out, - &h_contract_terms, - &merchant_pub)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_contract_terms_from_hash (plugin->cls, - &out, - &h_contract_terms, - &merchant_pub)); - FAILIF (1 != - plugin->find_contract_terms_by_date_and_range (plugin->cls, - fake_now, - &merchant_pub, - 2, - 1, - GNUNET_YES, - GNUNET_NO, - &pd_cb, - NULL)); - timestamp = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (×tamp); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_contract_terms (plugin->cls, - order_id_future, - &merchant_pub, - timestamp, - contract_terms_future)); - - fake_now = GNUNET_TIME_absolute_subtract (timestamp, delta); - - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (contract_terms_future, - &h_contract_terms_future)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->mark_proposal_paid (plugin->cls, - &h_contract_terms_future, - &merchant_pub)); - FAILIF (2 != - plugin->find_contract_terms_by_date_and_range (plugin->cls, - fake_now, - &merchant_pub, - 0, - 5, - GNUNET_NO, - GNUNET_NO, - &pd_cb, - NULL)); - - FAILIF (0 != - plugin->find_contract_terms_by_date (plugin->cls, - fake_now, - &merchant_pub, - 1, - &pd_cb, - NULL)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_deposit (plugin->cls, - &h_contract_terms, - &merchant_pub, - &coin_pub, - EXCHANGE_URL, - &amount_with_fee, - &deposit_fee, - &refund_fee, - &wire_fee, - &signkey_pub, - deposit_proof)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_coin_to_transfer (plugin->cls, - &h_contract_terms, - &coin_pub, - &wtid)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_transfer_to_proof (plugin->cls, - EXCHANGE_URL, - &wtid, - GNUNET_TIME_UNIT_ZERO_ABS, - &signkey_pub, - transfer_proof)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_payments (plugin->cls, - &h_contract_terms, - &merchant_pub, - &deposit_cb, - NULL)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_transfers_by_hash (plugin->cls, - &h_contract_terms, - &transfer_cb, - NULL)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_deposits_by_wtid (plugin->cls, - &wtid, - &deposit_cb, - NULL)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->find_proof_by_wtid (plugin->cls, - EXCHANGE_URL, - &wtid, - &proof_cb, - NULL)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->get_refunds_from_contract_terms_hash (plugin->cls, - &merchant_pub, - &h_contract_terms, - &refund_cb, - NULL)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->increase_refund_for_contract_NT (plugin->cls, - &h_contract_terms, - &merchant_pub, - &refund_amount, - "refund testing")); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->increase_refund_for_contract_NT (plugin->cls, - &h_contract_terms, - &merchant_pub, - &refund_amount, - "same refund amount as " - "the previous one, should succeed without changes (1)")); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->increase_refund_for_contract_NT (plugin->cls, - &h_contract_terms, - &merchant_pub, - &little_refund_amount, - "lower refund amount as the previous one, should succeed without changes (1)")); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->increase_refund_for_contract_NT (plugin->cls, - &h_contract_terms, - &merchant_pub, - &right_second_refund_amount, - "right refund increase")); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->increase_refund_for_contract_NT (plugin->cls, - &h_contract_terms, - &merchant_pub, - &too_big_refund_amount, - "make refund testing fail due to too big refund amount")); - - FAILIF (GNUNET_OK != - test_wire_fee ()); - FAILIF (GNUNET_OK != - test_tipping ()); - if (-1 == result) - result = 0; -} - /** * Main function that will be run by the scheduler. @@ -1023,19 +67,12 @@ run (void *cls) return; } - if (0) - test (cfg); /* disabled for now */ - else - result = 0; + result = 0; GNUNET_break (GNUNET_OK == plugin->drop_tables (plugin->cls)); TALER_MERCHANTDB_plugin_unload (plugin); plugin = NULL; - if (NULL != deposit_proof) - json_decref (deposit_proof); - if (NULL != transfer_proof) - json_decref (transfer_proof); } @@ -1049,7 +86,8 @@ main (int argc, struct GNUNET_CONFIGURATION_Handle *cfg; result = -1; - if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) + if (NULL == (plugin_name = strrchr (argv[0], + (int) '-'))) { GNUNET_break (0); return -1; diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index fa255e62..818e7ddd 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -802,7 +802,7 @@ struct TALER_MERCHANTDB_Plugin * @param cls closure * @param instance_id instance's identifier * @param order_id order_id used to lookup. - * @param[out] contract_terms where to store the result + * @param[out] contract_terms where to store the result, NULL to only check for existence * @return transaction status */ enum GNUNET_DB_QueryStatus @@ -813,13 +813,19 @@ struct TALER_MERCHANTDB_Plugin /** - * Store contract terms given its @a order_id + * Store contract terms given its @a order_id. Note that some attributes are + * expected to be calculated inside of the function, like the hash of the + * contract terms (to be hashed), the creation_time and pay_deadline (to be + * obtained from the merchant_orders table). The "session_id" should be + * initially set to the empty string. The "fulfillment_url" and "refund_deadline" + * must be extracted from @a contract_terms. * * @param cls closure * @param instance_id instance's identifier * @param order_id order_id used to store - * @param[out] contract_terms contract to store - * @return transaction status + * @param contract_terms contract to store + * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms + * is malformed */ enum GNUNET_DB_QueryStatus (*insert_contract_terms)(void *cls, @@ -828,6 +834,25 @@ struct TALER_MERCHANTDB_Plugin json_t *contract_terms); + /** + * Delete information about a contract. Note that the transaction must + * enforce that the contract is not awaiting payment anymore AND was not + * paid, or is past the legal expiration. + * + * @param cls closure + * @param instance_id instance to delete order of + * @param order_id order to delete + * @param legal_expiration how long do we need to keep (paid) contracts on + * file for legal reasons (i.e. taxation) + * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + * if locks prevent deletion OR order unknown + */ + enum GNUNET_DB_QueryStatus + (*delete_contract_terms)(void *cls, + const char *instance_id, + const char *order_id, + struct GNUNET_TIME_Relative legal_expiration); + /* ****************** OLD API ******************** */ |