summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-27 15:58:32 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-27 15:58:32 +0200
commite3d91bf74804871c5d7438a0b04265b69feba34a (patch)
treee8f03f09afcca6b645202059a7aade532c09dd59 /src
parent255f162ddb88e932d087574a52d959a7a1d5ba80 (diff)
downloadmerchant-e3d91bf74804871c5d7438a0b04265b69feba34a.tar.gz
merchant-e3d91bf74804871c5d7438a0b04265b69feba34a.tar.bz2
merchant-e3d91bf74804871c5d7438a0b04265b69feba34a.zip
db implementation of claiming
Diffstat (limited to 'src')
-rw-r--r--src/backend/merchant.conf4
-rw-r--r--src/backend/taler-merchant-httpd.c19
-rw-r--r--src/backend/taler-merchant-httpd.h8
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-claim.c14
-rw-r--r--src/backend/taler-merchant-httpd_private-delete-orders-ID.c14
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c638
-rw-r--r--src/backenddb/test_merchantdb.c970
-rw-r--r--src/include/taler_merchantdb_plugin.h33
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 (&timestamp),
- 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 (&timestamp);
- 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 (&timestamp);
-
- 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 ******************** */