diff options
Diffstat (limited to 'src/backenddb/plugin_merchantdb_postgres.c')
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 10787 |
1 files changed, 350 insertions, 10437 deletions
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 823ad819..e5f3f2a1 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014--2023 Taler Systems SA + (C) 2014--2024 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 @@ -20,6 +20,7 @@ * @author Christian Grothoff * @author Marcello Stanisci * @author Priscilla Huang + * @author Iván Ávalos */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> @@ -29,6 +30,117 @@ #include <taler/taler_json_lib.h> #include <taler/taler_mhd_lib.h> #include "taler_merchantdb_plugin.h" +#include "pg_helper.h" +#include "pg_insert_otp.h" +#include "pg_delete_otp.h" +#include "pg_update_otp.h" +#include "pg_select_otp.h" +#include "pg_select_otp_serial.h" +#include "pg_insert_login_token.h" +#include "pg_delete_login_token.h" +#include "pg_select_login_token.h" +#include "pg_insert_account.h" +#include "pg_update_account.h" +#include "pg_lookup_instances.h" +#include "pg_lookup_transfers.h" +#include "pg_lookup_pending_deposits.h" +#include "pg_update_wirewatch_progress.h" +#include "pg_select_wirewatch_accounts.h" +#include "pg_select_open_transfers.h" +#include "pg_delete_exchange_accounts.h" +#include "pg_select_accounts_by_exchange.h" +#include "pg_insert_exchange_account.h" +#include "pg_lookup_instance_auth.h" +#include "pg_lookup_otp_devices.h" +#include "pg_update_transfer_status.h" +#include "pg_insert_instance.h" +#include "pg_account_kyc_set_status.h" +#include "pg_account_kyc_get_status.h" +#include "pg_delete_instance_private_key.h" +#include "pg_purge_instance.h" +#include "pg_update_instance.h" +#include "pg_update_instance_auth.h" +#include "pg_inactivate_account.h" +#include "pg_activate_account.h" +#include "pg_lookup_products.h" +#include "pg_lookup_product.h" +#include "pg_delete_product.h" +#include "pg_insert_product.h" +#include "pg_update_product.h" +#include "pg_lock_product.h" +#include "pg_expire_locks.h" +#include "pg_delete_order.h" +#include "pg_lookup_order.h" +#include "pg_lookup_order_summary.h" +#include "pg_lookup_orders.h" +#include "pg_insert_order.h" +#include "pg_unlock_inventory.h" +#include "pg_insert_order_lock.h" +#include "pg_lookup_contract_terms3.h" +#include "pg_lookup_contract_terms2.h" +#include "pg_lookup_contract_terms.h" +#include "pg_insert_contract_terms.h" +#include "pg_update_contract_terms.h" +#include "pg_delete_contract_terms.h" +#include "pg_delete_template.h" +#include "pg_insert_template.h" +#include "pg_update_template.h" +#include "pg_lookup_templates.h" +#include "pg_lookup_template.h" +#include "pg_lookup_deposits.h" +#include "pg_insert_exchange_signkey.h" +#include "pg_insert_deposit.h" +#include "pg_insert_deposit_confirmation.h" +#include "pg_lookup_refunds.h" +#include "pg_mark_contract_paid.h" +#include "pg_select_account_by_uri.h" +#include "pg_refund_coin.h" +#include "pg_lookup_order_status.h" +#include "pg_lookup_order_status_by_serial.h" +#include "pg_lookup_deposits_by_order.h" +#include "pg_lookup_transfer_details_by_order.h" +#include "pg_mark_order_wired.h" +#include "pg_lookup_refunds_detailed.h" +#include "pg_insert_refund_proof.h" +#include "pg_lookup_refund_proof.h" +#include "pg_lookup_order_by_fulfillment.h" +#include "pg_delete_transfer.h" +#include "pg_check_transfer_exists.h" +#include "pg_lookup_account.h" +#include "pg_lookup_wire_fee.h" +#include "pg_lookup_deposits_by_contract_and_coin.h" +#include "pg_lookup_transfer.h" +#include "pg_lookup_transfer_summary.h" +#include "pg_lookup_transfer_details.h" +#include "pg_lookup_webhooks.h" +#include "pg_lookup_webhook.h" +#include "pg_delete_webhook.h" +#include "pg_insert_webhook.h" +#include "pg_update_webhook.h" +#include "pg_lookup_webhook_by_event.h" +#include "pg_delete_pending_webhook.h" +#include "pg_insert_pending_webhook.h" +#include "pg_update_pending_webhook.h" +#include "pg_lookup_pending_webhooks.h" +#include "pg_update_deposit_confirmation_status.h" +#include "pg_set_transfer_status_to_confirmed.h" +#include "pg_insert_exchange_keys.h" +#include "pg_select_exchange_keys.h" +#include "pg_insert_deposit_to_transfer.h" +#include "pg_increase_refund.h" +#include "pg_select_account.h" +#include "pg_select_accounts.h" +#include "pg_insert_transfer.h" +#include "pg_insert_transfer_details.h" +#include "pg_store_wire_fee_by_exchange.h" +#include "pg_insert_token_family.h" +#include "pg_lookup_token_family.h" +#include "pg_lookup_token_families.h" +#include "pg_delete_token_family.h" +#include "pg_update_token_family.h" +#include "pg_insert_token_family_key.h" +#include "pg_lookup_token_family_key.h" + /** * How often do we re-try if we run into a DB serialization error? @@ -37,76 +149,6 @@ /** - * Wrapper macro to add the currency from the plugin's state - * when fetching amounts from the database. - * - * @param field name of the database field to fetch amount from - * @param[out] amountp pointer to amount to set - */ -#define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \ - TALER_PQ_result_spec_amount ( \ - field,pg->currency,amountp) - -/** - * Wrapper macro to add the currency from the plugin's state - * when fetching amounts from the database. NBO variant. - * - * @param field name of the database field to fetch amount from - * @param[out] amountp pointer to amount to set - */ -#define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, amountp) \ - TALER_PQ_result_spec_amount_nbo ( \ - field,pg->currency,amountp) - - -/** - * Wrapper macro to add the currency from the plugin's state - * when fetching amounts from the database. - * - * @param field name of the database field to fetch amount from - * @param[out] amountp pointer to amount to set - */ -#define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \ - TALER_PQ_result_spec_amount ( \ - field,pg->currency,amountp) - - -/** - * Type of the "cls" argument given to each of the functions in - * our API. - */ -struct PostgresClosure -{ - - /** - * Postgres connection handle. - */ - struct GNUNET_PQ_Context *conn; - - /** - * Which currency do we deal in? - */ - char *currency; - - /** - * Directory with SQL statements to run to create tables. - */ - char *sql_dir; - - /** - * Underlying configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Name of the currently active transaction, NULL if none is active. - */ - const char *transaction_name; - -}; - - -/** * Drop all Taler tables. This should only be used by testcases. * * @param cls the `struct PostgresClosure` with the plugin-specific state @@ -148,6 +190,7 @@ postgres_create_tables (void *cls) GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"), GNUNET_PQ_EXECUTE_STATEMENT_END }; + enum GNUNET_GenericReturnValue ret; conn = GNUNET_PQ_connect_with_cfg (pc->cfg, "merchantdb-postgres", @@ -156,8 +199,10 @@ postgres_create_tables (void *cls) NULL); if (NULL == conn) return GNUNET_SYSERR; + ret = GNUNET_PQ_exec_sql (conn, + "procedures"); GNUNET_PQ_disconnect (conn); - return GNUNET_OK; + return ret; } @@ -224,14 +269,7 @@ postgres_event_notify (void *cls, } -/** - * Do a pre-flight check that we are not in an uncommitted transaction. - * If we are, die. - * Does not return anything, as we will continue regardless of the outcome. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - */ -static void +void postgres_preflight (void *cls) { struct PostgresClosure *pg = cls; @@ -245,14 +283,7 @@ postgres_preflight (void *cls) } -/** - * Check that the database connection is still up - * and automatically reconnects unless we are - * already inside of a transaction. - * - * @param pg connection to check - */ -static void +void check_connection (struct PostgresClosure *pg) { if (NULL != pg->transaction_name) @@ -262,10250 +293,15 @@ check_connection (struct PostgresClosure *pg) /** - * Start a transaction. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param name unique name identifying the transaction (for debugging), - * must point to a constant - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -postgres_start (void *cls, - const char *name) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - check_connection (pg); - postgres_preflight (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting merchant DB transaction `%s'\n", - name); - if (GNUNET_OK != - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - TALER_LOG_ERROR ("Failed to start transaction\n"); - GNUNET_break (0); - return GNUNET_SYSERR; - } - pg->transaction_name = name; - return GNUNET_OK; -} - - -/** - * Start a transaction in 'read committed' mode. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param name unique name identifying the transaction (for debugging), - * must point to a constant - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -postgres_start_read_committed (void *cls, - const char *name) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - check_connection (pg); - postgres_preflight (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Starting merchant DB transaction %s (READ COMMITTED)\n", - name); - if (GNUNET_OK != - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - TALER_LOG_ERROR ("Failed to start transaction\n"); - GNUNET_break (0); - return GNUNET_SYSERR; - } - pg->transaction_name = name; - return GNUNET_OK; -} - - -/** - * Roll back the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - */ -static void -postgres_rollback (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("ROLLBACK"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Rolling back merchant DB transaction `%s'\n", - pg->transaction_name); - GNUNET_break (GNUNET_OK == - GNUNET_PQ_exec_statements (pg->conn, - es)); - pg->transaction_name = NULL; -} - - -/** - * Commit the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return transaction status code - */ -static enum GNUNET_DB_QueryStatus -postgres_commit (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_end - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Committing merchant DB transaction %s\n", - pg->transaction_name); - pg->transaction_name = NULL; - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "end_transaction", - params); -} - - -/** - * Context for lookup_instances(). - */ -struct LookupInstancesContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_InstanceCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Database context. - */ - struct PostgresClosure *pg; - - /** - * Instance settings, valid only during find_instances_cb(). - */ - struct TALER_MERCHANTDB_InstanceSettings is; - - /** - * Instance authentication settings, valid only during find_instances_cb(). - */ - struct TALER_MERCHANTDB_InstanceAuthSettings ias; - - /** - * Instance serial number, valid only during find_instances_cb(). - */ - uint64_t instance_serial; - - /** - * Public key of the current instance, valid only during find_instances_cb(). - */ - struct TALER_MerchantPublicKeyP merchant_pub; - - /** - * Set to the return value on errors. - */ - enum GNUNET_DB_QueryStatus qs; - - /** - * true if we only are interested in instances for which we have the private key. - */ - bool active_only; -}; - - -/** - * We are processing an instances lookup and have the @a accounts. - * Find the private key if possible, and invoke the callback. - * - * @param lic context we are handling - * @param num_accounts length of @a accounts array - * @param accounts information about accounts of the instance in @a lic - */ -static void -call_with_accounts (struct LookupInstancesContext *lic, - unsigned int num_accounts, - const struct TALER_MERCHANTDB_AccountDetails accounts[]) -{ - struct PostgresClosure *pg = lic->pg; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&lic->instance_serial), - GNUNET_PQ_query_param_end - }; - struct TALER_MerchantPrivateKeyP merchant_priv; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", - &merchant_priv), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_instance_private_key", - params, - rs); - if (qs < 0) - { - GNUNET_break (0); - lic->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - if ( (0 == qs) && - (lic->active_only) ) - return; /* skip, not interesting */ - lic->cb (lic->cb_cls, - &lic->merchant_pub, - (0 == qs) ? NULL : &merchant_priv, - &lic->is, - &lic->ias, - num_accounts, - accounts); -} - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param cls of type `struct FindInstancesContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_accounts_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupInstancesContext *lic = cls; - char *paytos[num_results]; - struct TALER_MERCHANTDB_AccountDetails accounts[num_results]; - - for (unsigned int i = 0; i < num_results; i++) - { - uint8_t active; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &accounts[i].h_wire), - GNUNET_PQ_result_spec_auto_from_type ("salt", - &accounts[i].salt), - GNUNET_PQ_result_spec_string ("payto_uri", - &paytos[i]), - GNUNET_PQ_result_spec_auto_from_type ("active", - &active), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lic->qs = GNUNET_DB_STATUS_HARD_ERROR; - for (unsigned int j = 0; j < i; j++) - GNUNET_free (paytos[j]); - return; - } - accounts[i].active = (0 != active); - accounts[i].payto_uri = paytos[i]; - } - call_with_accounts (lic, - num_results, - accounts); - for (unsigned int i = 0; i < num_results; i++) - GNUNET_free (paytos[i]); -} - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about instances. - * - * @param cls of type `struct FindInstancesContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_instances_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupInstancesContext *lic = cls; - struct PostgresClosure *pg = lic->pg; - - for (unsigned int i = 0; i < num_results; i++) - { - bool no_auth; - bool no_salt; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("merchant_serial", - &lic->instance_serial), - GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", - &lic->merchant_pub), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("auth_hash", - &lic->ias.auth_hash), - &no_auth), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("auth_salt", - &lic->ias.auth_salt), - &no_salt), - GNUNET_PQ_result_spec_string ("merchant_id", - &lic->is.id), - GNUNET_PQ_result_spec_string ("merchant_name", - &lic->is.name), - TALER_PQ_result_spec_json ("address", - &lic->is.address), - TALER_PQ_result_spec_json ("jurisdiction", - &lic->is.jurisdiction), - TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_deposit_fee", - &lic->is.default_max_deposit_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_wire_fee", - &lic->is.default_max_wire_fee), - GNUNET_PQ_result_spec_uint32 ("default_wire_fee_amortization", - &lic->is.default_wire_fee_amortization), - GNUNET_PQ_result_spec_relative_time ("default_wire_transfer_delay", - &lic->is.default_wire_transfer_delay), - GNUNET_PQ_result_spec_relative_time ("default_pay_delay", - &lic->is.default_pay_delay), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("website", - &lic->is.website), - NULL), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("email", - &lic->is.email), - NULL), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("logo", - &lic->is.logo), - NULL), - GNUNET_PQ_result_spec_end - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&lic->instance_serial), - GNUNET_PQ_query_param_end - }; - - memset (&lic->ias.auth_salt, - 0, - sizeof (lic->ias.auth_salt)); - memset (&lic->ias.auth_hash, - 0, - sizeof (lic->ias.auth_hash)); - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lic->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - lic->qs = GNUNET_PQ_eval_prepared_multi_select (lic->pg->conn, - "lookup_accounts", - params, - &lookup_accounts_cb, - lic); - if (0 > lic->qs) - { - /* lookup_accounts_cb() did not run, still notify about the - account-less instance! */ - call_with_accounts (lic, - 0, - NULL); - } - GNUNET_PQ_cleanup_result (rs); - if (0 > lic->qs) - break; - } -} - - -/** - * Lookup all of the instances this backend has configured. - * - * @param cls closure - * @param active_only only find 'active' instances - * @param cb function to call on all instances found - * @param cb_cls closure for @a cb - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_instances (void *cls, - bool active_only, - TALER_MERCHANTDB_InstanceCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupInstancesContext lic = { - .cb = cb, - .cb_cls = cb_cls, - .active_only = active_only, - .pg = pg - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_instances", - params, - &lookup_instances_cb, - &lic); - if (0 > lic.qs) - return lic.qs; - return qs; -} - - -/** - * Lookup all one of the instances this backend has configured. - * - * @param cls closure - * @param id instance ID to resolve - * @param active_only only find 'active' instances - * @param cb function to call on all instances found - * @param cb_cls closure for @a cb - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_instance (void *cls, - const char *id, - bool active_only, - TALER_MERCHANTDB_InstanceCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupInstancesContext lic = { - .cb = cb, - .cb_cls = cb_cls, - .active_only = active_only, - .pg = pg - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_instance", - params, - &lookup_instances_cb, - &lic); - if (0 > lic.qs) - return lic.qs; - return qs; -} - - -/** - * Lookup authentication data of an instance. - * - * @param cls closure - * @param instance_id instance to query - * @param[out] ias where to store the auth data - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_instance_auth ( - void *cls, - const char *instance_id, - struct TALER_MERCHANTDB_InstanceAuthSettings *ias) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("auth_hash", - &ias->auth_hash), - GNUNET_PQ_result_spec_auto_from_type ("auth_salt", - &ias->auth_salt), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_instance_auth", - params, - rs); -} - - -/** - * Insert information about an instance into our database. - * - * @param cls closure - * @param merchant_pub public key of the instance - * @param merchant_priv private key of the instance - * @param is details about the instance - * @param ias authentication settings for the instance - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_instance ( - void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct TALER_MerchantPrivateKeyP *merchant_priv, - const struct TALER_MERCHANTDB_InstanceSettings *is, - const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_auto_from_type (&ias->auth_hash), - GNUNET_PQ_query_param_auto_from_type (&ias->auth_salt), - GNUNET_PQ_query_param_string (is->id), - GNUNET_PQ_query_param_string (is->name), - TALER_PQ_query_param_json (is->address), - TALER_PQ_query_param_json (is->jurisdiction), - TALER_PQ_query_param_amount (&is->default_max_deposit_fee), - TALER_PQ_query_param_amount (&is->default_max_wire_fee), - GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization), - GNUNET_PQ_query_param_relative_time ( - &is->default_wire_transfer_delay), - GNUNET_PQ_query_param_relative_time (&is->default_pay_delay), - (NULL == is->website) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (is->website), - (NULL == is->email) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (is->email), - (NULL == is->logo) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (is->logo), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_QueryParam params_priv[] = { - GNUNET_PQ_query_param_auto_from_type (merchant_priv), - GNUNET_PQ_query_param_string (is->id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_instance", - params); - if (qs <= 0) - return qs; - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_keys", - params_priv); -} - - -/** - * Insert information about an instance's account into our database. - * - * @param cls closure - * @param id identifier of the instance - * @param account_details details about the account - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_account ( - void *cls, - const char *id, - const struct TALER_MERCHANTDB_AccountDetails *account_details) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (id), - GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire), - GNUNET_PQ_query_param_auto_from_type (&account_details->salt), - GNUNET_PQ_query_param_string (account_details->payto_uri), - GNUNET_PQ_query_param_bool (account_details->active), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_account", - params); -} - - -/** - * Closure for kyc_status_cb(). - */ -struct KycStatusContext -{ - /** - * Function to call with results. - */ - TALER_MERCHANTDB_KycCallback kyc_cb; - - /** - * Closure for @e kyc_cb. - */ - void *kyc_cb_cls; - - /** - * Filter, NULL to not filter. - */ - const struct TALER_MerchantWireHashP *h_wire; - - /** - * Filter, NULL to not filter. - */ - const char *exchange_url; - - /** - * Number of results found. - */ - unsigned int count; - - /** - * Set to true on failure(s). - */ - bool failure; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param[in,out] cls of type `struct KycStatusContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -kyc_status_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct KycStatusContext *ksc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_MerchantWireHashP h_wire; - uint64_t kyc_serial; - char *exchange_url; - char *payto_uri; - struct GNUNET_TIME_Timestamp last_check; - uint8_t kyc_ok; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &h_wire), - GNUNET_PQ_result_spec_uint64 ("exchange_kyc_serial", - &kyc_serial), - GNUNET_PQ_result_spec_string ("payto_uri", - &payto_uri), - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - GNUNET_PQ_result_spec_timestamp ("kyc_timestamp", - &last_check), - GNUNET_PQ_result_spec_auto_from_type ("kyc_ok", - &kyc_ok), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ksc->failure = true; - return; - } - if ( (NULL != ksc->exchange_url) && - (0 != strcmp (ksc->exchange_url, - exchange_url)) ) - { - GNUNET_PQ_cleanup_result (rs); - continue; - } - if ( (NULL != ksc->h_wire) && - (0 != GNUNET_memcmp (ksc->h_wire, - &h_wire)) ) - { - GNUNET_PQ_cleanup_result (rs); - continue; - } - ksc->count++; - ksc->kyc_cb (ksc->kyc_cb_cls, - &h_wire, - kyc_serial, - payto_uri, - exchange_url, - last_check, - 0 != kyc_ok); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Check an instance's account's KYC status. - * - * @param cls closure - * @param merchant_id merchant backend instance ID - * @param h_wire hash of the wire account to check, - * NULL to check all accounts of the merchant - * @param exchange_url base URL of the exchange to check, - * NULL to check all exchanges - * @param kyc_cb KYC status callback to invoke - * @param kyc_cb_cls closure for @a kyc_cb - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_account_kyc_get_status (void *cls, - const char *merchant_id, - const struct TALER_MerchantWireHashP *h_wire, - const char *exchange_url, - TALER_MERCHANTDB_KycCallback kyc_cb, - void *kyc_cb_cls) -{ - struct PostgresClosure *pg = cls; - struct KycStatusContext ksc = { - .kyc_cb = kyc_cb, - .kyc_cb_cls = kyc_cb_cls, - .exchange_url = exchange_url, - .h_wire = h_wire - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_kyc_status", - params, - &kyc_status_cb, - &ksc); - if (ksc.failure) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (0 > qs) - return qs; - return ksc.count; -} - - -/** - * Update an instance's account's KYC status. - * - * @param cls closure - * @param merchant_id merchant backend instance ID - * @param h_wire hash of the wire account to check - * @param exchange_url base URL of the exchange to check - * @param exchange_kyc_serial serial number for our account at the exchange (0 if unknown) - * @param exchange_sig signature of the exchange, or NULL for none - * @param exchange_pub public key of the exchange, or NULL for none - * @param timestamp timestamp to store - * @param kyc_ok current KYC status (true for satisfied) - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_account_kyc_set_status ( - void *cls, - const char *merchant_id, - const struct TALER_MerchantWireHashP *h_wire, - const char *exchange_url, - uint64_t exchange_kyc_serial, - const struct TALER_ExchangeSignatureP *exchange_sig, - const struct TALER_ExchangePublicKeyP *exchange_pub, - struct GNUNET_TIME_Timestamp timestamp, - bool kyc_ok) -{ - struct PostgresClosure *pg = cls; - uint8_t ok = kyc_ok; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_auto_from_type (h_wire), - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_uint64 (&exchange_kyc_serial), - GNUNET_PQ_query_param_timestamp (×tamp), - GNUNET_PQ_query_param_auto_from_type (&ok), - exchange_pub - ? GNUNET_PQ_query_param_auto_from_type (exchange_pub) - : GNUNET_PQ_query_param_null (), - exchange_sig - ? GNUNET_PQ_query_param_auto_from_type (exchange_sig) - : GNUNET_PQ_query_param_null (), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "upsert_account_kyc", - params); -} - - -/** - * Delete private key of an instance from our database. - * - * @param cls closure - * @param merchant_id identifier of the instance - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_instance_private_key ( - void *cls, - const char *merchant_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_key", - params); -} - - -/** - * Purge an instance and all associated information from our database. - * Highly likely to cause undesired data loss. Use with caution. - * - * @param cls closure - * @param merchant_id identifier of the instance - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_purge_instance (void *cls, - const char *merchant_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "purge_instance", - params); -} - - -/** - * Update information about an instance into our database. - * - * @param cls closure - * @param is details about the instance - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_update_instance (void *cls, - const struct TALER_MERCHANTDB_InstanceSettings *is) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (is->id), - GNUNET_PQ_query_param_string (is->name), - TALER_PQ_query_param_json (is->address), - TALER_PQ_query_param_json (is->jurisdiction), - TALER_PQ_query_param_amount (&is->default_max_deposit_fee), - TALER_PQ_query_param_amount (&is->default_max_wire_fee), - GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization), - GNUNET_PQ_query_param_relative_time ( - &is->default_wire_transfer_delay), - GNUNET_PQ_query_param_relative_time (&is->default_pay_delay), - (NULL == is->website) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (is->website), - (NULL == is->email) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (is->email), - (NULL == is->logo) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (is->logo), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_instance", - params); -} - - -/** - * Update information about an instance's authentication settings - * into our database. - * - * @param cls closure - * @param merchant_id identity of the instance - * @param is authentication details about the instance - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_update_instance_auth ( - void *cls, - const char *merchant_id, - const struct TALER_MERCHANTDB_InstanceAuthSettings *is) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_auto_from_type (&is->auth_hash), - GNUNET_PQ_query_param_auto_from_type (&is->auth_salt), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_instance_auth", - params); -} - - -/** - * Set an instance's account in our database to "inactive". - * - * @param cls closure - * @param merchant_id merchant backend instance ID - * @param h_wire hash of the wire account to set to inactive - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_inactivate_account (void *cls, - const char *merchant_id, - const struct TALER_MerchantWireHashP *h_wire) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_auto_from_type (h_wire), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "inactivate_account", - params); -} - - -/** - * Set an instance's account in our database to "active". - * - * @param cls closure - * @param merchant_id merchant backend instance ID - * @param h_wire hash of the wire account to set to active - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_activate_account (void *cls, - const char *merchant_id, - const struct TALER_MerchantWireHashP *h_wire) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (merchant_id), - GNUNET_PQ_query_param_auto_from_type (h_wire), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "activate_account", - params); -} - - -/** - * Context used for postgres_lookup_products(). - */ -struct LookupProductsContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_ProductsCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about products. - * - * @param[in,out] cls of type `struct LookupProductsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_products_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupProductsContext *plc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - char *product_id; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("product_id", - &product_id), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - plc->extract_failed = true; - return; - } - plc->cb (plc->cb_cls, - product_id); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup all of the products the given instance has configured. - * - * @param cls closure - * @param instance_id instance to lookup products for - * @param cb function to call on all products found - * @param cb_cls closure for @a cb - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_products (void *cls, - const char *instance_id, - TALER_MERCHANTDB_ProductsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupProductsContext plc = { - .cb = cb, - .cb_cls = cb_cls, - /* Can be overwritten by the lookup_products_cb */ - .extract_failed = false, - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_products", - params, - &lookup_products_cb, - &plc); - /* If there was an error inside lookup_products_cb, return a hard error. */ - if (plc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Lookup details about a particular product. - * - * @param cls closure - * @param instance_id instance to lookup products for - * @param product_id product to lookup - * @param[out] pd set to the product details on success, can be NULL - * (in that case we only want to check if the product exists) - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_product (void *cls, - const char *instance_id, - const char *product_id, - struct TALER_MERCHANTDB_ProductDetails *pd) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (product_id), - GNUNET_PQ_query_param_end - }; - - if (NULL == pd) - { - struct GNUNET_PQ_ResultSpec rs_null[] = { - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_product", - params, - rs_null); - } - else - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("description", - &pd->description), - TALER_PQ_result_spec_json ("description_i18n", - &pd->description_i18n), - GNUNET_PQ_result_spec_string ("unit", - &pd->unit), - TALER_PQ_RESULT_SPEC_AMOUNT ("price", - &pd->price), - TALER_PQ_result_spec_json ("taxes", - &pd->taxes), - GNUNET_PQ_result_spec_uint64 ("total_stock", - &pd->total_stock), - GNUNET_PQ_result_spec_uint64 ("total_sold", - &pd->total_sold), - GNUNET_PQ_result_spec_uint64 ("total_lost", - &pd->total_lost), - GNUNET_PQ_result_spec_string ("image", - &pd->image), - TALER_PQ_result_spec_json ("address", - &pd->address), - GNUNET_PQ_result_spec_timestamp ("next_restock", - &pd->next_restock), - GNUNET_PQ_result_spec_uint32 ("minimum_age", - &pd->minimum_age), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_product", - params, - rs); - } -} - - -/** - * Delete information about a product. Note that the transaction must - * enforce that no stocks are currently locked. - * - * @param cls closure - * @param instance_id instance to delete product of - * @param product_id product to delete - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS - * if locks prevent deletion OR product unknown - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_product (void *cls, - const char *instance_id, - const char *product_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (product_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_product", - params); -} - - -/** - * Insert details about a particular product. - * - * @param cls closure - * @param instance_id instance to insert product for - * @param product_id product identifier of product to insert - * @param pd the product details to insert - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_product (void *cls, - const char *instance_id, - const char *product_id, - const struct TALER_MERCHANTDB_ProductDetails *pd) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (product_id), - GNUNET_PQ_query_param_string (pd->description), - TALER_PQ_query_param_json (pd->description_i18n), - GNUNET_PQ_query_param_string (pd->unit), - GNUNET_PQ_query_param_string (pd->image), - TALER_PQ_query_param_json (pd->taxes), - TALER_PQ_query_param_amount (&pd->price), - GNUNET_PQ_query_param_uint64 (&pd->total_stock), - TALER_PQ_query_param_json (pd->address), - GNUNET_PQ_query_param_timestamp (&pd->next_restock), - GNUNET_PQ_query_param_uint32 (&pd->minimum_age), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_product", - params); -} - - -/** - * Update details about a particular product. Note that the - * transaction must enforce that the sold/stocked/lost counters - * are not reduced (i.e. by expanding the WHERE clause on the existing - * values). - * - * @param cls closure - * @param instance_id instance to lookup products for - * @param product_id product to lookup - * @param[out] pd set to the product details on success, can be NULL - * (in that case we only want to check if the product exists) - * total_sold in @a pd is ignored, total_lost must not - * exceed total_stock minus the existing total_sold; - * total_sold and total_stock must be larger or equal to - * the existing value; - * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the - * non-decreasing constraints are not met *or* if the product - * does not yet exist. - */ -static enum GNUNET_DB_QueryStatus -postgres_update_product (void *cls, - const char *instance_id, - const char *product_id, - const struct TALER_MERCHANTDB_ProductDetails *pd) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), /* $1 */ - GNUNET_PQ_query_param_string (product_id), - GNUNET_PQ_query_param_string (pd->description), - TALER_PQ_query_param_json (pd->description_i18n), - GNUNET_PQ_query_param_string (pd->unit), - GNUNET_PQ_query_param_string (pd->image), /* $6 */ - TALER_PQ_query_param_json (pd->taxes), - TALER_PQ_query_param_amount (&pd->price), /* $8+$9 */ - GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $10 */ - GNUNET_PQ_query_param_uint64 (&pd->total_lost), - TALER_PQ_query_param_json (pd->address), - GNUNET_PQ_query_param_timestamp (&pd->next_restock), - GNUNET_PQ_query_param_uint32 (&pd->minimum_age), - GNUNET_PQ_query_param_end - }; - - if ( (pd->total_stock < pd->total_lost + pd->total_sold) || - (pd->total_lost < pd->total_lost - + pd->total_sold) /* integer overflow */) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_product", - params); -} - - -/** - * Lock stocks of a particular product. Note that the transaction must - * enforce that the "stocked-sold-lost >= locked" constraint holds. - * - * @param cls closure - * @param instance_id instance to lookup products for - * @param product_id product to lookup - * @param uuid the UUID that holds the lock - * @param quantity how many units should be locked - * @param expiration_time when should the lock expire - * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the - * product is unknown OR if there insufficient stocks remaining - */ -static enum GNUNET_DB_QueryStatus -postgres_lock_product (void *cls, - const char *instance_id, - const char *product_id, - const struct GNUNET_Uuid *uuid, - uint64_t quantity, - struct GNUNET_TIME_Timestamp expiration_time) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (product_id), - GNUNET_PQ_query_param_auto_from_type (uuid), - GNUNET_PQ_query_param_uint64 (&quantity), - GNUNET_PQ_query_param_timestamp (&expiration_time), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "lock_product", - params); -} - - -/** - * Release all expired product locks, including - * those from expired offers -- across all - * instances. - * - * @param cls closure - */ -static void -postgres_expire_locks (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_absolute_time (&now), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs1; - enum GNUNET_DB_QueryStatus qs2; - enum GNUNET_DB_QueryStatus qs3; - - check_connection (pg); - qs1 = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "unlock_products", - params); - if (qs1 < 0) - { - GNUNET_break (0); - return; - } - qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "unlock_orders", - params); - if (qs2 < 0) - { - GNUNET_break (0); - return; - } - qs3 = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "unlock_contracts", - params); - if (qs3 < 0) - { - GNUNET_break (0); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Released %d+%d+%d locks\n", - qs1, - qs2, - qs3); -} - - -/** - * Delete information about an order. Note that the transaction must - * enforce that the order is not awaiting payment anymore. - * - * @param cls closure - * @param instance_id instance to delete order of - * @param order_id order to delete - * @param force delete claimed but unpaid orders as well - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS - * if pending payment prevents deletion OR order unknown - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_order (void *cls, - const char *instance_id, - const char *order_id, - bool force) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_absolute_time (&now), - GNUNET_PQ_query_param_bool (force), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_QueryParam params2[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_order", - params); - if ( (qs <= 0) || (! force)) - return qs; - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_contract", - params2); -} - - -/** - * Retrieve order given its @a order_id and the @a instance_id. - * - * @param cls closure - * @param instance_id instance to obtain order of - * @param order_id order id used to perform the lookup - * @param[out] claim_token the claim token generated for the order, - * NULL to only test if the order exists - * @param[out] h_post_data set to the hash of the POST data that created the order - * @param[out] contract_terms where to store the retrieved contract terms, - * NULL to only test if the order exists - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_order (void *cls, - const char *instance_id, - const char *order_id, - struct TALER_ClaimTokenP *claim_token, - struct TALER_MerchantPostDataHashP *h_post_data, - json_t **contract_terms) -{ - struct PostgresClosure *pg = cls; - json_t *j; - struct TALER_ClaimTokenP ct; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_json ("contract_terms", - &j), - GNUNET_PQ_result_spec_auto_from_type ("claim_token", - &ct), - GNUNET_PQ_result_spec_auto_from_type ("h_post_data", - h_post_data), - GNUNET_PQ_result_spec_end - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finding contract term, order_id: '%s', instance_id: '%s'.\n", - order_id, - instance_id); - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_order", - params, - rs); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - if (NULL != contract_terms) - *contract_terms = j; - else - json_decref (j); - if (NULL != claim_token) - *claim_token = ct; - } - else - { - /* just to be safe: NULL it */ - if (NULL != contract_terms) - *contract_terms = NULL; - if (NULL != claim_token) - *claim_token = (struct TALER_ClaimTokenP) { 0 } - ; - } - return qs; -} - - -/** - * Retrieve order summary given its @a order_id and the @a instance_id. - * - * @param cls closure - * @param instance_id instance to obtain order of - * @param order_id order id used to perform the lookup - * @param[out] timestamp when was the order created - * @param[out] order_serial under which serial do we keep this order - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_order_summary (void *cls, - const char *instance_id, - const char *order_id, - struct GNUNET_TIME_Timestamp *timestamp, - uint64_t *order_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("order_serial", - order_serial), - GNUNET_PQ_result_spec_timestamp ("creation_time", - timestamp), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_order_summary", - params, - rs); -} - - -/** - * Context used for postgres_lookup_orders(). - */ -struct LookupOrdersContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_OrdersCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about orders. - * - * @param[in,out] cls of type `struct LookupOrdersContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_orders_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupOrdersContext *plc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - char *order_id; - uint64_t order_serial; - struct GNUNET_TIME_Timestamp ts; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("order_id", - &order_id), - GNUNET_PQ_result_spec_uint64 ("order_serial", - &order_serial), - GNUNET_PQ_result_spec_timestamp ("creation_time", - &ts), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - plc->extract_failed = true; - return; - } - plc->cb (plc->cb_cls, - order_id, - order_serial, - ts); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Retrieve orders given the @a instance_id. - * - * @param cls closure - * @param instance_id instance to obtain order of - * @param of filter to apply when looking up orders - * @param cb callback to pass all the orders that are found - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_orders (void *cls, - const char *instance_id, - const struct TALER_MERCHANTDB_OrderFilter *of, - TALER_MERCHANTDB_OrdersCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupOrdersContext plc = { - .cb = cb, - .cb_cls = cb_cls - }; - uint64_t limit = (of->delta > 0) ? of->delta : -of->delta; - uint8_t paid; - uint8_t refunded; - uint8_t wired; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&limit), - GNUNET_PQ_query_param_uint64 (&of->start_row), - GNUNET_PQ_query_param_timestamp (&of->date), - GNUNET_PQ_query_param_auto_from_type (&paid), - GNUNET_PQ_query_param_auto_from_type (&refunded), - GNUNET_PQ_query_param_auto_from_type (&wired), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - char stmt[128]; - - paid = (TALER_EXCHANGE_YNA_YES == of->paid); - refunded = (TALER_EXCHANGE_YNA_YES == of->refunded); - wired = (TALER_EXCHANGE_YNA_YES == of->wired); - /* painfully many cases..., note that "_xxx" being present in 'stmt' merely - means that we filter by that variable, the value we filter for is - computed above */ - GNUNET_snprintf (stmt, - sizeof (stmt), - "lookup_orders_%s%s%s%s", - (of->delta > 0) ? "inc" : "dec", - (TALER_EXCHANGE_YNA_ALL == of->paid) ? "" : "_paid", - (TALER_EXCHANGE_YNA_ALL == of->refunded) ? "" : - "_refunded", - (TALER_EXCHANGE_YNA_ALL == of->wired) ? "" : "_wired"); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, - params, - &lookup_orders_cb, - &plc); - if (plc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Insert order into the DB. - * - * @param cls closure - * @param instance_id identifies the instance responsible for the order - * @param order_id alphanumeric string that uniquely identifies the proposal - * @param h_post_data hash of the POST data for idempotency checks - * @param pay_deadline how long does the customer have to pay for the order - * @param claim_token token to use for access control - * @param contract_terms proposal data to store - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_order (void *cls, - const char *instance_id, - const char *order_id, - const struct TALER_MerchantPostDataHashP *h_post_data, - struct GNUNET_TIME_Timestamp pay_deadline, - const struct TALER_ClaimTokenP *claim_token, - const json_t *contract_terms) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp now; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_timestamp (&pay_deadline), - GNUNET_PQ_query_param_auto_from_type (claim_token), - GNUNET_PQ_query_param_auto_from_type (h_post_data), - GNUNET_PQ_query_param_timestamp (&now), - TALER_PQ_query_param_json (contract_terms), - GNUNET_PQ_query_param_end - }; - - now = GNUNET_TIME_timestamp_get (); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "inserting order: order_id: %s, instance_id: %s.\n", - order_id, - instance_id); - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_order", - params); -} - - -/** - * Release an inventory lock by UUID. Releases ALL stocks locked under - * the given UUID. - * - * @param cls closure - * @param uuid the UUID to release locks for - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are no locks under @a uuid - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success - */ -static enum GNUNET_DB_QueryStatus -postgres_unlock_inventory (void *cls, - const struct GNUNET_Uuid *uuid) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (uuid), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "unlock_inventory", - params); -} - - -/** - * Lock inventory stock to a particular order. - * - * @param cls closure - * @param instance_id identifies the instance responsible for the order - * @param order_id alphanumeric string that uniquely identifies the order - * @param product_id uniquely identifies the product to be locked - * @param quantity how many units should be locked to the @a order_id - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are insufficient stocks - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_order_lock (void *cls, - const char *instance_id, - const char *order_id, - const char *product_id, - uint64_t quantity) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_string (product_id), - GNUNET_PQ_query_param_uint64 (&quantity), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_order_lock", - params); -} - - -/** - * Retrieve contract terms given its @a order_id - * - * @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, NULL to only check for existence - * @param[out] order_serial set to the order's serial number - * @param[out] paid set to true if the order is fully paid - * @param[out] claim_token set to the claim token, NULL to only check for existence - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_contract_terms (void *cls, - const char *instance_id, - const char *order_id, - json_t **contract_terms, - uint64_t *order_serial, - bool *paid, - struct TALER_ClaimTokenP *claim_token) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - struct TALER_ClaimTokenP ct; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - /* contract_terms must be first! */ - TALER_PQ_result_spec_json ("contract_terms", - contract_terms), - GNUNET_PQ_result_spec_uint64 ("order_serial", - order_serial), - GNUNET_PQ_result_spec_bool ("paid", - paid), - GNUNET_PQ_result_spec_auto_from_type ("claim_token", - &ct), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_contract_terms", - params, - (NULL != contract_terms) - ? rs - : &rs[1]); - if (NULL != claim_token) - *claim_token = ct; - return qs; -} - - -/** - * 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 contract_terms contract terms to store - * @param[out] order_serial set to the serial of the order - * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms - * is malformed - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_contract_terms (void *cls, - const char *instance_id, - const char *order_id, - json_t *contract_terms, - uint64_t *order_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp pay_deadline; - struct GNUNET_TIME_Timestamp refund_deadline; - const char *fulfillment_url; - struct TALER_PrivateContractHashP h_contract_terms; - - if (GNUNET_OK != - TALER_JSON_contract_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("pay_deadline", - &pay_deadline), - GNUNET_JSON_spec_timestamp ("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; - } - } - - fulfillment_url = - json_string_value (json_object_get (contract_terms, - "fulfillment_url")); - 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_timestamp (&pay_deadline), - GNUNET_PQ_query_param_timestamp (&refund_deadline), - (NULL == fulfillment_url) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (fulfillment_url), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("order_serial", - order_serial), - GNUNET_PQ_result_spec_end - }; - - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "insert_contract_terms", - params, - rs); - } -} - - -/** - * Update the contract terms stored for @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 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_update_contract_terms (void *cls, - const char *instance_id, - const char *order_id, - json_t *contract_terms) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp pay_deadline; - struct GNUNET_TIME_Timestamp refund_deadline; - const char *fulfillment_url = NULL; - struct TALER_PrivateContractHashP h_contract_terms; - - if (GNUNET_OK != - TALER_JSON_contract_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("pay_deadline", - &pay_deadline), - GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("fulfillment_url", - &fulfillment_url), - NULL), - 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_timestamp (&pay_deadline), - GNUNET_PQ_query_param_timestamp (&refund_deadline), - (NULL == fulfillment_url) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (fulfillment_url), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_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_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 - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_contract_terms", - params); -} - - -/** - * Closure for #lookup_deposits_cb(). - */ -struct LookupDepositsContext -{ - /** - * Function to call with results. - */ - TALER_MERCHANTDB_DepositsCallback cb; - - /** - * Closure for @e cls. - */ - void *cb_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Transaction status (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[in,out] cls of type `struct LookupDepositsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_deposits_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupDepositsContext *ldc = cls; - struct PostgresClosure *pg = ldc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_Amount amount_with_fee; - struct TALER_Amount deposit_fee; - struct TALER_Amount refund_fee; - struct TALER_Amount wire_fee; - char *exchange_url; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &coin_pub), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &amount_with_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee", - &deposit_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee", - &refund_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", - &wire_fee), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ldc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - ldc->cb (ldc->cb_cls, - exchange_url, - &coin_pub, - &amount_with_fee, - &deposit_fee, - &refund_fee, - &wire_fee); - GNUNET_PQ_cleanup_result (rs); - } - ldc->qs = num_results; -} - - -/** - * Lookup information about coins that were successfully deposited for a - * given contract. - * - * @param cls closure - * @param instance_id instance to lookup deposits for - * @param h_contract_terms proposal data's hashcode - * @param cb function to call with payment data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_deposits ( - void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - TALER_MERCHANTDB_DepositsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_end - }; - struct LookupDepositsContext ldc = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg - }; - enum GNUNET_DB_QueryStatus qs; - - /* no preflight check here, run in its own transaction by the caller! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finding deposits for h_contract_terms '%s'\n", - GNUNET_h2s (&h_contract_terms->hash)); - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_deposits", - params, - &lookup_deposits_cb, - &ldc); - if (qs <= 0) - return qs; - return ldc.qs; -} - - -/** - * Insert an exchange signing key into our database. - * - * @param cls closure - * @param master_pub exchange master public key used for @a master_sig - * @param exchange_pub exchange signing key to insert - * @param start_date when does the signing key become valid - * @param expire_date when does the signing key stop being used - * @param end_date when does the signing key become void as proof - * @param master_sig signature of @a master_pub over the @a exchange_pub and the dates - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_exchange_signkey ( - void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_ExchangePublicKeyP *exchange_pub, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp expire_date, - struct GNUNET_TIME_Timestamp end_date, - const struct TALER_MasterSignatureP *master_sig) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (master_pub), - GNUNET_PQ_query_param_auto_from_type (exchange_pub), - GNUNET_PQ_query_param_timestamp (&start_date), - GNUNET_PQ_query_param_timestamp (&expire_date), - GNUNET_PQ_query_param_timestamp (&end_date), - GNUNET_PQ_query_param_auto_from_type (master_sig), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - postgres_preflight (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_exchange_signkey", - params); - -} - - -/** - * Insert payment confirmation from the exchange into the database. - * - * @param cls closure - * @param instance_id instance to lookup deposits for - * @param deposit_timestamp time when the exchange generated the deposit confirmation - * @param h_contract_terms proposal data's hashcode - * @param coin_pub public key of the coin - * @param exchange_url URL of the exchange that issued @a coin_pub - * @param amount_with_fee amount the exchange will deposit for this coin - * @param deposit_fee fee the exchange will charge for this coin - * @param wire_fee wire fee the exchange charges - * @param refund_fee fee the exchange charges to refund this coin - * @param h_wire hash of the wire details of the target account of the merchant - * @param exchange_sig signature from exchange that coin was accepted - * @param exchange_pub signgin key that was used for @a exchange_sig - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_deposit ( - void *cls, - const char *instance_id, - struct GNUNET_TIME_Timestamp deposit_timestamp, - const struct TALER_PrivateContractHashP *h_contract_terms, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const char *exchange_url, - const struct TALER_Amount *amount_with_fee, - const struct TALER_Amount *deposit_fee, - const struct TALER_Amount *refund_fee, - const struct TALER_Amount *wire_fee, - const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExchangeSignatureP *exchange_sig, - const struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_timestamp (&deposit_timestamp), /* $3 */ - GNUNET_PQ_query_param_auto_from_type (coin_pub), - GNUNET_PQ_query_param_string (exchange_url), - TALER_PQ_query_param_amount (amount_with_fee), /* $6/$7 */ - TALER_PQ_query_param_amount (deposit_fee), /* $8, $9 */ - TALER_PQ_query_param_amount (refund_fee), /* $10, $11 */ - TALER_PQ_query_param_amount (wire_fee), /* $12, $13 */ - GNUNET_PQ_query_param_auto_from_type (h_wire), /* $14 */ - GNUNET_PQ_query_param_auto_from_type (exchange_sig), /* $15 */ - GNUNET_PQ_query_param_auto_from_type (exchange_pub), /* $16 */ - GNUNET_PQ_query_param_end - }; - - /* no preflight check here, run in transaction by caller! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing deposit for instance `%s' h_contract_terms `%s', coin_pub: `%s', amount_with_fee: %s\n", - instance_id, - GNUNET_h2s (&h_contract_terms->hash), - TALER_B2S (coin_pub), - TALER_amount2s (amount_with_fee)); - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_deposit", - params); - -} - - -/** - * Closure for #lookup_refunds_cb(). - */ -struct LookupRefundsContext -{ - /** - * Function to call for each refund. - */ - TALER_MERCHANTDB_RefundCallback rc; - - /** - * Closure for @e rc. - */ - void *rc_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Transaction result. - */ - 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 LookupRefundsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_refunds_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupRefundsContext *lrc = cls; - struct PostgresClosure *pg = lrc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_Amount refund_amount; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &coin_pub), - TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount", - &refund_amount), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lrc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - lrc->rc (lrc->rc_cls, - &coin_pub, - &refund_amount); - GNUNET_PQ_cleanup_result (rs); /* technically useless here */ - } - lrc->qs = num_results; -} - - -/** - * Obtain refunds associated with a contract. - * - * @param cls closure, typically a connection to the db - * @param instance_id instance to lookup refunds for - * @param h_contract_terms hash code of the contract - * @param rc function to call for each coin on which there is a refund - * @param rc_cls closure for @a rc - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_refunds ( - void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - TALER_MERCHANTDB_RefundCallback rc, - void *rc_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_end - }; - struct LookupRefundsContext lrc = { - .rc = rc, - .rc_cls = rc_cls, - .pg = pg - }; - enum GNUNET_DB_QueryStatus qs; - - /* no preflight check here, run in transaction by caller! */ - TALER_LOG_DEBUG ("Looking for refund of h_contract_terms %s at `%s'\n", - GNUNET_h2s (&h_contract_terms->hash), - instance_id); - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_refunds", - params, - &lookup_refunds_cb, - &lrc); - if (0 >= qs) - return qs; - return lrc.qs; -} - - -/** - * Mark contract as paid and store the current @a session_id - * for which the contract was paid. Deletes the underlying order - * and marks the locked stocks of the order as sold. - * - * @param cls closure - * @param instance_id instance to mark contract as paid for - * @param h_contract_terms hash of the contract that is now paid - * @param session_id the session that paid the contract - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_mark_contract_paid ( - void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - const char *session_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_string (session_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_QueryParam uparams[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - /* Session ID must always be given by the caller. */ - GNUNET_assert (NULL != session_id); - - /* no preflight check here, run in transaction by caller! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Marking h_contract_terms '%s' of %s as paid for session `%s'\n", - GNUNET_h2s (&h_contract_terms->hash), - instance_id, - session_id); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "mark_contract_paid", - params); - if (qs <= 0) - return qs; - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "mark_inventory_sold", - uparams); - if (qs < 0) - return qs; /* 0: no inventory management, that's OK! */ - /* ON DELETE CASCADE deletes from merchant_order_locks */ - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_completed_order", - uparams); -} - - -/** - * Function called during aborts to refund a coin. Marks the - * respective coin as refunded. - * - * @param cls closure - * @param instance_id instance to refund payment for - * @param h_contract_terms hash of the contract to refund coin for - * @param refund_timestamp timestamp of the refund - * @param coin_pub public key of the coin to refund (fully) - * @param reason text justifying the refund - * @return transaction status - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a coin_pub is unknown to us; - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid, - * regardless of whether it actually increased the refund - */ -static enum GNUNET_DB_QueryStatus -postgres_refund_coin (void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - struct GNUNET_TIME_Timestamp refund_timestamp, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const char *reason) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_timestamp (&refund_timestamp), - GNUNET_PQ_query_param_auto_from_type (coin_pub), - GNUNET_PQ_query_param_string (reason), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "refund_coin", - params); -} - - -/** - * Retrieve contract terms given its @a order_id - * - * @param cls closure - * @param instance_id instance's identifier - * @param order_id order to lookup contract for - * @param[out] h_contract_terms set to the hash of the contract. - * @param[out] paid set to the payment status of the contract - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_order_status (void *cls, - const char *instance_id, - const char *order_id, - struct TALER_PrivateContractHashP * - h_contract_terms, - bool *paid) -{ - struct PostgresClosure *pg = cls; - uint8_t paid8; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", - h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ("paid", - &paid8), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_order_status", - params, - rs); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - *paid = (0 != paid8); - else - *paid = false; /* just to be safe(r) */ - return qs; -} - - -/** - * Retrieve contract terms given its @a order_serial - * - * @param cls closure - * @param instance_id instance's identifier - * @param order_serial serial ID of the order to look up - * @param[out] order_id set to ID of the order - * @param[out] h_contract_terms set to the hash of the contract. - * @param[out] paid set to the payment status of the contract - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_order_status_by_serial (void *cls, - const char *instance_id, - uint64_t order_serial, - char **order_id, - struct TALER_PrivateContractHashP * - h_contract_terms, - bool *paid) -{ - struct PostgresClosure *pg = cls; - uint8_t paid8; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&order_serial), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", - h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ("paid", - &paid8), - GNUNET_PQ_result_spec_string ("order_id", - order_id), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_order_status_by_serial", - params, - rs); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - *paid = (0 != paid8); - else - *paid = false; /* just to be safe(r) */ - return qs; -} - - -/** - * Retrieve payment and wire status for a given @a order_serial and session ID. - * - * @param cls closure - * @param order_serial identifies the order - * @param session_id session for which to check the payment status, NULL for any - * @param[out] paid set to the payment status of the contract - * @param[out] wired set to the wire transfer status of the exchange payment - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_payment_status (void *cls, - uint64_t order_serial, - const char *session_id, - bool *paid, - bool *wired) -{ - struct PostgresClosure *pg = cls; - uint8_t paid8; - uint8_t wired8; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("paid", - &paid8), - GNUNET_PQ_result_spec_auto_from_type ("wired", - &wired8), - GNUNET_PQ_result_spec_end - }; - check_connection (pg); - if (NULL == session_id) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&order_serial), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_payment_status", - params, - rs); - } - else - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&order_serial), - GNUNET_PQ_query_param_string (session_id), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_payment_status_session_id", - params, - rs); - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - *paid = (0 != paid8); - *wired = (0 != wired8); - } - else - { - *paid = false; /* just to be safe(r) */ - *wired = false; /* just to be safe(r) */ - } - return qs; -} - - -/** - * Closure for lookup_deposits_by_order_cb(). - */ -struct LookupDepositsByOrderContext -{ - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Function to call with all results. - */ - TALER_MERCHANTDB_DepositedCoinsCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Set to the query result. - */ - 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 LookupDepositsByOrderContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_deposits_by_order_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupDepositsByOrderContext *ldoc = cls; - struct PostgresClosure *pg = ldoc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - uint64_t deposit_serial; - char *exchange_url; - struct TALER_MerchantWireHashP h_wire; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_Amount amount_with_fee; - struct TALER_Amount deposit_fee; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("deposit_serial", - &deposit_serial), - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &h_wire), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &amount_with_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee", - &deposit_fee), - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &coin_pub), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ldoc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - ldoc->cb (ldoc->cb_cls, - deposit_serial, - exchange_url, - &h_wire, - &amount_with_fee, - &deposit_fee, - &coin_pub); - GNUNET_PQ_cleanup_result (rs); /* technically useless here */ - } - ldoc->qs = num_results; -} - - -/** - * Retrieve details about coins that were deposited for an order. - * - * @param cls closure - * @param order_serial identifies the order - * @param cb function to call for each deposited coin - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_deposits_by_order (void *cls, - uint64_t order_serial, - TALER_MERCHANTDB_DepositedCoinsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupDepositsByOrderContext ldoc = { - .pg = pg, - .cb = cb, - .cb_cls = cb_cls - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&order_serial), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_deposits_by_order", - params, - &lookup_deposits_by_order_cb, - &ldoc); - if (qs < 0) - return qs; - return ldoc.qs; -} - - -/** - * Closure for lookup_deposits_by_order_cb(). - */ -struct LookupTransferDetailsByOrderContext -{ - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Function to call with all results. - */ - TALER_MERCHANTDB_OrderTransferDetailsCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Set to the query result. - */ - 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 LookupTransferDetailsByOrderContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_transfer_details_by_order_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTransferDetailsByOrderContext *ltdo = cls; - struct PostgresClosure *pg = ltdo->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - struct TALER_WireTransferIdentifierRawP wtid; - char *exchange_url; - uint64_t deposit_serial; - struct GNUNET_TIME_Timestamp execution_time; - struct TALER_Amount deposit_value; - struct TALER_Amount deposit_fee; - uint8_t transfer_confirmed; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("deposit_serial", - &deposit_serial), - GNUNET_PQ_result_spec_timestamp ("deposit_timestamp", - &execution_time), - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - GNUNET_PQ_result_spec_auto_from_type ("wtid", - &wtid), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value", - &deposit_value), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee", - &deposit_fee), - GNUNET_PQ_result_spec_auto_from_type ("transfer_confirmed", - &transfer_confirmed), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ltdo->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - ltdo->cb (ltdo->cb_cls, - &wtid, - exchange_url, - execution_time, - &deposit_value, - &deposit_fee, - (0 != transfer_confirmed)); - GNUNET_PQ_cleanup_result (rs); /* technically useless here */ - } - ltdo->qs = num_results; -} - - -/** - * Retrieve wire transfer details for all deposits associated with - * a given @a order_serial. - * - * @param cls closure - * @param order_serial identifies the order - * @param cb function called with the wire transfer details - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_transfer_details_by_order ( - void *cls, - uint64_t order_serial, - TALER_MERCHANTDB_OrderTransferDetailsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupTransferDetailsByOrderContext ltdo = { - .pg = pg, - .cb = cb, - .cb_cls = cb_cls - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&order_serial), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - "lookup_transfer_details_by_order", - params, - &lookup_transfer_details_by_order_cb, - <do); - if (qs < 0) - return qs; - return ltdo.qs; -} - - -/** - * Insert wire transfer details for a deposit. - * - * @param cls closure - * @param deposit_serial serial number of the deposit - * @param dd deposit transfer data from the exchange to store - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_deposit_to_transfer ( - void *cls, - uint64_t deposit_serial, - const struct TALER_EXCHANGE_DepositData *dd) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&deposit_serial), - TALER_PQ_query_param_amount (&dd->coin_contribution), - GNUNET_PQ_query_param_timestamp (&dd->execution_time), - GNUNET_PQ_query_param_auto_from_type (&dd->exchange_sig), - GNUNET_PQ_query_param_auto_from_type (&dd->exchange_pub), - GNUNET_PQ_query_param_auto_from_type (&dd->wtid), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_deposit_to_transfer", - params); -} - - -/** - * Set 'wired' status for an order to 'true'. - * - * @param cls closure - * @param order_serial serial number of the order - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_mark_order_wired (void *cls, - uint64_t order_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&order_serial), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "mark_order_wired", - params); -} - - -/** - * Closure for #process_refund_cb(). - */ -struct FindRefundContext -{ - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Updated to reflect total amount refunded so far. - */ - struct TALER_Amount refunded_amount; - - /** - * Set to the largest refund transaction ID encountered. - */ - uint64_t max_rtransaction_id; - - /** - * Set to true on hard errors. - */ - bool err; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure, our `struct FindRefundContext` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -process_refund_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct FindRefundContext *ictx = cls; - struct PostgresClosure *pg = ictx->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - /* Sum up existing refunds */ - struct TALER_Amount acc; - uint64_t rtransaction_id; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount", - &acc), - GNUNET_PQ_result_spec_uint64 ("rtransaction_id", - &rtransaction_id), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ictx->err = true; - return; - } - if (0 > - TALER_amount_add (&ictx->refunded_amount, - &ictx->refunded_amount, - &acc)) - { - GNUNET_break (0); - ictx->err = true; - return; - } - ictx->max_rtransaction_id = GNUNET_MAX (ictx->max_rtransaction_id, - rtransaction_id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found refund of %s\n", - TALER_amount2s (&acc)); - } -} - - -/** - * Closure for #process_deposits_for_refund_cb(). - */ -struct InsertRefundContext -{ - /** - * Used to provide a connection to the db - */ - struct PostgresClosure *pg; - - /** - * Amount to which increase the refund for this contract - */ - const struct TALER_Amount *refund; - - /** - * Human-readable reason behind this refund - */ - const char *reason; - - /** - * Transaction status code. - */ - enum TALER_MERCHANTDB_RefundStatus rs; -}; - - -/** - * Data extracted per coin. - */ -struct RefundCoinData -{ - - /** - * Public key of a coin. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Amount deposited for this coin. - */ - struct TALER_Amount deposited_with_fee; - - /** - * Amount refunded already for this coin. - */ - struct TALER_Amount refund_amount; - - /** - * Order serial (actually not really per-coin). - */ - uint64_t order_serial; - - /** - * Maximum rtransaction_id for this coin so far. - */ - uint64_t max_rtransaction_id; - -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure, our `struct InsertRefundContext` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -process_deposits_for_refund_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct InsertRefundContext *ctx = cls; - struct PostgresClosure *pg = ctx->pg; - struct TALER_Amount current_refund; - struct RefundCoinData rcd[GNUNET_NZL (num_results)]; - struct GNUNET_TIME_Timestamp now; - - now = GNUNET_TIME_timestamp_get (); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (ctx->refund->currency, - ¤t_refund)); - memset (rcd, 0, sizeof (rcd)); - /* Pass 1: Collect amount of existing refunds into current_refund. - * Also store existing refunded amount for each deposit in deposit_refund. */ - for (unsigned int i = 0; i<num_results; i++) - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &rcd[i].coin_pub), - GNUNET_PQ_result_spec_uint64 ("order_serial", - &rcd[i].order_serial), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &rcd[i].deposited_with_fee), - GNUNET_PQ_result_spec_end - }; - struct FindRefundContext ictx = { - .pg = pg - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - } - - { - enum GNUNET_DB_QueryStatus ires; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub), - GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial), - GNUNET_PQ_query_param_end - }; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (ctx->refund->currency, - &ictx.refunded_amount)); - ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn, - "find_refunds_by_coin", - params, - &process_refund_cb, - &ictx); - if ( (ictx.err) || - (GNUNET_DB_STATUS_HARD_ERROR == ires) ) - { - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - } - if (GNUNET_DB_STATUS_SOFT_ERROR == ires) - { - ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR; - return; - } - } - if (0 > - TALER_amount_add (¤t_refund, - ¤t_refund, - &ictx.refunded_amount)) - { - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - } - rcd[i].refund_amount = ictx.refunded_amount; - rcd[i].max_rtransaction_id = ictx.max_rtransaction_id; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Existing refund for coin %s is %s\n", - TALER_B2S (&rcd[i].coin_pub), - TALER_amount2s (&ictx.refunded_amount)); - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Total existing refund is %s\n", - TALER_amount2s (¤t_refund)); - - /* stop immediately if we are 'done' === amount already - * refunded. */ - if (0 >= TALER_amount_cmp (ctx->refund, - ¤t_refund)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Existing refund of %s at or above requested refund. Finished early.\n", - TALER_amount2s (¤t_refund)); - ctx->rs = TALER_MERCHANTDB_RS_SUCCESS; - return; - } - - /* Phase 2: Try to increase current refund until it matches desired refund */ - for (unsigned int i = 0; i<num_results; i++) - { - const struct TALER_Amount *increment; - struct TALER_Amount left; - struct TALER_Amount remaining_refund; - - /* How much of the coin is left after the existing refunds? */ - if (0 > - TALER_amount_subtract (&left, - &rcd[i].deposited_with_fee, - &rcd[i].refund_amount)) - { - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - } - - if ( (0 == left.value) && - (0 == left.fraction) ) - { - /* coin was fully refunded, move to next coin */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Coin %s fully refunded, moving to next coin\n", - TALER_B2S (&rcd[i].coin_pub)); - continue; - } - - rcd[i].max_rtransaction_id++; - /* How much of the refund is still to be paid back? */ - if (0 > - TALER_amount_subtract (&remaining_refund, - ctx->refund, - ¤t_refund)) - { - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - } - - /* By how much will we increase the refund for this coin? */ - if (0 >= TALER_amount_cmp (&remaining_refund, - &left)) - { - /* remaining_refund <= left */ - increment = &remaining_refund; - } - else - { - increment = &left; - } - - if (0 > - TALER_amount_add (¤t_refund, - ¤t_refund, - increment)) - { - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - } - - /* actually run the refund */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Coin %s deposit amount is %s\n", - TALER_B2S (&rcd[i].coin_pub), - TALER_amount2s (&rcd[i].deposited_with_fee)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Coin %s refund will be incremented by %s\n", - TALER_B2S (&rcd[i].coin_pub), - TALER_amount2s (increment)); - { - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial), - GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already inc'ed */ - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub), - GNUNET_PQ_query_param_string (ctx->reason), - TALER_PQ_query_param_amount (increment), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_refund", - params); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR; - return; - case GNUNET_DB_STATUS_SOFT_ERROR: - ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR; - return; - default: - ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs; - break; - } - } - - /* stop immediately if we are done */ - if (0 == TALER_amount_cmp (ctx->refund, - ¤t_refund)) - { - ctx->rs = TALER_MERCHANTDB_RS_SUCCESS; - return; - } - } - - /** - * We end up here if not all of the refund has been covered. - * Although this should be checked as the business should never - * issue a refund bigger than the contract's actual price, we cannot - * rely upon the frontend being correct. - */// - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "The refund of %s is bigger than the order's value\n", - TALER_amount2s (ctx->refund)); - ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH; -} - - -/** - * Function called when some backoffice staff decides to award or - * increase the refund on an existing contract. This function - * MUST be called from within a transaction scope setup by the - * caller as it executes multiple SQL statements. - * - * @param cls closure - * @param instance_id instance identifier - * @param order_id the order to increase the refund for - * @param refund maximum refund to return to the customer for this contract - * @param reason 0-terminated UTF-8 string giving the reason why the customer - * got a refund (free form, business-specific) - * @return transaction status - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we - * were originally paid and thus the transaction failed; - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid, - * regardless of whether it actually increased the refund beyond - * what was already refunded (idempotency!) - */ -static enum TALER_MERCHANTDB_RefundStatus -postgres_increase_refund (void *cls, - const char *instance_id, - const char *order_id, - const struct TALER_Amount *refund, - const char *reason) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (order_id), - GNUNET_PQ_query_param_end - }; - struct InsertRefundContext ctx = { - .pg = pg, - .refund = refund, - .reason = reason - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Asked to refund %s on order %s\n", - TALER_amount2s (refund), - order_id); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "find_deposits_for_refund", - params, - &process_deposits_for_refund_cb, - &ctx); - switch (qs) - { - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* never paid, means we clearly cannot refund anything */ - return TALER_MERCHANTDB_RS_NO_SUCH_ORDER; - case GNUNET_DB_STATUS_SOFT_ERROR: - return TALER_MERCHANTDB_RS_SOFT_ERROR; - case GNUNET_DB_STATUS_HARD_ERROR: - return TALER_MERCHANTDB_RS_HARD_ERROR; - default: - /* Got one or more deposits */ - return ctx.rs; - } -} - - -/** - * Closure for #lookup_refunds_detailed_cb(). - */ -struct LookupRefundsDetailedContext -{ - /** - * Function to call for each refund. - */ - TALER_MERCHANTDB_RefundDetailCallback rc; - - /** - * Closure for @e rc. - */ - void *rc_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Transaction result. - */ - 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 GetRefundsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_refunds_detailed_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupRefundsDetailedContext *lrdc = cls; - struct PostgresClosure *pg = lrdc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - uint64_t refund_serial; - struct GNUNET_TIME_Timestamp timestamp; - struct TALER_CoinSpendPublicKeyP coin_pub; - uint64_t rtransaction_id; - struct TALER_Amount refund_amount; - char *reason; - char *exchange_url; - uint8_t pending8; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("refund_serial", - &refund_serial), - GNUNET_PQ_result_spec_timestamp ("refund_timestamp", - ×tamp), - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &coin_pub), - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - GNUNET_PQ_result_spec_uint64 ("rtransaction_id", - &rtransaction_id), - GNUNET_PQ_result_spec_string ("reason", - &reason), - TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount", - &refund_amount), - GNUNET_PQ_result_spec_auto_from_type ("pending", - &pending8), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lrdc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - lrdc->rc (lrdc->rc_cls, - refund_serial, - timestamp, - &coin_pub, - exchange_url, - rtransaction_id, - reason, - &refund_amount, - 0 != pending8); - GNUNET_PQ_cleanup_result (rs); - } - lrdc->qs = num_results; -} - - -/** - * Obtain detailed refund data associated with a contract. - * - * @param cls closure, typically a connection to the db - * @param instance_id instance to lookup refunds for - * @param h_contract_terms hash code of the contract - * @param rc function to call for each coin on which there is a refund - * @param rc_cls closure for @a rc - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_refunds_detailed ( - void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - TALER_MERCHANTDB_RefundDetailCallback rc, - void *rc_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_end - }; - struct LookupRefundsDetailedContext lrdc = { - .rc = rc, - .rc_cls = rc_cls, - .pg = pg - }; - enum GNUNET_DB_QueryStatus qs; - - /* no preflight check here, run in transaction by caller! */ - TALER_LOG_DEBUG ("Looking for refund %s + %s\n", - GNUNET_h2s (&h_contract_terms->hash), - instance_id); - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_refunds_detailed", - params, - &lookup_refunds_detailed_cb, - &lrdc); - if (0 >= qs) - return qs; - return lrdc.qs; -} - - -/** - * Insert refund proof data from the exchange into the database. - * - * @param cls closure - * @param refund_serial serial number of the refund - * @param exchange_sig signature from exchange that coin was refunded - * @param exchange_pub signing key that was used for @a exchange_sig - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_refund_proof ( - void *cls, - uint64_t refund_serial, - const struct TALER_ExchangeSignatureP *exchange_sig, - const struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&refund_serial), - GNUNET_PQ_query_param_auto_from_type (exchange_sig), - GNUNET_PQ_query_param_auto_from_type (exchange_pub), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_refund_proof", - params); -} - - -/** - * Lookup refund proof data. - * - * @param cls closure - * @param refund_serial serial number of the refund - * @param[out] exchange_sig set to signature from exchange - * @param[out] exchange_pub signing key that was used for @a exchange_sig - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_refund_proof (void *cls, - uint64_t refund_serial, - struct TALER_ExchangeSignatureP *exchange_sig, - struct TALER_ExchangePublicKeyP *exchange_pub) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&refund_serial), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("exchange_sig", - exchange_sig), - GNUNET_PQ_result_spec_auto_from_type ("exchange_pub", - exchange_pub), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_refund_proof", - params, - rs); -} - - -/** - * Retrieve the order ID that was used to pay for a resource within a session. - * - * @param cls closure - * @param instance_id identifying the instance - * @param fulfillment_url URL that canonically identifies the resource - * being paid for - * @param session_id session id - * @param[out] order_id where to store the order ID that was used when - * paying for the resource URL - * @return transaction status - */ -enum GNUNET_DB_QueryStatus -postgres_lookup_order_by_fulfillment (void *cls, - const char *instance_id, - const char *fulfillment_url, - const char *session_id, - char **order_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (fulfillment_url), - GNUNET_PQ_query_param_string (session_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("order_id", - order_id), - GNUNET_PQ_result_spec_end - }; - - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_order_by_fulfillment", - params, - rs); -} - - -/** - * Insert information about a wire transfer the merchant has received. - * - * @param cls closure - * @param instance_id the instance that received the transfer - * @param exchange_url which exchange made the transfer - * @param wtid identifier of the wire transfer - * @param credit_amount how much did we receive - * @param payto_uri what is the merchant's bank account that received the transfer - * @param confirmed whether the transfer was confirmed by the merchant or - * was merely claimed by the exchange at this point - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_transfer ( - void *cls, - const char *instance_id, - const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - const struct TALER_Amount *credit_amount, - const char *payto_uri, - bool confirmed) -{ - struct PostgresClosure *pg = cls; - uint8_t confirmed8 = confirmed; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_auto_from_type (wtid), - TALER_PQ_query_param_amount (credit_amount), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_auto_from_type (&confirmed8), - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_transfer", - params); -} - - -/** - * Delete information about a transfer. Note that transfers - * confirmed by the exchange cannot be deleted anymore. - * - * @param cls closure - * @param instance_id instance to delete transfer of - * @param transfer_serial_id transfer to delete - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS - * if deletion is prohibited OR transfer is unknown - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_transfer (void *cls, - const char *instance_id, - uint64_t transfer_serial_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&transfer_serial_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_transfer", - params); -} - - -/** - * Check if information about a transfer exists with the - * backend. Returns no data, only the query status. - * - * @param cls closure - * @param instance_id instance to delete transfer of - * @param transfer_serial_id transfer to delete - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT - * if the transfer record exists - */ -static enum GNUNET_DB_QueryStatus -postgres_check_transfer_exists (void *cls, - const char *instance_id, - uint64_t transfer_serial_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&transfer_serial_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "check_transfer_exists", - params, - rs); -} - - -/** - * Lookup account serial by payto URI. - * - * @param cls closure - * @param instance_id instance to lookup the account from - * @param payto_uri what is the merchant's bank account to lookup - * @param[out] account_serial serial number of the account - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_account (void *cls, - const char *instance_id, - const char *payto_uri, - uint64_t *account_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("account_serial", - account_serial), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_account", - params, - rs); -} - - -/** - * Insert information about a wire transfer the merchant has received. - * - * @param cls closure - * @param instance_id instance to provide transfer details for - * @param exchange_url which exchange made the transfer - * @param payto_uri what is the merchant's bank account that received the transfer - * @param wtid identifier of the wire transfer - * @param td transfer details to store - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_transfer_details ( - void *cls, - const char *instance_id, - const char *exchange_url, - const char *payto_uri, - const struct TALER_WireTransferIdentifierRawP *wtid, - const struct TALER_EXCHANGE_TransferData *td) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - uint64_t credit_serial; - unsigned int retries; - - retries = 0; - check_connection (pg); -RETRY: - if (MAX_RETRIES < ++retries) - return GNUNET_DB_STATUS_SOFT_ERROR; - if (GNUNET_OK != - postgres_start_read_committed (pg, - "insert transfer details")) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - /* lookup credit serial */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("credit_serial", - &credit_serial), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_credit_serial", - params, - rs); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - postgres_rollback (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "'lookup_credit_serial' for account %s and amount %s failed with status %d\n", - payto_uri, - TALER_amount2s (&td->total_amount), - qs); - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - postgres_rollback (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "'lookup_credit_serial' for account %s failed with transfer unknown\n", - payto_uri); - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - } - } - - /* update merchant_transfer_signatures table */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&credit_serial), - TALER_PQ_query_param_amount (&td->total_amount), - TALER_PQ_query_param_amount (&td->wire_fee), - GNUNET_PQ_query_param_timestamp (&td->execution_time), - GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig), - GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_transfer_signature", - params); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - postgres_rollback (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "'insert_transfer_signature' failed with status %d\n", - qs); - return qs; - } - if (0 == qs) - { - postgres_rollback (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "'insert_transfer_signature' failed with status %d\n", - qs); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - - /* Update transfer-coin association table */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Updating transfer-coin association table\n"); - for (unsigned int i = 0; i<td->details_length; i++) - { - const struct TALER_TrackTransferDetails *d = &td->details[i]; - uint64_t i64 = (uint64_t) i; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&credit_serial), - GNUNET_PQ_query_param_uint64 (&i64), - TALER_PQ_query_param_amount (&d->coin_value), - TALER_PQ_query_param_amount (&d->coin_fee), /* deposit fee */ - GNUNET_PQ_query_param_auto_from_type (&d->coin_pub), - GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms), - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_transfer_to_coin_mapping", - params); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - postgres_rollback (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "'insert_transfer_to_coin_mapping' failed with status %d\n", - qs); - return qs; - } - if (0 == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "'insert_transfer_to_coin_mapping' failed at %u: deposit unknown\n", - i); - } - } - /* Update merchant_contract_terms 'wired' status: for all coins - that were wired, set the respective order's "wired" status to - true, *if* all other deposited coins associated with that order - have also been wired (this time or earlier) */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Updating contract terms 'wired' status\n"); - for (unsigned int i = 0; i<td->details_length; i++) - { - const struct TALER_TrackTransferDetails *d = &td->details[i]; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (&d->coin_pub), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_wired_by_coin_pub", - params); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - postgres_rollback (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "'update_wired_by_coin_pub' failed with status %d\n", - qs); - return qs; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Committing transaction...\n"); - qs = postgres_commit (pg); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - return qs; -} - - -/** - * Obtain information about wire fees charged by an exchange, - * including signature (so we have proof). - * - * @param cls closure - * @param master_pub public key of the exchange - * @param wire_method the wire method - * @param contract_date date of the contract to use for the lookup - * @param[out] fees wire fees charged - * @param[out] start_date start of fee being used - * @param[out] end_date end of fee being used - * @param[out] master_sig signature of exchange over fee structure - * @return transaction status code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_wire_fee (void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const char *wire_method, - struct GNUNET_TIME_Timestamp contract_date, - struct TALER_WireFeeSet *fees, - struct GNUNET_TIME_Timestamp *start_date, - struct GNUNET_TIME_Timestamp *end_date, - struct TALER_MasterSignatureP *master_sig) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_HashCode h_wire_method; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (master_pub), - GNUNET_PQ_query_param_auto_from_type (&h_wire_method), - GNUNET_PQ_query_param_timestamp (&contract_date), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", - &fees->wire), - TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", - &fees->closing), - GNUNET_PQ_result_spec_timestamp ("start_date", - start_date), - GNUNET_PQ_result_spec_timestamp ("end_date", - end_date), - GNUNET_PQ_result_spec_auto_from_type ("master_sig", - master_sig), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - GNUNET_CRYPTO_hash (wire_method, - strlen (wire_method) + 1, - &h_wire_method); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_wire_fee", - params, - rs); -} - - -/** - * Closure for #lookup_deposits_by_contract_and_coin_cb(). - */ -struct LookupDepositsByCnCContext -{ - /** - * Function to call for each deposit. - */ - TALER_MERCHANTDB_CoinDepositCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Transaction result. - */ - 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 LookupDepositsByCnCContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_deposits_by_contract_and_coin_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupDepositsByCnCContext *ldcc = cls; - struct PostgresClosure *pg = ldcc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - char *exchange_url; - struct TALER_Amount amount_with_fee; - struct TALER_Amount deposit_fee; - struct TALER_Amount refund_fee; - struct TALER_Amount wire_fee; - struct TALER_MerchantWireHashP h_wire; - struct GNUNET_TIME_Timestamp deposit_timestamp; - struct GNUNET_TIME_Timestamp refund_deadline; - struct TALER_ExchangeSignatureP exchange_sig; - struct TALER_ExchangePublicKeyP exchange_pub; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", - &amount_with_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee", - &deposit_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee", - &refund_fee), - TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", - &wire_fee), - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &h_wire), - GNUNET_PQ_result_spec_timestamp ("deposit_timestamp", - &deposit_timestamp), - GNUNET_PQ_result_spec_timestamp ("refund_deadline", - &refund_deadline), - GNUNET_PQ_result_spec_auto_from_type ("exchange_sig", - &exchange_sig), - GNUNET_PQ_result_spec_auto_from_type ("exchange_pub", - &exchange_pub), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ldcc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - ldcc->cb (ldcc->cb_cls, - exchange_url, - &amount_with_fee, - &deposit_fee, - &refund_fee, - &wire_fee, - &h_wire, - deposit_timestamp, - refund_deadline, - &exchange_sig, - &exchange_pub); - GNUNET_PQ_cleanup_result (rs); - } - ldcc->qs = num_results; -} - - -/** - * Lookup information about coin payments by @a h_contract_terms and - * @a coin_pub. - * - * @param cls closure - * @param instance_id instance to lookup payments for - * @param h_contract_terms proposal data's hashcode - * @param coin_pub public key to use for the search - * @param cb function to call with payment data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_deposits_by_contract_and_coin ( - void *cls, - const char *instance_id, - const struct TALER_PrivateContractHashP *h_contract_terms, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - TALER_MERCHANTDB_CoinDepositCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_auto_from_type (coin_pub), - GNUNET_PQ_query_param_end - }; - struct LookupDepositsByCnCContext ldcc = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - "lookup_deposits_by_contract_and_coin", - params, - &lookup_deposits_by_contract_and_coin_cb, - &ldcc); - if (0 >= qs) - return qs; - return ldcc.qs; -} - - -/** - * Lookup transfer status. - * - * @param cls closure - * @param instance_id at which instance should we resolve the transfer - * @param exchange_url the exchange that made the transfer - * @param wtid wire transfer subject - * @param[out] total_amount amount that was debited from our - * aggregate balance at the exchange (in total, sum of - * the wire transfer amount and the @a wire_fee) - * @param[out] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true) - * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true) - * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true) - * @param[out] have_exchange_sig do we have a response from the exchange about this transfer - * @param[out] verified did we confirm the transfer was OK - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_transfer ( - void *cls, - const char *instance_id, - const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - struct TALER_Amount *total_amount, - struct TALER_Amount *wire_fee, - struct TALER_Amount *exchange_amount, - struct GNUNET_TIME_Timestamp *execution_time, - bool *have_exchange_sig, - bool *verified) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - uint8_t verified8; - /** Amount we got actually credited, _excludes_ the wire fee */ - bool no_sig; - struct TALER_Amount credit_amount; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount", - &credit_amount), - GNUNET_PQ_result_spec_allow_null ( - TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", - wire_fee), - &no_sig), - GNUNET_PQ_result_spec_allow_null ( - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount", - exchange_amount), - NULL), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_timestamp ("execution_time", - execution_time), - NULL), - GNUNET_PQ_result_spec_auto_from_type ("verified", - &verified8), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - *execution_time = GNUNET_TIME_UNIT_ZERO_TS; - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_transfer", - params, - rs); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Lookup transfer returned %d\n", - qs); - if (qs > 0) - { - *have_exchange_sig = ! no_sig; - *verified = (0 != verified8); - if ( (! no_sig) && - (0 > - TALER_amount_add (total_amount, - &credit_amount, - wire_fee)) ) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - else - { - *verified = false; - *have_exchange_sig = false; - } - return qs; -} - - -/** - * Set transfer status to verified. - * - * @param cls closure - * @param exchange_url the exchange that made the transfer - * @param wtid wire transfer subject - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_set_transfer_status_to_verified ( - void *cls, - const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select ( - pg->conn, - "set_transfer_status_to_verified", - params); -} - - -/** - * Closure for #lookup_transfer_summary_cb(). - */ -struct LookupTransferSummaryContext -{ - /** - * Function to call for each order that was aggregated. - */ - TALER_MERCHANTDB_TransferSummaryCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Transaction result. - */ - 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 LookupTransferSummaryContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_transfer_summary_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTransferSummaryContext *ltdc = cls; - struct PostgresClosure *pg = ltdc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - char *order_id; - struct TALER_Amount deposit_value; - struct TALER_Amount deposit_fee; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("order_id", - &order_id), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value", - &deposit_value), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee", - &deposit_fee), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - ltdc->cb (ltdc->cb_cls, - order_id, - &deposit_value, - &deposit_fee); - GNUNET_PQ_cleanup_result (rs); - } - ltdc->qs = num_results; -} - - -/** - * Lookup transfer summary. - * - * @param cls closure - * @param exchange_url the exchange that made the transfer - * @param wtid wire transfer subject - * @param cb function to call with detailed transfer data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_transfer_summary ( - void *cls, - const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_MERCHANTDB_TransferSummaryCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_end - }; - struct LookupTransferSummaryContext ltdc = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - "lookup_transfer_summary", - params, - &lookup_transfer_summary_cb, - <dc); - if (0 >= qs) - return qs; - return ltdc.qs; -} - - -/** - * Closure for #lookup_transfer_details_cb(). - */ -struct LookupTransferDetailsContext -{ - /** - * Function to call for each order that was aggregated. - */ - TALER_MERCHANTDB_TransferDetailsCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Transaction result. - */ - 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 LookupTransferDetailsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_transfer_details_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTransferDetailsContext *ltdc = cls; - struct PostgresClosure *pg = ltdc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - uint64_t current_offset; - struct TALER_TrackTransferDetails ttd; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("offset_in_exchange_list", - ¤t_offset), - GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", - &ttd.h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &ttd.coin_pub), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value", - &ttd.coin_value), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee", - &ttd.coin_fee), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - ltdc->cb (ltdc->cb_cls, - (unsigned int) current_offset, - &ttd); - GNUNET_PQ_cleanup_result (rs); - } - ltdc->qs = num_results; -} - - -/** - * Lookup transfer details. - * - * @param cls closure - * @param exchange_url the exchange that made the transfer - * @param wtid wire transfer subject - * @param cb function to call with detailed transfer data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_transfer_details ( - void *cls, - const char *exchange_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_MERCHANTDB_TransferDetailsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_end - }; - struct LookupTransferDetailsContext ltdc = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - "lookup_transfer_details", - params, - &lookup_transfer_details_cb, - <dc); - if (0 >= qs) - return qs; - return ltdc.qs; -} - - -/** - * Closure for #lookup_transfers_cb(). - */ -struct LookupTransfersContext -{ - /** - * Function to call on results. - */ - TALER_MERCHANTDB_TransferCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Postgres context. - */ - struct PostgresClosure *pg; - - /** - * Transaction status (set). - */ - enum GNUNET_DB_QueryStatus qs; - - /** - * Filter to apply by verification status. - */ - enum TALER_EXCHANGE_YesNoAll verified; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls of type `struct LookupTransfersContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_transfers_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTransfersContext *ltc = cls; - struct PostgresClosure *pg = ltc->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - struct TALER_Amount credit_amount; - struct TALER_WireTransferIdentifierRawP wtid; - char *payto_uri; - char *exchange_url; - uint64_t transfer_serial_id; - struct GNUNET_TIME_Timestamp execution_time; - enum TALER_EXCHANGE_YesNoAll verified; - uint8_t verified8; - uint8_t confirmed8; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount", - &credit_amount), - GNUNET_PQ_result_spec_auto_from_type ("wtid", - &wtid), - GNUNET_PQ_result_spec_string ("payto_uri", - &payto_uri), - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - GNUNET_PQ_result_spec_uint64 ("credit_serial", - &transfer_serial_id), - GNUNET_PQ_result_spec_timestamp ("execution_time", - &execution_time), - GNUNET_PQ_result_spec_auto_from_type ("verified", - &verified8), - GNUNET_PQ_result_spec_auto_from_type ("confirmed", - &confirmed8), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ltc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - if (0 == verified8) - verified = TALER_EXCHANGE_YNA_NO; - else - verified = TALER_EXCHANGE_YNA_YES; - if ( (ltc->verified == TALER_EXCHANGE_YNA_ALL) || - (ltc->verified == verified) ) - { - ltc->cb (ltc->cb_cls, - &credit_amount, - &wtid, - payto_uri, - exchange_url, - transfer_serial_id, - execution_time, - TALER_EXCHANGE_YNA_YES == verified, - 0 != confirmed8); - } - GNUNET_PQ_cleanup_result (rs); - } - ltc->qs = num_results; -} - - -/** - * Lookup transfers. Note that filtering by @a verified status is done - * outside of SQL, as we already have 8 prepared statements and adding - * a filter on verified would further double the number of statements for - * a likely rather ineffective filter. So we apply that filter in - * #lookup_transfers_cb(). - * - * @param cls closure - * @param instance_id instance to lookup payments for - * @param payto_uri account that we are interested in transfers to - * @param before timestamp for the earliest transfer we care about - * @param after timestamp for the last transfer we care about - * @param limit number of entries to return, negative for descending in execution time, - * positive for ascending in execution time - * @param offset transfer_serial number of the transfer we want to offset from - * @param verified filter transfers by verification status - * @param cb function to call with detailed transfer data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_transfers (void *cls, - const char *instance_id, - const char *payto_uri, - struct GNUNET_TIME_Timestamp before, - struct GNUNET_TIME_Timestamp after, - int64_t limit, - uint64_t offset, - enum TALER_EXCHANGE_YesNoAll verified, - TALER_MERCHANTDB_TransferCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit); - struct LookupTransfersContext ltc = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg, - .verified = verified - }; - enum GNUNET_DB_QueryStatus qs; - bool by_time; - - by_time = ( (! GNUNET_TIME_absolute_is_never (before.abs_time)) || - (! GNUNET_TIME_absolute_is_zero (after.abs_time)) ); - check_connection (pg); - if (by_time) - { - if (NULL != payto_uri) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_timestamp (&before), - GNUNET_PQ_query_param_timestamp (&after), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_time_payto_asc" - : "lookup_transfers_time_payto_desc", - params, - &lookup_transfers_cb, - <c); - } - else - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_timestamp (&before), - GNUNET_PQ_query_param_timestamp (&after), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_time_asc" - : "lookup_transfers_time_desc", - params, - &lookup_transfers_cb, - <c); - } - } - else - { - if (NULL != payto_uri) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_payto_asc" - : "lookup_transfers_payto_desc", - params, - &lookup_transfers_cb, - <c); - } - else - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_uint64 (&plimit), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_multi_select ( - pg->conn, - (limit > 0) - ? "lookup_transfers_asc" - : "lookup_transfers_desc", - params, - &lookup_transfers_cb, - <c); - } - } - if (0 >= qs) - return qs; - return ltc.qs; -} - - -/** - * Store information about wire fees charged by an exchange, - * including signature (so we have proof). - * - * @param cls closure - * @param master_pub public key of the exchange - * @param h_wire_method hash of wire method - * @param fees the fee charged - * @param start_date start of fee being used - * @param end_date end of fee being used - * @param master_sig signature of exchange over fee structure - * @return transaction status code - */ -static enum GNUNET_DB_QueryStatus -postgres_store_wire_fee_by_exchange ( - void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const struct GNUNET_HashCode *h_wire_method, - const struct TALER_WireFeeSet *fees, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp end_date, - const struct TALER_MasterSignatureP *master_sig) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (master_pub), - GNUNET_PQ_query_param_auto_from_type (h_wire_method), - TALER_PQ_query_param_amount (&fees->wire), - TALER_PQ_query_param_amount (&fees->closing), - GNUNET_PQ_query_param_timestamp (&start_date), - GNUNET_PQ_query_param_timestamp (&end_date), - GNUNET_PQ_query_param_auto_from_type (master_sig), - GNUNET_PQ_query_param_end - }; - - /* no preflight check here, run in its own transaction by the caller */ - check_connection (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Storing wire fee for %s starting at %s of %s\n", - TALER_B2S (master_pub), - GNUNET_TIME_timestamp2s (start_date), - TALER_amount2s (&fees->wire)); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_wire_fee", - params); -} - - -/** - * Add @a credit to a reserve to be used for tipping. Note that - * this function does not actually perform any wire transfers to - * credit the reserve, it merely tells the merchant backend that - * a reserve now exists. This has to happen before tips can be - * authorized. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance is the reserve tied to - * @param reserve_priv which reserve is topped up or created - * @param reserve_pub which reserve is topped up or created - * @param exchange_url what URL is the exchange reachable at where the reserve is located - * @param payto_uri URI to use to fund the reserve - * @param initial_balance how much money will be added to the reserve - * @param expiration when does the reserve expire? - * @return transaction status, usually - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success - */ -static enum TALER_ErrorCode -postgres_insert_reserve (void *cls, - const char *instance_id, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_ReservePublicKeyP *reserve_pub, - const char *exchange_url, - const char *payto_uri, - const struct TALER_Amount *initial_balance, - struct GNUNET_TIME_Timestamp expiration) -{ - struct PostgresClosure *pg = cls; - unsigned int retries; - enum GNUNET_DB_QueryStatus qs; - - retries = 0; - check_connection (pg); -RETRY: - if (MAX_RETRIES < ++retries) - return TALER_EC_GENERIC_DB_SOFT_FAILURE; - if (GNUNET_OK != - postgres_start (pg, - "insert reserve")) - { - GNUNET_break (0); - return TALER_EC_GENERIC_DB_START_FAILED; - } - - /* Setup reserve */ - { - struct GNUNET_TIME_Timestamp now; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_timestamp (&expiration), - TALER_PQ_query_param_amount (initial_balance), - GNUNET_PQ_query_param_end - }; - - now = GNUNET_TIME_timestamp_get (); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_reserve", - params); - if (0 > qs) - { - postgres_rollback (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - return qs; - } - } - /* Store private key */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_auto_from_type (reserve_priv), - GNUNET_PQ_query_param_string (exchange_url), - GNUNET_PQ_query_param_string (payto_uri), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_reserve_key", - params); - if (0 > qs) - { - postgres_rollback (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - return qs; - } - } - qs = postgres_commit (pg); - if (0 <= qs) - return TALER_EC_NONE; /* success */ - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - return qs; -} - - -/** - * Confirms @a credit as the amount the exchange claims to have received and - * thus really 'activates' the reserve. This has to happen before tips can - * be authorized. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance is the reserve tied to - * @param reserve_pub which reserve is topped up or created - * @param initial_exchange_balance how much money was be added to the reserve - * according to the exchange - * @return transaction status, usually - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success - */ -static enum GNUNET_DB_QueryStatus -postgres_activate_reserve (void *cls, - const char *instance_id, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_Amount *initial_exchange_balance) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - TALER_PQ_query_param_amount (initial_exchange_balance), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "activate_reserve", - params); -} - - -/** - * Closure for #lookup_reserves_cb. - */ -struct LookupReservesContext -{ - /** - * Postgres context. - */ - struct PostgresClosure *pg; - - /** - * Function to call with the results - */ - TALER_MERCHANTDB_ReservesCallback cb; - - /** - * Closure for @e cb - */ - void *cb_cls; - - /** - * Filter by active reserves. - */ - enum TALER_EXCHANGE_YesNoAll active; - - /** - * Filter by failures (mismatch in exchange claimed and - * merchant claimed initial amounts). - */ - enum TALER_EXCHANGE_YesNoAll failures; - - /** - * Set in case of errors. - */ - enum GNUNET_DB_QueryStatus qs; - -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param[in,out] cls of type `struct LookupReservesContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_reserves_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupReservesContext *lrc = cls; - struct PostgresClosure *pg = lrc->pg; - - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_ReservePublicKeyP reserve_pub; - struct GNUNET_TIME_Timestamp creation_time; - struct GNUNET_TIME_Timestamp expiration_time; - struct TALER_Amount merchant_initial_balance; - struct TALER_Amount exchange_initial_balance; - struct TALER_Amount pickup_amount; - struct TALER_Amount committed_amount; - uint8_t active; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", - &reserve_pub), - GNUNET_PQ_result_spec_timestamp ("creation_time", - &creation_time), - GNUNET_PQ_result_spec_timestamp ("expiration", - &expiration_time), - TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance", - &merchant_initial_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance", - &exchange_initial_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed", - &committed_amount), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up", - &pickup_amount), - GNUNET_PQ_result_spec_auto_from_type ("active", - &active), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lrc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - switch (lrc->active) - { - case TALER_EXCHANGE_YNA_YES: - if (0 == active) - continue; - break; - case TALER_EXCHANGE_YNA_NO: - if (0 != active) - continue; - break; - case TALER_EXCHANGE_YNA_ALL: - break; - } - switch (lrc->failures) - { - case TALER_EXCHANGE_YNA_YES: - if (0 == - TALER_amount_cmp (&merchant_initial_balance, - &exchange_initial_balance)) - continue; - break; - case TALER_EXCHANGE_YNA_NO: - if (0 != - TALER_amount_cmp (&merchant_initial_balance, - &exchange_initial_balance)) - continue; - break; - case TALER_EXCHANGE_YNA_ALL: - break; - } - lrc->cb (lrc->cb_cls, - &reserve_pub, - creation_time, - expiration_time, - &merchant_initial_balance, - &exchange_initial_balance, - &pickup_amount, - &committed_amount, - (0 != active)); - } -} - - -/** - * Lookup reserves. - * - * @param cls closure - * @param instance_id instance to lookup payments for - * @param created_after filter by reserves created after this date - * @param active filter by active reserves - * @param failures filter by reserves with a disagreement on the initial balance - * @param cb function to call with reserve summary data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_reserves (void *cls, - const char *instance_id, - struct GNUNET_TIME_Timestamp created_after, - enum TALER_EXCHANGE_YesNoAll active, - enum TALER_EXCHANGE_YesNoAll failures, - TALER_MERCHANTDB_ReservesCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupReservesContext lrc = { - .pg = pg, - .active = active, - .failures = failures, - .cb = cb, - .cb_cls = cb_cls - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_timestamp (&created_after), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_reserves", - params, - &lookup_reserves_cb, - &lrc); - if (lrc.qs < 0) - return lrc.qs; - return qs; -} - - -/** - * Closure for #lookup_pending_reserves_cb. - */ -struct LookupPendingReservesContext -{ - /** - * Postgres context. - */ - struct PostgresClosure *pg; - - /** - * Function to call with the results - */ - TALER_MERCHANTDB_PendingReservesCallback cb; - - /** - * Closure for @e cb - */ - void *cb_cls; - - /** - * Set in case of errors. - */ - enum GNUNET_DB_QueryStatus qs; - -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param[in,out] cls of type `struct LookupReservesContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_pending_reserves_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupPendingReservesContext *lrc = cls; - struct PostgresClosure *pg = lrc->pg; - - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount merchant_initial_balance; - char *exchange_url; - char *instance_id; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", - &reserve_pub), - GNUNET_PQ_result_spec_string ("merchant_id", - &instance_id), - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance", - &merchant_initial_balance), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lrc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - lrc->cb (lrc->cb_cls, - instance_id, - exchange_url, - &reserve_pub, - &merchant_initial_balance); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup reserves pending activation across all instances. - * - * @param cls closure - * @param cb function to call with reserve summary data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_pending_reserves (void *cls, - TALER_MERCHANTDB_PendingReservesCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupPendingReservesContext lrc = { - .pg = pg, - .cb = cb, - .cb_cls = cb_cls - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_pending_reserves", - params, - &lookup_pending_reserves_cb, - &lrc); - if (lrc.qs < 0) - return lrc.qs; - return qs; -} - - -/** - * Closure for #lookup_reserve_tips_cb(). - */ -struct LookupTipsContext -{ - /** - * Postgres context. - */ - struct PostgresClosure *pg; - - /** - * Array with information about tips generated from this reserve. - */ - struct TALER_MERCHANTDB_TipDetails *tips; - - /** - * Length of the @e tips array. - */ - unsigned int tips_length; - - /** - * Set in case of errors. - */ - enum GNUNET_DB_QueryStatus qs; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param[in,out] cls of type `struct LookupTipsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_reserve_tips_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTipsContext *ltc = cls; - struct PostgresClosure *pg = ltc->pg; - - GNUNET_array_grow (ltc->tips, - ltc->tips_length, - num_results); - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_MERCHANTDB_TipDetails *td = <c->tips[i]; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("justification", - &td->reason), - GNUNET_PQ_result_spec_auto_from_type ("tip_id", - &td->tip_id), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - &td->total_amount), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ltc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - } -} - - -/** - * Lookup reserve details. - * - * @param cls closure - * @param instance_id instance to lookup payments for - * @param reserve_pub public key of the reserve to inspect - * @param fetch_tips if true, also return information about tips - * @param cb function to call with reserve summary data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_reserve (void *cls, - const char *instance_id, - const struct TALER_ReservePublicKeyP *reserve_pub, - bool fetch_tips, - TALER_MERCHANTDB_ReserveDetailsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupTipsContext ltc = { - .pg = pg, - .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_TIME_Timestamp creation_time; - struct GNUNET_TIME_Timestamp expiration_time; - struct TALER_Amount merchant_initial_balance; - struct TALER_Amount exchange_initial_balance; - struct TALER_Amount pickup_amount; - struct TALER_Amount committed_amount; - uint8_t active; - char *exchange_url = NULL; - char *payto_uri = NULL; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("creation_time", - &creation_time), - GNUNET_PQ_result_spec_timestamp ("expiration", - &expiration_time), - TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance", - &merchant_initial_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance", - &exchange_initial_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up", - &pickup_amount), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed", - &committed_amount), - GNUNET_PQ_result_spec_auto_from_type ("active", - &active), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("exchange_url", - &exchange_url), - NULL), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("payto_uri", - &payto_uri), - NULL), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_reserve", - params, - rs); - if (qs < 0) - return qs; - if (! fetch_tips) - { - cb (cb_cls, - creation_time, - expiration_time, - &merchant_initial_balance, - &exchange_initial_balance, - &pickup_amount, - &committed_amount, - (0 != active), - exchange_url, - payto_uri, - 0, - NULL); - GNUNET_PQ_cleanup_result (rs); - return qs; - } - - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_reserve_tips", - params, - &lookup_reserve_tips_cb, - <c); - if (qs < 0) - return qs; - if (ltc.qs >= 0) - { - cb (cb_cls, - creation_time, - expiration_time, - &merchant_initial_balance, - &exchange_initial_balance, - &pickup_amount, - &committed_amount, - 0 != active, - exchange_url, - payto_uri, - ltc.tips_length, - ltc.tips); - } - for (unsigned int i = 0; i<ltc.tips_length; i++) - GNUNET_free (ltc.tips[i].reason); - GNUNET_array_grow (ltc.tips, - ltc.tips_length, - 0); - GNUNET_PQ_cleanup_result (rs); - return ltc.qs; -} - - -/** - * Delete a reserve's private key. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance is the reserve tied to - * @param reserve_pub which reserve is to be deleted - * @return transaction status, usually - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_reserve (void *cls, - const char *instance_id, - const struct TALER_ReservePublicKeyP *reserve_pub) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_reserve", - params); -} - - -/** - * Purge all of the information about a reserve, including tips. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance is the reserve tied to - * @param reserve_pub which reserve is to be purged - * @return transaction status, usually - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success - */ -static enum GNUNET_DB_QueryStatus -postgres_purge_reserve (void *cls, - const char *instance_id, - const struct TALER_ReservePublicKeyP *reserve_pub) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "purge_reserve", - params); -} - - -/** - * Closure for #lookup_reserve_for_tip_cb(). - */ -struct LookupReserveForTipContext -{ - /** - * Postgres context. - */ - struct PostgresClosure *pg; - - /** - * Public key of the reserve we found. - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * How much money must be left in the reserve. - */ - struct TALER_Amount required_amount; - - /** - * Set to the expiration time of the reserve we found. - * #GNUNET_TIME_UNIT_FOREVER_ABS if we found none. - */ - struct GNUNET_TIME_Timestamp expiration; - - /** - * Error status. - */ - enum TALER_ErrorCode ec; - - /** - * Did we find a good reserve? - */ - bool ok; -}; - - -/** - * How long must a reserve be at least still valid before we use - * it for a tip? - */ -#define MIN_EXPIRATION GNUNET_TIME_UNIT_HOURS - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param[in,out] cls of type `struct LookupReserveForTipContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_reserve_for_tip_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupReserveForTipContext *lac = cls; - struct PostgresClosure *pg = lac->pg; - - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount committed_amount; - struct TALER_Amount remaining; - struct TALER_Amount initial_balance; - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", - &reserve_pub), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance", - &initial_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed", - &committed_amount), - GNUNET_PQ_result_spec_timestamp ("expiration", - &expiration), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lac->ec = TALER_EC_GENERIC_DB_FETCH_FAILED; - return; - } - if (0 > - TALER_amount_subtract (&remaining, - &initial_balance, - &committed_amount)) - { - GNUNET_break (0); - continue; - } - if (0 > - TALER_amount_cmp (&remaining, - &lac->required_amount)) - { - /* insufficient balance */ - if (lac->ok) - continue; /* got another reserve */ - lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS; - continue; - } - if ( (! GNUNET_TIME_absolute_is_never (lac->expiration.abs_time)) && - GNUNET_TIME_timestamp_cmp (expiration, >, lac->expiration) && - GNUNET_TIME_relative_cmp ( - GNUNET_TIME_absolute_get_remaining (lac->expiration.abs_time), - >, - MIN_EXPIRATION) ) - { - /* reserve expired */ - if (lac->ok) - continue; /* got another reserve */ - lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED; - continue; - } - lac->ok = true; - lac->ec = TALER_EC_NONE; - lac->expiration = expiration; - lac->reserve_pub = reserve_pub; - } -} - - -/** - * Authorize a tip over @a amount from reserve @a reserve_pub. Remember - * the authorization under @a tip_id for later, together with the - * @a justification. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance should generate the tip - * @param reserve_pub which reserve is debited, NULL to pick one in the DB - * @param amount how high is the tip (with fees) - * @param justification why was the tip approved - * @param next_url where to send the URL post tip pickup - * @param[out] tip_id set to the unique ID for the tip - * @param[out] expiration set to when the tip expires - * @return transaction status, - * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired - * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND if the reserve is not known - * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left - * #TALER_EC_GENERIC_DB_START_FAILED on hard DB errors - * #TALER_EC_GENERIC_DB_FETCH_FAILED on hard DB errors - * #TALER_EC_GENERIC_DB_STORE_FAILED on hard DB errors - * #TALER_EC_GENERIC_DB_INVARIANT_FAILURE on hard DB errors - * #TALER_EC_GENERIC_DB_SOFT_FAILURE soft DB errors (client should retry) - * #TALER_EC_NONE upon success - */ -static enum TALER_ErrorCode -postgres_authorize_tip (void *cls, - const char *instance_id, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_Amount *amount, - const char *justification, - const char *next_url, - struct TALER_TipIdentifierP *tip_id, - struct GNUNET_TIME_Timestamp *expiration) -{ - struct PostgresClosure *pg = cls; - unsigned int retries = 0; - enum GNUNET_DB_QueryStatus qs; - struct TALER_Amount tips_committed; - struct TALER_Amount exchange_initial_balance; - const struct TALER_ReservePublicKeyP *reserve_pubp; - struct LookupReserveForTipContext lac = { - .pg = pg, - .required_amount = *amount, - .expiration = GNUNET_TIME_UNIT_FOREVER_TS - }; - - check_connection (pg); -RETRY: - reserve_pubp = reserve_pub; - if (MAX_RETRIES < ++retries) - { - GNUNET_break (0); - return - TALER_EC_GENERIC_DB_SOFT_FAILURE; - } - if (GNUNET_OK != - postgres_start (pg, - "authorize tip")) - { - GNUNET_break (0); - return TALER_EC_GENERIC_DB_START_FAILED; - } - if (NULL == reserve_pubp) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_reserve_for_tip", - params, - &lookup_reserve_for_tip_cb, - &lac); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - postgres_rollback (pg); - goto RETRY; - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - postgres_rollback (pg); - return TALER_EC_GENERIC_DB_FETCH_FAILED; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - postgres_rollback (pg); - return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - default: - break; - } - if (TALER_EC_NONE != lac.ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Enabling tip reserved failed with status %d\n", - lac.ec); - postgres_rollback (pg); - return lac.ec; - } - GNUNET_assert (lac.ok); - reserve_pubp = &lac.reserve_pub; - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pubp), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration", - expiration), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed", - &tips_committed), - TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance", - &exchange_initial_balance), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_reserve_status", - params, - rs); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - postgres_rollback (pg); - goto RETRY; - } - if (qs < 0) - { - GNUNET_break (0); - postgres_rollback (pg); - return TALER_EC_GENERIC_DB_FETCH_FAILED; - } - if (0 == qs) - { - postgres_rollback (pg); - return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND; - } - } - { - struct TALER_Amount remaining; - - if (0 > - TALER_amount_subtract (&remaining, - &exchange_initial_balance, - &tips_committed)) - { - GNUNET_break (0); - postgres_rollback (pg); - return TALER_EC_GENERIC_DB_INVARIANT_FAILURE; - } - if (0 > - TALER_amount_cmp (&remaining, - amount)) - { - postgres_rollback (pg); - return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS; - } - } - GNUNET_assert (0 <= - TALER_amount_add (&tips_committed, - &tips_committed, - amount)); - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pubp), - TALER_PQ_query_param_amount (&tips_committed), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_reserve_tips_committed", - params); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - postgres_rollback (pg); - goto RETRY; - } - if (qs < 0) - { - GNUNET_break (0); - postgres_rollback (pg); - return TALER_EC_GENERIC_DB_STORE_FAILED; - } - } - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - tip_id, - sizeof (*tip_id)); - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (reserve_pubp), - GNUNET_PQ_query_param_auto_from_type (tip_id), - GNUNET_PQ_query_param_string (justification), - GNUNET_PQ_query_param_string (next_url), - GNUNET_PQ_query_param_timestamp (expiration), - TALER_PQ_query_param_amount (amount), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_tip", - params); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - postgres_rollback (pg); - goto RETRY; - } - if (qs < 0) - { - GNUNET_break (0); - postgres_rollback (pg); - return TALER_EC_GENERIC_DB_STORE_FAILED; - } - } - qs = postgres_commit (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto RETRY; - if (qs < 0) - { - GNUNET_break (0); - postgres_rollback (pg); - return TALER_EC_GENERIC_DB_COMMIT_FAILED; - } - return TALER_EC_NONE; -} - - -/** - * Closure for #lookup_signatures_cb(). - */ -struct LookupSignaturesContext -{ - /** - * Length of the @e sigs array - */ - unsigned int sigs_length; - - /** - * Where to store the signatures. - */ - struct TALER_BlindedDenominationSignature *sigs; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param[in,out] cls of type `struct LookupSignaturesContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_signatures_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupSignaturesContext *lsc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - uint32_t offset; - struct TALER_BlindedDenominationSignature bsig; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint32 ("coin_offset", - &offset), - TALER_PQ_result_spec_blinded_denom_sig ("blind_sig", - &bsig), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - return; - } - if (offset >= lsc->sigs_length) - { - GNUNET_break_op (0); - GNUNET_PQ_cleanup_result (rs); - continue; - } - /* Must be NULL due to UNIQUE constraint on offset and - requirement that client launched us with 'sigs' - pre-initialized to NULL. */ - lsc->sigs[offset] = bsig; - } -} - - -/** - * Lookup pickup details for pickup @a pickup_id. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance should we lookup tip details for - * @param tip_id which tip should we lookup details on - * @param pickup_id which pickup should we lookup details on - * @param[out] exchange_url which exchange is the tip withdrawn from - * @param[out] reserve_priv private key the tip is withdrawn from (set if still available!) - * @param sigs_length length of the @a sigs array - * @param[out] sigs set to the (blind) signatures we have for this @a pickup_id, - * those that are unavailable are left at NULL - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_pickup (void *cls, - const char *instance_id, - const struct TALER_TipIdentifierP *tip_id, - const struct TALER_PickupIdentifierP *pickup_id, - char **exchange_url, - struct TALER_ReservePrivateKeyP *reserve_priv, - unsigned int sigs_length, - struct TALER_BlindedDenominationSignature sigs[]) -{ - struct PostgresClosure *pg = cls; - uint64_t pickup_serial; - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (tip_id), - GNUNET_PQ_query_param_auto_from_type (pickup_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("exchange_url", - exchange_url), - GNUNET_PQ_result_spec_auto_from_type ("reserve_priv", - reserve_priv), - GNUNET_PQ_result_spec_uint64 ("pickup_serial", - &pickup_serial), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_pickup", - params, - rs); - if (qs <= 0) - return qs; - } - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&pickup_serial), - GNUNET_PQ_query_param_end - }; - struct LookupSignaturesContext lsc = { - .sigs_length = sigs_length, - .sigs = sigs - }; - - return GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_pickup_signatures", - params, - &lookup_signatures_cb, - &lsc); - } -} - - -/** - * Lookup tip details for tip @a tip_id. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance should we lookup tip details for - * @param tip_id which tip should we lookup details on - * @param[out] total_authorized amount how high is the tip (with fees) - * @param[out] total_picked_up how much of the tip was so far picked up (with fees) - * @param[out] expiration set to when the tip expires - * @param[out] exchange_url set to the exchange URL where the reserve is - * @param[out] reserve_priv set to private key of reserve to be debited - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_tip (void *cls, - const char *instance_id, - const struct TALER_TipIdentifierP *tip_id, - struct TALER_Amount *total_authorized, - struct TALER_Amount *total_picked_up, - struct GNUNET_TIME_Timestamp *expiration, - char **exchange_url, - struct TALER_ReservePrivateKeyP *reserve_priv) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (tip_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - total_authorized), - TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up", - total_picked_up), - GNUNET_PQ_result_spec_timestamp ("expiration", - expiration), - GNUNET_PQ_result_spec_string ("exchange_url", - exchange_url), - GNUNET_PQ_result_spec_auto_from_type ("reserve_priv", - reserve_priv), - GNUNET_PQ_result_spec_end - }; - - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_tip", - params, - rs); -} - - -/** - * Context used for postgres_lookup_tips(). - */ -struct LookupMerchantTipsContext -{ - /** - * Postgres context. - */ - struct PostgresClosure *pg; - - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_TipsCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Internal result. - */ - enum GNUNET_DB_QueryStatus qs; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about tips. - * - * @param[in,out] cls of type `struct LookupTipsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_tips_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupMerchantTipsContext *plc = cls; - struct PostgresClosure *pg = plc->pg; - - for (unsigned int i = 0; i < num_results; i++) - { - uint64_t row_id; - struct TALER_TipIdentifierP tip_id; - struct TALER_Amount tip_amount; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("tip_serial", - &row_id), - GNUNET_PQ_result_spec_auto_from_type ("tip_id", - &tip_id), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - &tip_amount), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - plc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - plc->cb (plc->cb_cls, - row_id, - tip_id, - tip_amount); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup tips - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance should we lookup tips for - * @param expired should we include expired tips? - * @param limit maximum number of results to return, positive for - * ascending row id, negative for descending - * @param offset row id to start returning results from - * @param cb function to call with tip data - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_tips (void *cls, - const char *instance_id, - enum TALER_EXCHANGE_YesNoAll expired, - int64_t limit, - uint64_t offset, - TALER_MERCHANTDB_TipsCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupMerchantTipsContext plc = { - .pg = pg, - .cb = cb, - .cb_cls = cb_cls - }; - uint64_t ulimit = (limit > 0) ? limit : -limit; - uint8_t bexpired; - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&ulimit), - GNUNET_PQ_query_param_uint64 (&offset), - GNUNET_PQ_query_param_absolute_time (&now), - GNUNET_PQ_query_param_auto_from_type (&bexpired), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - char stmt[128]; - - bexpired = (TALER_EXCHANGE_YNA_YES == expired); - GNUNET_snprintf (stmt, - sizeof (stmt), - "lookup_tips_%s%s", - (limit > 0) ? "inc" : "dec", - (TALER_EXCHANGE_YNA_ALL == expired) ? "" : "_expired"); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, - params, - &lookup_tips_cb, - &plc); - if (0 != plc.qs) - return plc.qs; - return qs; -} - - -/** - * Closure for #lookup_pickup_details_cb(). - */ -struct LookupTipDetailsContext -{ - /** - * Length of the @e sigs array - */ - unsigned int *pickups_length; - - /** - * Where to store the signatures. - */ - struct TALER_MERCHANTDB_PickupDetails **pickups; - - /** - * Database handle. - */ - struct PostgresClosure *pg; - - /** - * Transaction status. - */ - enum GNUNET_DB_QueryStatus qs; - -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about pickups. - * - * @param[in,out] cls of type `struct LookupTipDetailsContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_pickup_details_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTipDetailsContext *ltdc = cls; - struct PostgresClosure *pg = ltdc->pg; - - *ltdc->pickups_length = num_results; - *ltdc->pickups = GNUNET_new_array (num_results, - struct TALER_MERCHANTDB_PickupDetails); - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_MERCHANTDB_PickupDetails *pd = &((*ltdc->pickups)[i]); - uint64_t num_planchets = 0; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("pickup_id", - &pd->pickup_id), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - &pd->requested_amount), - GNUNET_PQ_result_spec_uint64 ("num_planchets", - &num_planchets), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR; - GNUNET_array_grow (*ltdc->pickups, - *ltdc->pickups_length, - 0); - return; - } - - pd->num_planchets = num_planchets; - } -} - - -/** - * Lookup tip details for tip @a tip_id. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance should we lookup tip details for - * @param tip_id which tip should we lookup details on - * @param fpu should we fetch details about individual pickups - * @param[out] total_authorized amount how high is the tip (with fees) - * @param[out] total_picked_up how much of the tip was so far picked up (with fees) - * @param[out] justification why was the tip approved - * @param[out] expiration set to when the tip expires - * @param[out] reserve_pub set to which reserve is debited - * @param[out] pickups_length set to the length of @e pickups - * @param[out] pickups if @a fpu is true, set to details about the pickup operations - * @return transaction status, - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_tip_details (void *cls, - const char *instance_id, - const struct TALER_TipIdentifierP *tip_id, - bool fpu, - struct TALER_Amount *total_authorized, - struct TALER_Amount *total_picked_up, - char **justification, - struct GNUNET_TIME_Timestamp *expiration, - struct TALER_ReservePublicKeyP *reserve_pub, - unsigned int *pickups_length, - struct TALER_MERCHANTDB_PickupDetails **pickups) -{ - struct PostgresClosure *pg = cls; - uint64_t tip_serial; - enum GNUNET_DB_QueryStatus qs; - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (tip_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("tip_serial", - &tip_serial), - TALER_PQ_RESULT_SPEC_AMOUNT ("amount", - total_authorized), - TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up", - total_picked_up), - GNUNET_PQ_result_spec_string ("justification", - justification), - GNUNET_PQ_result_spec_timestamp ("expiration", - expiration), - GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", - reserve_pub), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_tip_details", - params, - rs); - if (qs <= 0) - return qs; - if (! fpu) - { - *pickups_length = 0; - *pickups = NULL; - return qs; - } - } - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&tip_serial), - GNUNET_PQ_query_param_end - }; - - struct LookupTipDetailsContext ltdc = { - .pickups_length = pickups_length, - .pickups = pickups, - .pg = pg, - .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT - }; - - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_pickup_details", - params, - &lookup_pickup_details_cb, - <dc); - if (qs < 0) - return qs; - return ltdc.qs; - } -} - - -/** - * Insert details about a tip pickup operation. The @a total_picked_up - * UPDATES the total amount under the @a tip_id, while the @a - * total_requested is the amount to be associated with this @a pickup_id. - * While there is usually only one pickup event that picks up the entire - * amount, our schema allows for wallets to pick up the amount incrementally - * over multiple pick up operations. - * - * @param cls closure, typically a connection to the db - * @param instance_id which instance gave the tip - * @param tip_id the unique ID for the tip - * @param total_picked_up how much was picked up overall at this - * point (includes @a total_requested) - * @param pickup_id unique ID for the operation - * @param total_requested how much is being picked up in this operation - * @return transaction status, usually - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_pickup (void *cls, - const char *instance_id, - const struct TALER_TipIdentifierP *tip_id, - const struct TALER_Amount *total_picked_up, - const struct TALER_PickupIdentifierP *pickup_id, - const struct TALER_Amount *total_requested) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (tip_id), - GNUNET_PQ_query_param_auto_from_type (pickup_id), - TALER_PQ_query_param_amount (total_requested), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_pickup", - params); - if (0 > qs) - return qs; - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (tip_id), - TALER_PQ_query_param_amount (total_picked_up), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_picked_up_tip", - params); - if (0 > qs) - return qs; - } - { - uint64_t reserve_serial; - struct TALER_Amount reserve_picked_up; - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_auto_from_type (tip_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("reserve_serial", - &reserve_serial), - TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up", - &reserve_picked_up), - GNUNET_PQ_result_spec_end - - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_picked_up_reserve", - params, - rs); - if (0 > qs) - return qs; - } - if (0 >= - TALER_amount_add (&reserve_picked_up, - &reserve_picked_up, - total_requested)) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&reserve_serial), - TALER_PQ_query_param_amount (&reserve_picked_up), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_picked_up_reserve", - params); - if (0 > qs) - return qs; - } - } - return qs; -} - - -/** - * Insert blind signature obtained from the exchange during a - * tip pickup operation. - * - * @param cls closure, typically a connection to the db - * @param pickup_id unique ID for the operation - * @param offset offset of the blind signature for the pickup - * @param blind_sig the blind signature - * @return transaction status, usually - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_pickup_blind_signature ( - void *cls, - const struct TALER_PickupIdentifierP *pickup_id, - uint32_t offset, - const struct TALER_BlindedDenominationSignature *blind_sig) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (pickup_id), - GNUNET_PQ_query_param_uint32 (&offset), - TALER_PQ_query_param_blinded_denom_sig (blind_sig), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_pickup_blind_signature", - params); -} - - -/** - * Delete information about a template. - * - * @param cls closure - * @param instance_id instance to delete template of - * @param template_id template to delete - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS - * if template unknown. - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_template (void *cls, - const char *instance_id, - const char *template_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_template", - params); -} - - -/** - * Insert details about a particular template. - * - * @param cls closure - * @param instance_id instance to insert template for - * @param template_id template identifier of template to insert - * @param td the template details to insert - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_template (void *cls, - const char *instance_id, - const char *template_id, - const struct TALER_MERCHANTDB_TemplateDetails *td) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_string (td->template_description), - (NULL == td->image) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (td->image), - TALER_PQ_query_param_json (td->template_contract), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_template", - params); -} - - -/** - * Update details about a particular template. - * - * @param cls closure - * @param instance_id instance to update template for - * @param template_id template to update - * @param td update to the template details on success, can be NULL - * (in that case we only want to check if the template exists) - * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template - * does not yet exist. - */ -static enum GNUNET_DB_QueryStatus -postgres_update_template (void *cls, - const char *instance_id, - const char *template_id, - const struct TALER_MERCHANTDB_TemplateDetails *td) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_string (td->template_description), - (NULL == td->image) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (td->image), - TALER_PQ_query_param_json (td->template_contract), - GNUNET_PQ_query_param_end - }; - - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_template", - params); -} - - -/** - * Context used for postgres_lookup_template(). - */ -struct LookupTemplateContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_TemplatesCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about template. - * - * @param[in,out] cls of type `struct LookupTemplateContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_templates_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTemplateContext *tlc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - char *template_id; - char *template_description; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("template_id", - &template_id), - GNUNET_PQ_result_spec_string ("template_description", - &template_description), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - tlc->extract_failed = true; - return; - } - tlc->cb (tlc->cb_cls, - template_id, - template_description); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup all of the templates the given instance has configured. - * - * @param cls closure - * @param instance_id instance to lookup template for - * @param cb function to call on all template found - * @param cb_cls closure for @a cb - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_templates (void *cls, - const char *instance_id, - TALER_MERCHANTDB_TemplatesCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupTemplateContext tlc = { - .cb = cb, - .cb_cls = cb_cls, - /* Can be overwritten by the lookup_template_cb */ - .extract_failed = false, - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_templates", - params, - &lookup_templates_cb, - &tlc); - /* If there was an error inside lookup_template_cb, return a hard error. */ - if (tlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Lookup details about a particular template. - * - * @param cls closure - * @param instance_id instance to lookup template for - * @param template_id template to lookup - * @param[out] td set to the template details on success, can be NULL - * (in that case we only want to check if the template exists) - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_template (void *cls, - const char *instance_id, - const char *template_id, - struct TALER_MERCHANTDB_TemplateDetails *td) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_end - }; - - if (NULL == td) - { - struct GNUNET_PQ_ResultSpec rs_null[] = { - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_template", - params, - rs_null); - } - else - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("template_description", - &td->template_description), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("image", - &td->image), - NULL), - TALER_PQ_result_spec_json ("template_contract", - &td->template_contract), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - td->image = NULL; - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_template", - params, - rs); - } -} - - -/** - * Delete information about a webhook. - * - * @param cls closure - * @param instance_id instance to delete webhook of - * @param webhook_id webhook to delete - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS - * if webhook unknown. - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_webhook (void *cls, - const char *instance_id, - const char *webhook_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (webhook_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_webhook", - params); -} - - -/** - * Insert details about a particular webhook. - * - * @param cls closure - * @param instance_id instance to insert webhook for - * @param webhook_id webhook identifier of webhook to insert - * @param wb the webhook details to insert - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_webhook (void *cls, - const char *instance_id, - const char *webhook_id, - const struct TALER_MERCHANTDB_WebhookDetails *wb) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (webhook_id), - GNUNET_PQ_query_param_string (wb->event_type), - GNUNET_PQ_query_param_string (wb->url), - GNUNET_PQ_query_param_string (wb->http_method), - GNUNET_PQ_query_param_string (wb->header_template), - GNUNET_PQ_query_param_string (wb->body_template), - GNUNET_PQ_query_param_end - - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_webhook", - params); -} - - -/** - * Update details about a particular webhook. - * - * @param cls closure - * @param instance_id instance to update template for - * @param webhook_id webhook to update - * @param wb update to the webhook details on success, can be NULL - * (in that case we only want to check if the webhook exists) - * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the webhook - * does not yet exist. - */ -static enum GNUNET_DB_QueryStatus -postgres_update_webhook (void *cls, - const char *instance_id, - const char *webhook_id, - const struct TALER_MERCHANTDB_WebhookDetails *wb) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (webhook_id), - GNUNET_PQ_query_param_string (wb->event_type), - GNUNET_PQ_query_param_string (wb->url), - GNUNET_PQ_query_param_string (wb->http_method), - GNUNET_PQ_query_param_string (wb->header_template), - GNUNET_PQ_query_param_string (wb->body_template), - GNUNET_PQ_query_param_end - }; - - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_webhook", - params); -} - - -/** - * Context used for postgres_lookup_webhook(). - */ -struct LookupWebhookContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_WebhooksCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about webhook. - * - * @param[in,out] cls of type `struct LookupWebhookContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_webhooks_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupWebhookContext *wlc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - char *webhook_id; - char *event_type; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("webhook_id", - &webhook_id), - GNUNET_PQ_result_spec_string ("event_type", - &event_type), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - wlc->extract_failed = true; - return; - } - wlc->cb (wlc->cb_cls, - webhook_id, - event_type); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup all of the webhooks the given instance has configured. - * - * @param cls closure - * @param instance_id instance to lookup webhook for - * @param cb function to call on all webhook found - * @param cb_cls closure for @a cb - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_webhooks (void *cls, - const char *instance_id, - TALER_MERCHANTDB_WebhooksCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupWebhookContext wlc = { - .cb = cb, - .cb_cls = cb_cls, - /* Can be overwritten by the lookup_webhook_cb */ - .extract_failed = false, - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_webhooks", - params, - &lookup_webhooks_cb, - &wlc); - /* If there was an error inside lookup_webhook_cb, return a hard error. */ - if (wlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Lookup details about a particular webhook. - * - * @param cls closure - * @param instance_id instance to lookup webhook for - * @param webhook_id webhook to lookup - * @param[out] wb set to the webhook details on success, can be NULL - * (in that case we only want to check if the webhook exists) - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_webhook (void *cls, - const char *instance_id, - const char *webhook_id, - struct TALER_MERCHANTDB_WebhookDetails *wb) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (webhook_id), - GNUNET_PQ_query_param_end - }; - - if (NULL == wb) - { - struct GNUNET_PQ_ResultSpec rs_null[] = { - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_webhook", - params, - rs_null); - } - else - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("event_type", - &wb->event_type), - GNUNET_PQ_result_spec_string ("url", - &wb->url), - GNUNET_PQ_result_spec_string ("http_method", - &wb->http_method), - GNUNET_PQ_result_spec_string ("header_template", - &wb->header_template), - GNUNET_PQ_result_spec_string ("body_template", - &wb->body_template), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_webhook", - params, - rs); - } -} - - -/** - * Context used for postgres_lookup_webhook(). - */ -struct LookupWebhookDetailContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_WebhookDetailCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about webhook. - * - * @param[in,out] cls of type `struct LookupPendingWebhookContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_webhook_by_event_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupWebhookDetailContext *wlc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - uint64_t webhook_serial; - char *event_type; - char *url; - char *http_method; - char *header_template; - char *body_template; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("webhook_serial", - &webhook_serial), - GNUNET_PQ_result_spec_string ("event_type", - &event_type), - GNUNET_PQ_result_spec_string ("url", - &url), - GNUNET_PQ_result_spec_string ("http_method", - &http_method), - GNUNET_PQ_result_spec_string ("header_template", - &header_template), - GNUNET_PQ_result_spec_string ("body_template", - &body_template), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - wlc->extract_failed = true; - return; - } - wlc->cb (wlc->cb_cls, - webhook_serial, - event_type, - url, - http_method, - header_template, - body_template); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup webhook by event - * - * @param cls closure - * @param instance_id instance to lookup webhook for - * @param event_type event that we need to put in the pending webhook - * @param[out] cb set to the webhook details on success - * @param cb_cls callback closure - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_webhook_by_event (void *cls, - const char *instance_id, - const char *event_type, - TALER_MERCHANTDB_WebhookDetailCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupWebhookDetailContext wlc = { - .cb = cb, - .cb_cls = cb_cls, - .extract_failed = false, - }; - - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (event_type), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_webhook_by_event", - params, - &lookup_webhook_by_event_cb, - &wlc); - - if (wlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Insert webhook in the pending webhook. - * - * @param cls closure - * @param instance_id instance to insert webhook for - * @param webhook_serial webhook to insert in the pending webhook - * @param url to make the request to - * @param http_method for the webhook - * @param header of the webhook - * @param body of the webhook - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_pending_webhook (void *cls, - const char *instance_id, - uint64_t webhook_serial, - const char *url, - const char *http_method, - const char *header, - const char *body) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&webhook_serial), - GNUNET_PQ_query_param_string (url), - GNUNET_PQ_query_param_string (http_method), - NULL == header - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (header), - NULL == body - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (body), - GNUNET_PQ_query_param_end - }; - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_pending_webhook", - params); -} - - -/** - * Lookup details about a particular pending webhook. - * - * @param cls closure - * @param instance_id instance to lookup webhook for - * @param webhook_serial webhook to lookup - * @param[out] pwb set to the pending webhook details on success, can be NULL - * (in that case we only want to check if the webhook exists) - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_pending_webhook (void *cls, - const char *instance_id, - uint64_t *webhook_serial, - struct TALER_MERCHANTDB_PendingWebhookDetails * - pwb) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (webhook_serial), - GNUNET_PQ_query_param_end - }; - - if (NULL == pwb) - { - struct GNUNET_PQ_ResultSpec rs_null[] = { - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_pending_webhook", - params, - rs_null); - } - else - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_absolute_time ("next_attempt", - &pwb->next_attempt), - GNUNET_PQ_result_spec_uint32 ("retries", - &pwb->retries), - GNUNET_PQ_result_spec_string ("url", - &pwb->url), - GNUNET_PQ_result_spec_string ("http_method", - &pwb->http_method), - GNUNET_PQ_result_spec_string ("header", - &pwb->header), - GNUNET_PQ_result_spec_string ("body", - &pwb->body), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_pending_webhook", - params, - rs); - } -} - - -/** - * Context used for postgres_lookup_future_webhook(). - */ -struct LookupPendingWebhookContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_PendingWebhooksCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about webhook. - * - * @param[in,out] cls of type `struct LookupPendingWebhookContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_pending_webhooks_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupPendingWebhookContext *pwlc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - uint64_t webhook_serial; - struct GNUNET_TIME_Absolute next_attempt; - uint32_t retries; - char *url; - char *http_method; - char *header; - char *body; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("webhook_serial", - &webhook_serial), - GNUNET_PQ_result_spec_absolute_time ("next_attempt", - &next_attempt), - GNUNET_PQ_result_spec_uint32 ("retries", - &retries), - GNUNET_PQ_result_spec_string ("url", - &url), - GNUNET_PQ_result_spec_string ("http_method", - &http_method), - GNUNET_PQ_result_spec_string ("header", - &header), - GNUNET_PQ_result_spec_string ("body", - &body), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - pwlc->extract_failed = true; - return; - } - pwlc->cb (pwlc->cb_cls, - webhook_serial, - next_attempt, - retries, - url, - http_method, - header, - body); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup the webhook that need to be send in priority. - * send. - * - * @param cls closure - * @param cb pending webhook callback - * @param cb_cls callback closure - */ -// WHERE next_attempt <= now ORDER BY next_attempt ASC -static enum GNUNET_DB_QueryStatus -postgres_lookup_pending_webhooks (void *cls, - TALER_MERCHANTDB_PendingWebhooksCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupPendingWebhookContext pwlc = { - .cb = cb, - .cb_cls = cb_cls, - .extract_failed = false, - }; - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_PQ_QueryParam params_null[] = { - GNUNET_PQ_query_param_absolute_time (&now), - GNUNET_PQ_query_param_end - }; - - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_pending_webhooks", - params_null, - &lookup_pending_webhooks_cb, - &pwlc); - - if (pwlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Lookup future webhook in the pending webhook that need to be send. - * With that we can know how long the system can 'sleep'. - * - * @param cls closure - * @param cb pending webhook callback - * @param cb_cls callback closure - */ -// ORDER BY next_attempt ASC LIMIT 1 -static enum GNUNET_DB_QueryStatus -postgres_lookup_future_webhook (void *cls, - TALER_MERCHANTDB_PendingWebhooksCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupPendingWebhookContext pwlc = { - .cb = cb, - .cb_cls = cb_cls, - .extract_failed = false, - }; - struct GNUNET_PQ_QueryParam params_null[] = { - GNUNET_PQ_query_param_end - }; - - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_future_webhook", - params_null, - &lookup_pending_webhooks_cb, - &pwlc); - - if (pwlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Lookup all the webhooks in the pending webhook. - * Use by the administrator - * - * @param cls closure - * @param instance_id to lookup webhooks for this instance particularly - * @param min_row to see the list of the pending webhook that it is started with this minimum row. - * @param max_results to see the list of the pending webhook that it is end with this max results. - * @param cb pending webhook callback - * @param cb_cls callback closure - */ -// WHERE webhook_pending_serial > min_row ORDER BY webhook_pending_serial ASC LIMIT max_results -static enum GNUNET_DB_QueryStatus -postgres_lookup_all_webhooks (void *cls, - const char *instance_id, - uint64_t min_row, - uint32_t max_results, - TALER_MERCHANTDB_PendingWebhooksCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupPendingWebhookContext pwlc = { - .cb = cb, - .cb_cls = cb_cls, - .extract_failed = false, - }; - uint64_t max_results64 = max_results; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_uint64 (&min_row), - GNUNET_PQ_query_param_uint64 (&max_results64), - GNUNET_PQ_query_param_end - }; - - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_all_webhooks", - params, - &lookup_pending_webhooks_cb, - &pwlc); - - if (pwlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Update the pending webhook. It is use if the webhook can't be send. - * - * @param cls closure - * @param webhook_serial webhook that need to be update - * @param next_attempt when to try the webhook next - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_update_pending_webhook (void *cls, - uint64_t webhook_serial, - struct GNUNET_TIME_Absolute next_attempt) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&webhook_serial), - GNUNET_PQ_query_param_absolute_time (&next_attempt), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_pending_webhook", - params); -} - - -/** - * Delete a webhook in the pending webhook after the - * webhook was completed successfully. - * - * @param cls closure - * @param webhook_serial webhook that need to be delete in the pending webhook - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_pending_webhook (void *cls, - uint64_t webhook_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&webhook_serial), - GNUNET_PQ_query_param_end - }; - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_pending_webhook", - params); -} - - -/** * Establish connection to the database. * * @param cls plugin context * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ -static int +static enum GNUNET_GenericReturnValue postgres_connect (void *cls) { struct PostgresClosure *pg = cls; - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("end_transaction", - "COMMIT"), - /* for call_with_accounts(), part of postgres_lookup_instances() */ - GNUNET_PQ_make_prepare ("lookup_instance_private_key", - "SELECT" - " merchant_priv" - " FROM merchant_keys" - " WHERE merchant_serial=$1"), - /* for find_instances_cb(), part of postgres_lookup_instances() */ - GNUNET_PQ_make_prepare ("lookup_accounts", - "SELECT" - " h_wire" - ",salt" - ",payto_uri" - ",active" - " FROM merchant_accounts" - " WHERE merchant_serial=$1"), - /* for postgres_lookup_instances() */ - GNUNET_PQ_make_prepare ("lookup_instance_auth", - "SELECT" - " auth_hash" - ",auth_salt" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_lookup_instances() */ - GNUNET_PQ_make_prepare ("lookup_instances", - "SELECT" - " merchant_serial" - ",merchant_pub" - ",auth_hash" - ",auth_salt" - ",merchant_id" - ",merchant_name" - ",address" - ",jurisdiction" - ",default_max_deposit_fee_val" - ",default_max_deposit_fee_frac" - ",default_max_wire_fee_val" - ",default_max_wire_fee_frac" - ",default_wire_fee_amortization" - ",default_wire_transfer_delay" - ",default_pay_delay" - ",website" - ",email" - ",logo" - " FROM merchant_instances"), - /* for postgres_lookup_instance() */ - GNUNET_PQ_make_prepare ("lookup_instance", - "SELECT" - " merchant_serial" - ",merchant_pub" - ",auth_hash" - ",auth_salt" - ",merchant_id" - ",merchant_name" - ",address" - ",jurisdiction" - ",default_max_deposit_fee_val" - ",default_max_deposit_fee_frac" - ",default_max_wire_fee_val" - ",default_max_wire_fee_frac" - ",default_wire_fee_amortization" - ",default_wire_transfer_delay" - ",default_pay_delay" - ",website" - ",email" - ",logo" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_insert_instance() */ - GNUNET_PQ_make_prepare ("insert_instance", - "INSERT INTO merchant_instances" - "(merchant_pub" - ",auth_hash" - ",auth_salt" - ",merchant_id" - ",merchant_name" - ",address" - ",jurisdiction" - ",default_max_deposit_fee_val" - ",default_max_deposit_fee_frac" - ",default_max_wire_fee_val" - ",default_max_wire_fee_frac" - ",default_wire_fee_amortization" - ",default_wire_transfer_delay" - ",default_pay_delay" - ",website" - ",email" - ",logo)" - "VALUES" - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)"), - /* for postgres_insert_instance() */ - GNUNET_PQ_make_prepare ("insert_keys", - "INSERT INTO merchant_keys" - "(merchant_priv" - ",merchant_serial)" - " SELECT $1, merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$2"), - /* for postgres_account_kyc_set_status */ - GNUNET_PQ_make_prepare ("upsert_account_kyc", - "INSERT INTO merchant_kyc" - "(kyc_timestamp" - ",kyc_ok" - ",exchange_kyc_serial" - ",account_serial" - ",exchange_url" - ",exchange_pub" - ",exchange_sig)" - " SELECT $5, $6, $4, account_serial, $3, $7, $8" - " FROM merchant_instances" - " JOIN merchant_accounts USING (merchant_serial)" - " WHERE merchant_id=$1" - " AND h_wire=$2" - " ON CONFLICT(account_serial,exchange_url) DO " - "UPDATE" - " SET exchange_kyc_serial=$4" - " ,kyc_timestamp=$5" - " ,kyc_ok=$6" - " ,exchange_pub=$7" - " ,exchange_sig=$8"), - /* for postgres_account_kyc_get_status */ - GNUNET_PQ_make_prepare ("lookup_kyc_status", - "SELECT" - " h_wire" - ",exchange_kyc_serial" - ",payto_uri" - ",exchange_url" - ",kyc_timestamp" - ",kyc_ok" - " FROM merchant_instances" - " JOIN merchant_accounts" - " USING (merchant_serial)" - " JOIN merchant_kyc" - " USING (account_serial)" - " WHERE merchant_instances.merchant_id=$1"), - /* for postgres_insert_account() */ - GNUNET_PQ_make_prepare ("insert_account", - "INSERT INTO merchant_accounts" - "(merchant_serial" - ",h_wire" - ",salt" - ",payto_uri" - ",active)" - " SELECT merchant_serial, $2, $3, $4, $5" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_delete_instance_private_key() */ - GNUNET_PQ_make_prepare ("delete_key", - "DELETE FROM merchant_keys" - " USING merchant_instances" - " WHERE merchant_keys.merchant_serial" - " = merchant_instances.merchant_serial" - " AND merchant_instances.merchant_id = $1"), - /* for postgres_purge_instance() */ - GNUNET_PQ_make_prepare ("purge_instance", - "DELETE FROM merchant_instances" - " WHERE merchant_instances.merchant_id = $1"), - /* for postgres_update_instance() */ - GNUNET_PQ_make_prepare ("update_instance", - "UPDATE merchant_instances SET" - " merchant_name=$2" - ",address=$3" - ",jurisdiction=$4" - ",default_max_deposit_fee_val=$5" - ",default_max_deposit_fee_frac=$6" - ",default_max_wire_fee_val=$7" - ",default_max_wire_fee_frac=$8" - ",default_wire_fee_amortization=$9" - ",default_wire_transfer_delay=$10" - ",default_pay_delay=$11" - ",website=$12" - ",email=$13" - ",logo=$14" - " WHERE merchant_id = $1"), - /* for postgres_update_instance_auth() */ - GNUNET_PQ_make_prepare ("update_instance_auth", - "UPDATE merchant_instances SET" - " auth_hash=$2" - ",auth_salt=$3" - " WHERE merchant_id=$1"), - /* for postgres_inactivate_account(); the merchant - instance is implied from the random salt that - is part of the h_wire calculation */ - GNUNET_PQ_make_prepare ("inactivate_account", - "UPDATE merchant_accounts SET" - " active=FALSE" - " WHERE h_wire=$2 AND" - " merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_activate_account() */ - GNUNET_PQ_make_prepare ("activate_account", - "UPDATE merchant_accounts SET" - " active=TRUE" - " WHERE h_wire=$2 AND" - " merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_lookup_products() */ - GNUNET_PQ_make_prepare ("lookup_products", - "SELECT" - " product_id" - " FROM merchant_inventory" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1"), - /* for postgres_lookup_product() */ - GNUNET_PQ_make_prepare ("lookup_product", - "SELECT" - " description" - ",description_i18n" - ",unit" - ",price_val" - ",price_frac" - ",taxes" - ",total_stock" - ",total_sold" - ",total_lost" - ",image" - ",merchant_inventory.address" - ",next_restock" - ",minimum_age" - " FROM merchant_inventory" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND merchant_inventory.product_id=$2"), - /* for postgres_delete_product() */ - GNUNET_PQ_make_prepare ("delete_product", - "DELETE" - " FROM merchant_inventory" - " WHERE merchant_inventory.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_inventory.product_id=$2" - " AND product_serial NOT IN " - " (SELECT product_serial FROM merchant_order_locks)" - " AND product_serial NOT IN " - " (SELECT product_serial FROM merchant_inventory_locks)"), - /* for postgres_insert_product() */ - GNUNET_PQ_make_prepare ("insert_product", - "INSERT INTO merchant_inventory" - "(merchant_serial" - ",product_id" - ",description" - ",description_i18n" - ",unit" - ",image" - ",taxes" - ",price_val" - ",price_frac" - ",total_stock" - ",address" - ",next_restock" - ",minimum_age" - ")" - " SELECT merchant_serial," - " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_update_product() */ - GNUNET_PQ_make_prepare ("update_product", - "UPDATE merchant_inventory SET" - " description=$3" - ",description_i18n=$4" - ",unit=$5" - ",image=$6" - ",taxes=$7" - ",price_val=$8" - ",price_frac=$9" - ",total_stock=$10" - ",total_lost=$11" - ",address=$12" - ",next_restock=$13" - ",minimum_age=$14" - " WHERE merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND product_id=$2" - " AND total_stock <= $10" - " AND total_lost <= $11"), - - /* for postgres_lock_product() */ - GNUNET_PQ_make_prepare ("lock_product", - "WITH ps AS" - " (SELECT product_serial" - " FROM merchant_inventory" - " WHERE product_id=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))" - "INSERT INTO merchant_inventory_locks" - "(product_serial" - ",lock_uuid" - ",total_locked" - ",expiration)" - " SELECT product_serial, $3, $4, $5" - " FROM merchant_inventory" - " JOIN ps USING (product_serial)" - " WHERE " - " total_stock - total_sold - total_lost - $4 >= " - " (SELECT COALESCE(SUM(total_locked), 0)" - " FROM merchant_inventory_locks" - " WHERE product_serial=ps.product_serial) + " - " (SELECT COALESCE(SUM(total_locked), 0)" - " FROM merchant_order_locks" - " WHERE product_serial=ps.product_serial)"), - - /* for postgres_expire_locks() */ - GNUNET_PQ_make_prepare ("unlock_products", - "DELETE FROM merchant_inventory_locks" - " WHERE expiration < $1"), - /* for postgres_expire_locks() */ - GNUNET_PQ_make_prepare ("unlock_orders", - "DELETE FROM merchant_orders" - " WHERE pay_deadline < $1"), - /* for postgres_expire_locks() */ - GNUNET_PQ_make_prepare ("unlock_contracts", - "DELETE FROM merchant_contract_terms" - " WHERE NOT paid" - " AND pay_deadline < $1"), - - /* for postgres_delete_order() */ - GNUNET_PQ_make_prepare ("delete_order", - "WITH ms AS" - "(SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - ", mc AS" - "(SELECT paid" - " FROM merchant_contract_terms" - " JOIN ms USING (merchant_serial)" - " WHERE order_id=$2) " - "DELETE" - " FROM merchant_orders mo" - " WHERE order_id=$2" - " AND merchant_serial=(SELECT merchant_serial FROM ms)" - " AND ( (pay_deadline < $3)" - " OR (NOT EXISTS (SELECT paid FROM mc))" - " OR ($4 AND (FALSE=(SELECT paid FROM mc))) );"), - GNUNET_PQ_make_prepare ("delete_contract", - "DELETE" - " FROM merchant_contract_terms" - " WHERE order_id=$2 AND" - " merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND NOT paid;"), - /* for postgres_lookup_order() */ - GNUNET_PQ_make_prepare ("lookup_order", - "SELECT" - " contract_terms" - ",claim_token" - ",h_post_data" - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_orders.order_id=$2"), - /* for postgres_lookup_order_summary() */ - GNUNET_PQ_make_prepare ("lookup_order_summary", - "(SELECT" - " creation_time" - ",order_serial" - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_contract_terms.order_id=$2)" - "UNION" - "(SELECT" - " creation_time" - ",order_serial" - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_orders.order_id=$2)"), - /* for postgres_lookup_orders() */ - GNUNET_PQ_make_prepare ("lookup_orders_inc", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_paid", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " BOOL($5) = paid" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " CAST($6 as BOOL) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " BOOL($5) = paid" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " BOOL($5) = paid" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial ASC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial > $3" - " AND" - " creation_time > $4" - " AND" - " BOOL($5) = paid" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial ASC" - " LIMIT $2)" - " ORDER BY order_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_paid", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($5) = paid" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($5) = paid" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($5) = paid" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded_wired", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_orders" - " WHERE merchant_orders.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */ - " AND" - " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */ - " AND" - " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " ORDER BY order_serial DESC" - " LIMIT $2)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_contract_terms" - " WHERE merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " order_serial < $3" - " AND" - " creation_time < $4" - " AND" - " BOOL($5) = paid" - " AND" - " BOOL($6) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))" - " AND" - " BOOL($7) = wired" - " ORDER BY order_serial DESC" - " LIMIT $2)" - " ORDER BY order_serial DESC" - " LIMIT $2"), - /* for postgres_insert_order() */ - GNUNET_PQ_make_prepare ("insert_order", - "INSERT INTO merchant_orders" - "(merchant_serial" - ",order_id" - ",pay_deadline" - ",claim_token" - ",h_post_data" - ",creation_time" - ",contract_terms)" - " SELECT merchant_serial," - " $2, $3, $4, $5, $6, $7" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_unlock_inventory() */ - GNUNET_PQ_make_prepare ("unlock_inventory", - "DELETE" - " FROM merchant_inventory_locks" - " WHERE lock_uuid=$1"), - /* for postgres_insert_order_lock() */ - GNUNET_PQ_make_prepare ("insert_order_lock", - "WITH tmp AS" - " (SELECT " - " product_serial" - " ,merchant_serial" - " ,total_stock" - " ,total_sold" - " ,total_lost" - " FROM merchant_inventory" - " WHERE product_id=$3" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))" - " INSERT INTO merchant_order_locks" - " (product_serial" - " ,total_locked" - " ,order_serial)" - " SELECT tmp.product_serial, $4, order_serial" - " FROM merchant_orders" - " JOIN tmp USING(merchant_serial)" - " WHERE order_id=$2 AND" - " tmp.total_stock - tmp.total_sold - tmp.total_lost - $4 >= " - " (SELECT COALESCE(SUM(total_locked), 0)" - " FROM merchant_inventory_locks" - " WHERE product_serial=tmp.product_serial) + " - " (SELECT COALESCE(SUM(total_locked), 0)" - " FROM merchant_order_locks" - " WHERE product_serial=tmp.product_serial)"), - /* for postgres_lookup_contract_terms() */ - GNUNET_PQ_make_prepare ("lookup_contract_terms", - "SELECT" - " contract_terms" - ",order_serial" - ",claim_token" - ",paid" - " FROM merchant_contract_terms" - " WHERE order_id=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* 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" - ",claim_token)" - "SELECT" - " mo.order_serial" - ",mo.merchant_serial" - ",mo.order_id" - ",$3" /* contract_terms */ - ",$4" /* h_contract_terms */ - ",mo.creation_time" - ",$5" /* pay_deadline */ - ",$6" /* refund_deadline */ - ",$7" /* fulfillment_url */ - ",mo.claim_token " - "FROM merchant_orders mo" - " WHERE order_id=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " RETURNING order_serial"), - /* for postgres_update_contract_terms() */ - GNUNET_PQ_make_prepare ("update_contract_terms", - "UPDATE merchant_contract_terms SET" - " contract_terms=$3" - ",h_contract_terms=$4" - ",pay_deadline=$5" - ",refund_deadline=$6" - ",fulfillment_url=$7" - " WHERE order_id=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* 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) )"), - /* for postgres_lookup_deposits() */ - GNUNET_PQ_make_prepare ("lookup_deposits", - "SELECT" - " exchange_url" - ",coin_pub" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",deposit_fee_val" - ",deposit_fee_frac" - ",refund_fee_val" - ",refund_fee_frac" - ",wire_fee_val" - ",wire_fee_frac" - " FROM merchant_deposits" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_insert_exchange_signkey() */ - GNUNET_PQ_make_prepare ("insert_exchange_signkey", - "INSERT INTO merchant_exchange_signing_keys" - "(master_pub" - ",exchange_pub" - ",start_date" - ",expire_date" - ",end_date" - ",master_sig)" - "VALUES" - "($1, $2, $3, $4, $5, $6)"), - /* for postgres_insert_deposit() */ - GNUNET_PQ_make_prepare ("insert_deposit", - "WITH md AS" - " (SELECT account_serial, merchant_serial" - " FROM merchant_accounts" - " WHERE h_wire=$14" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))" - ", ed AS" - " (SELECT signkey_serial" - " FROM merchant_exchange_signing_keys" - " WHERE exchange_pub=$16" - " ORDER BY start_date DESC" - " LIMIT 1)" - "INSERT INTO merchant_deposits" - "(order_serial" - ",deposit_timestamp" - ",coin_pub" - ",exchange_url" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",deposit_fee_val" - ",deposit_fee_frac" - ",refund_fee_val" - ",refund_fee_frac" - ",wire_fee_val" - ",wire_fee_frac" - ",exchange_sig" - ",signkey_serial" - ",account_serial)" - " SELECT " - " order_serial" - " ,$3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $15" - " ,ed.signkey_serial" - " ,md.account_serial" - " FROM merchant_contract_terms" - " JOIN md USING (merchant_serial)" - " FULL OUTER JOIN ed ON TRUE" - " WHERE h_contract_terms=$2"), - /* for postgres_lookup_refunds() */ - GNUNET_PQ_make_prepare ("lookup_refunds", - "SELECT" - " coin_pub" - ",refund_amount_val" - ",refund_amount_frac" - " FROM merchant_refunds" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_mark_contract_paid() */ - GNUNET_PQ_make_prepare ("mark_contract_paid", - "UPDATE merchant_contract_terms SET" - " paid=TRUE" - ",session_id=$3" - " WHERE h_contract_terms=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_mark_contract_paid() */ - GNUNET_PQ_make_prepare ("mark_inventory_sold", - "UPDATE merchant_inventory SET" - " total_sold=total_sold + order_locks.total_locked" - " FROM (SELECT total_locked,product_serial" - " FROM merchant_order_locks" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))" - " ) AS order_locks" - " WHERE merchant_inventory.product_serial" - " =order_locks.product_serial"), - /* for postgres_mark_contract_paid() */ - GNUNET_PQ_make_prepare ("delete_completed_order", - "WITH md AS" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1) " - "DELETE" - " FROM merchant_orders" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " JOIN md USING (merchant_serial)" - " WHERE h_contract_terms=$2)"), - /* for postgres_refund_coin() */ - GNUNET_PQ_make_prepare ("refund_coin", - "INSERT INTO merchant_refunds" - "(order_serial" - ",rtransaction_id" - ",refund_timestamp" - ",coin_pub" - ",reason" - ",refund_amount_val" - ",refund_amount_frac" - ") " - "SELECT " - " order_serial" - ",0" /* rtransaction_id always 0 for /abort */ - ",$3" - ",coin_pub" - ",$5" - ",amount_with_fee_val" - ",amount_with_fee_frac" - " FROM merchant_deposits" - " WHERE coin_pub=$4" - " AND order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - - /* for postgres_lookup_order_status() */ - GNUNET_PQ_make_prepare ("lookup_order_status", - "SELECT" - " h_contract_terms" - ",paid" - " FROM merchant_contract_terms" - " WHERE merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND order_id=$2"), - - /* for postgres_lookup_order_status_by_serial() */ - GNUNET_PQ_make_prepare ("lookup_order_status_by_serial", - "SELECT" - " h_contract_terms" - ",order_id" - ",paid" - " FROM merchant_contract_terms" - " WHERE merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND order_serial=$2"), - - /* for postgres_lookup_payment_status() */ - GNUNET_PQ_make_prepare ("lookup_payment_status", - "SELECT" - " wired" - ",paid" - " FROM merchant_contract_terms" - " WHERE order_serial=$1"), - /* for postgres_lookup_payment_status() */ - GNUNET_PQ_make_prepare ("lookup_payment_status_session_id", - "SELECT" - " wired" - ",paid" - " FROM merchant_contract_terms" - " WHERE order_serial=$1" - " AND session_id=$2"), - /* for postgres_lookup_deposits_by_order() */ - GNUNET_PQ_make_prepare ("lookup_deposits_by_order", - "SELECT" - " deposit_serial" - ",exchange_url" - ",h_wire" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",deposit_fee_val" - ",deposit_fee_frac" - ",coin_pub" - " FROM merchant_deposits" - " JOIN merchant_accounts USING (account_serial)" - " WHERE order_serial=$1"), - /* for postgres_lookup_transfer_details_by_order() */ - GNUNET_PQ_make_prepare ("lookup_transfer_details_by_order", - "SELECT" - " md.deposit_serial" - ",md.exchange_url" - ",mt.wtid" - ",exchange_deposit_value_val" - ",exchange_deposit_value_frac" - ",exchange_deposit_fee_val" - ",exchange_deposit_fee_frac" - ",deposit_timestamp" - ",mt.confirmed AS transfer_confirmed" - " FROM merchant_transfer_to_coin" - " JOIN merchant_deposits AS md USING (deposit_serial)" - " JOIN merchant_transfers AS mt USING (credit_serial)" - " WHERE deposit_serial IN" - " (SELECT deposit_serial" - " FROM merchant_deposits" - " WHERE order_serial=$1)"), - /* for postgres_insert_deposit_to_transfer() */ - GNUNET_PQ_make_prepare ("insert_deposit_to_transfer", - "INSERT INTO merchant_deposit_to_transfer" - "(deposit_serial" - ",coin_contribution_value_val" - ",coin_contribution_value_frac" - ",credit_serial" - ",execution_time" - ",signkey_serial" - ",exchange_sig" - ") SELECT $1, $2, $3, credit_serial, $4, signkey_serial, $5" - " FROM merchant_transfers" - " CROSS JOIN merchant_exchange_signing_keys" - " WHERE exchange_pub=$6" - " AND wtid=$7"), - /* for postgres_mark_order_wired() */ - GNUNET_PQ_make_prepare ("mark_order_wired", - "UPDATE merchant_contract_terms SET" - " wired=true" - " WHERE order_serial=$1"), - /* for process_refund_cb() used in postgres_increase_refund() */ - GNUNET_PQ_make_prepare ("find_refunds_by_coin", - "SELECT" - " refund_amount_val" - ",refund_amount_frac" - ",rtransaction_id" - " FROM merchant_refunds" - " WHERE coin_pub=$1" - " AND order_serial=$2"), - /* for process_deposits_for_refund_cb() used in postgres_increase_refund() */ - GNUNET_PQ_make_prepare ("insert_refund", - "INSERT INTO merchant_refunds" - "(order_serial" - ",rtransaction_id" - ",refund_timestamp" - ",coin_pub" - ",reason" - ",refund_amount_val" - ",refund_amount_frac" - ") VALUES" - "($1, $2, $3, $4, $5, $6, $7)"), - /* for postgres_increase_refund() */ - GNUNET_PQ_make_prepare ("find_deposits_for_refund", - "SELECT" - " coin_pub" - ",order_serial" - ",amount_with_fee_val" - ",amount_with_fee_frac" - " FROM merchant_deposits" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE order_id=$2" - " AND paid=TRUE" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_lookup_refunds_detailed() */ - GNUNET_PQ_make_prepare ("lookup_refunds_detailed", - "SELECT" - " refund_serial" - ",refund_timestamp" - ",coin_pub" - ",merchant_deposits.exchange_url" - ",rtransaction_id" - ",reason" - ",refund_amount_val" - ",refund_amount_frac" - ",merchant_refund_proofs.exchange_sig IS NULL AS pending" - " FROM merchant_refunds" - " JOIN merchant_deposits USING (order_serial, coin_pub)" - " LEFT JOIN merchant_refund_proofs USING (refund_serial)" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_insert_refund_proof() */ - GNUNET_PQ_make_prepare ("insert_refund_proof", - "INSERT INTO merchant_refund_proofs" - "(refund_serial" - ",exchange_sig" - ",signkey_serial)" - "SELECT $1, $2, signkey_serial" - " FROM merchant_exchange_signing_keys" - " WHERE exchange_pub=$3" - " ORDER BY start_date DESC" - " LIMIT 1"), - /* for postgres_lookup_refund_proof() */ - GNUNET_PQ_make_prepare ("lookup_refund_proof", - "SELECT" - " merchant_exchange_signing_keys.exchange_pub" - ",exchange_sig" - " FROM merchant_refund_proofs" - " JOIN merchant_exchange_signing_keys" - " USING (signkey_serial)" - " WHERE" - " refund_serial=$1"), - /* for postgres_lookup_order_by_fulfillment() */ - GNUNET_PQ_make_prepare ("lookup_order_by_fulfillment", - "SELECT" - " order_id" - " FROM merchant_contract_terms" - " WHERE fulfillment_url=$2" - " AND session_id=$3" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_insert_transfer() */ - GNUNET_PQ_make_prepare ("insert_transfer", - "INSERT INTO merchant_transfers" - "(exchange_url" - ",wtid" - ",credit_amount_val" - ",credit_amount_frac" - ",account_serial" - ",confirmed)" - "SELECT" - " $1, $2, $3, $4, account_serial, $6" - " FROM merchant_accounts" - " WHERE payto_uri=$5" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$7)"), - /* for postgres_delete_transfer() */ - GNUNET_PQ_make_prepare ("delete_transfer", - "DELETE FROM merchant_transfers" - " WHERE" - " credit_serial=$2" - " AND account_serial IN " - " (SELECT account_serial " - " FROM merchant_accounts" - " WHERE merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_check_transfer_exists() */ - GNUNET_PQ_make_prepare ("check_transfer_exists", - "SELECT" - " 1" - " FROM merchant_transfers" - " JOIN merchant_accounts" - " USING (account_serial)" - " WHERE" - " credit_serial=$2" - " AND" - " merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_lookup_account() */ - GNUNET_PQ_make_prepare ("lookup_account", - "SELECT" - " account_serial" - " FROM merchant_accounts" - " WHERE payto_uri=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_insert_transfer_details() */ - GNUNET_PQ_make_prepare ("lookup_credit_serial", - "SELECT" - " credit_serial" - " FROM merchant_transfers" - " WHERE exchange_url=$1" - " AND wtid=$4" - " AND account_serial=" - " (SELECT account_serial" - " FROM merchant_accounts" - " WHERE payto_uri=$2" - " AND exchange_url=$1" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$3))"), - /* for postgres_insert_transfer_details() */ - GNUNET_PQ_make_prepare ("insert_transfer_signature", - "INSERT INTO merchant_transfer_signatures" - "(credit_serial" - ",signkey_serial" - ",credit_amount_val" - ",credit_amount_frac" - ",wire_fee_val" - ",wire_fee_frac" - ",execution_time" - ",exchange_sig) " - "SELECT $1, signkey_serial, $2, $3, $4, $5, $6, $7" - " FROM merchant_exchange_signing_keys" - " WHERE exchange_pub=$8" - " ORDER BY start_date DESC" - " LIMIT 1"), - /* for postgres_insert_transfer_details() */ - GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping", - "INSERT INTO merchant_transfer_to_coin" - "(deposit_serial" - ",credit_serial" - ",offset_in_exchange_list" - ",exchange_deposit_value_val" - ",exchange_deposit_value_frac" - ",exchange_deposit_fee_val" - ",exchange_deposit_fee_frac) " - "SELECT deposit_serial, $1, $2, $3, $4, $5, $6" - " FROM merchant_deposits" - " JOIN merchant_contract_terms USING (order_serial)" - " WHERE coin_pub=$7" - " AND h_contract_terms=$8" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$9)"), - /* for postgres_insert_transfer_details() */ - GNUNET_PQ_make_prepare ("update_wired_by_coin_pub", - "WITH os AS" /* select orders affected by the coin */ - "(SELECT order_serial" - " FROM merchant_deposits" - " WHERE coin_pub=$1)" - "UPDATE merchant_contract_terms " - " SET wired=TRUE " - " WHERE order_serial IN " - " (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/ - " WHERE NOT EXISTS " - " (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */ - " JOIN os USING (order_serial)" /* filter early */ - " WHERE deposit_serial NOT IN" - " (SELECT deposit_serial " /* all coins associated with order that WERE wired */ - " FROM merchant_deposits " - " JOIN os USING (order_serial)" /* filter early */ - " JOIN merchant_deposit_to_transfer USING (deposit_serial)" - " JOIN merchant_transfers USING (credit_serial)" - " WHERE confirmed=TRUE)))"), - /* for postgres_lookup_wire_fee() */ - GNUNET_PQ_make_prepare ("lookup_wire_fee", - "SELECT" - " wire_fee_val" - ",wire_fee_frac" - ",closing_fee_val" - ",closing_fee_frac" - ",start_date" - ",end_date" - ",master_sig" - " FROM merchant_exchange_wire_fees" - " WHERE master_pub=$1" - " AND h_wire_method=$2" - " AND start_date <= $3" - " AND end_date > $3"), - /* for postgres_lookup_deposits_by_contract_and_coin() */ - GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin", - "SELECT" - " exchange_url" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",deposit_fee_val" - ",deposit_fee_frac" - ",refund_fee_val" - ",refund_fee_frac" - ",wire_fee_val" - ",wire_fee_frac" - ",h_wire" - ",deposit_timestamp" - ",refund_deadline" - ",exchange_sig" - ",exchange_pub" - " FROM merchant_contract_terms" - " JOIN merchant_deposits USING (order_serial)" - " JOIN merchant_exchange_signing_keys USING (signkey_serial)" - " JOIN merchant_accounts USING (account_serial)" - " WHERE h_contract_terms=$2" - " AND coin_pub=$3" - " AND merchant_contract_terms.merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_lookup_transfer() */ - GNUNET_PQ_make_prepare ("lookup_transfer", - "SELECT" - " mt.credit_amount_val AS credit_amount_val" - ",mt.credit_amount_frac AS credit_amount_frac" - ",mts.credit_amount_val AS exchange_amount_val" - ",mts.credit_amount_frac AS exchange_amount_frac" - ",wire_fee_val" - ",wire_fee_frac" - ",execution_time" - ",verified" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_instances USING (merchant_serial)" - " LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)" - " WHERE wtid=$2" - " AND exchange_url=$1" - " AND merchant_id=$3;"), - /* for postgres_set_transfer_status_to_verified() */ - GNUNET_PQ_make_prepare ("set_transfer_status_to_verified", - "UPDATE merchant_transfers SET" - " verified=TRUE" - " WHERE wtid=$1" - " AND exchange_url=$2"), - /* for postgres_lookup_transfer_summary() */ - GNUNET_PQ_make_prepare ("lookup_transfer_summary", - "SELECT" - " order_id" - ",exchange_deposit_value_val" - ",exchange_deposit_value_frac" - ",exchange_deposit_fee_val" - ",exchange_deposit_fee_frac" - " FROM merchant_transfers" - " JOIN merchant_transfer_to_coin USING (credit_serial)" - " JOIN merchant_deposits USING (deposit_serial)" - " JOIN merchant_contract_terms USING (order_serial)" - " WHERE wtid=$2" - " AND merchant_transfers.exchange_url=$1"), - /* for postgres_lookup_transfer_details() */ - GNUNET_PQ_make_prepare ("lookup_transfer_details", - "SELECT" - " merchant_contract_terms.h_contract_terms" - ",merchant_transfer_to_coin.offset_in_exchange_list" - ",merchant_deposits.coin_pub" - ",exchange_deposit_value_val" - ",exchange_deposit_value_frac" - ",exchange_deposit_fee_val" - ",exchange_deposit_fee_frac" - " FROM merchant_transfer_to_coin" - " JOIN merchant_deposits USING (deposit_serial)" - " JOIN merchant_contract_terms USING (order_serial)" - " JOIN merchant_transfers USING (credit_serial)" - " WHERE merchant_transfers.wtid=$2" - " AND merchant_transfers.exchange_url=$1"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_asc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial > $4" - " AND payto_uri = $6" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $5"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_time_asc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial > $4" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $5"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_payto_asc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial > $2" - " AND payto_uri = $4" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $3"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_asc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial > $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial ASC" - " LIMIT $3"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_desc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial < $4" - " AND payto_uri = $6" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $5"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_time_desc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",merchant_transfer_signatures.execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE execution_time < $2" - " AND execution_time >= $3" - " AND credit_serial < $4" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $5"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_payto_desc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial < $2" - " AND payto_uri = $4" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $3"), - /* for postgres_lookup_transfers() */ - GNUNET_PQ_make_prepare ("lookup_transfers_desc", - "SELECT" - " mt.credit_amount_val" - ",mt.credit_amount_frac" - ",wtid" - ",merchant_accounts.payto_uri" - ",exchange_url" - ",credit_serial" - ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL" - " THEN 9223372036854775807" /* largest BIGINT possible */ - " ELSE merchant_transfer_signatures.execution_time" - " END AS execution_time" - ",verified" - ",confirmed" - " FROM merchant_transfers mt" - " JOIN merchant_accounts USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures USING (credit_serial)" - " WHERE credit_serial < $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " ORDER BY credit_serial DESC" - " LIMIT $3"), - /* For postgres_store_wire_fee_by_exchange() */ - GNUNET_PQ_make_prepare ("insert_wire_fee", - "INSERT INTO merchant_exchange_wire_fees" - "(master_pub" - ",h_wire_method" - ",wire_fee_val" - ",wire_fee_frac" - ",closing_fee_val" - ",closing_fee_frac" - ",start_date" - ",end_date" - ",master_sig)" - " VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8, $9)"), - /* For postgres_insert_reserve() */ - GNUNET_PQ_make_prepare ("insert_reserve", - "INSERT INTO merchant_tip_reserves" - "(reserve_pub" - ",merchant_serial" - ",creation_time" - ",expiration" - ",merchant_initial_balance_val" - ",merchant_initial_balance_frac" - ")" - "SELECT $2, merchant_serial, $3, $4, $5, $6" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* For postgres_activate_reserve() */ - GNUNET_PQ_make_prepare ("activate_reserve", - "UPDATE merchant_tip_reserves SET" - " exchange_initial_balance_val=$3" - ",exchange_initial_balance_frac=$4" - " WHERE reserve_pub=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_insert_reserve() */ - GNUNET_PQ_make_prepare ("insert_reserve_key", - "INSERT INTO merchant_tip_reserve_keys" - "(reserve_serial" - ",reserve_priv" - ",exchange_url" - ",payto_uri" - ")" - "SELECT reserve_serial, $3, $4, $5" - " FROM merchant_tip_reserves" - " WHERE reserve_pub=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_lookup_reserves() */ - GNUNET_PQ_make_prepare ("lookup_reserves", - "SELECT" - " reserve_pub" - ",creation_time" - ",expiration" - ",merchant_initial_balance_val" - ",merchant_initial_balance_frac" - ",exchange_initial_balance_val" - ",exchange_initial_balance_frac" - ",tips_committed_val" - ",tips_committed_frac" - ",tips_picked_up_val" - ",tips_picked_up_frac" - ",reserve_priv IS NOT NULL AS active" - " FROM merchant_tip_reserves" - " FULL OUTER JOIN merchant_tip_reserve_keys USING (reserve_serial)" - " WHERE creation_time > $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_lookup_pending_reserves() */ - GNUNET_PQ_make_prepare ("lookup_pending_reserves", - "SELECT" - " reserve_pub" - ",merchant_id" - ",exchange_url" - ",merchant_initial_balance_val" - ",merchant_initial_balance_frac" - " FROM merchant_tip_reserves" - " JOIN merchant_instances USING (merchant_serial)" - " JOIN merchant_tip_reserve_keys USING (reserve_serial)" - " WHERE exchange_initial_balance_val=0" - " AND exchange_initial_balance_frac=0"), - /* For postgres_lookup_reserve() */ - GNUNET_PQ_make_prepare ("lookup_reserve", - "SELECT" - " creation_time" - ",expiration" - ",merchant_initial_balance_val" - ",merchant_initial_balance_frac" - ",exchange_initial_balance_val" - ",exchange_initial_balance_frac" - ",tips_committed_val" - ",tips_committed_frac" - ",tips_picked_up_val" - ",tips_picked_up_frac" - ",reserve_priv IS NOT NULL AS active" - ",exchange_url" - ",payto_uri" - " FROM merchant_tip_reserves" - " FULL OUTER JOIN merchant_tip_reserve_keys USING (reserve_serial)" - " WHERE reserve_pub = $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_lookup_reserve() */ - GNUNET_PQ_make_prepare ("lookup_reserve_tips", - "SELECT" - " justification" - ",tip_id" - ",amount_val" - ",amount_frac" - " FROM merchant_tips" - " WHERE reserve_serial =" - " (SELECT reserve_serial" - " FROM merchant_tip_reserves" - " WHERE reserve_pub=$2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_delete_reserve() */ - GNUNET_PQ_make_prepare ("delete_reserve", - "DELETE" - " FROM merchant_tip_reserve_keys" - " WHERE reserve_serial=" - " (SELECT reserve_serial" - " FROM merchant_tip_reserves" - " WHERE reserve_pub=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1))"), - /* for postgres_purge_reserve() */ - GNUNET_PQ_make_prepare ("purge_reserve", - "DELETE" - " FROM merchant_tip_reserves" - " WHERE reserve_pub=$2" - " AND merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_authorize_tip() */ - GNUNET_PQ_make_prepare ("lookup_reserve_for_tip", - "SELECT" - " reserve_pub" - ",expiration" - ",exchange_initial_balance_val" - ",exchange_initial_balance_frac" - ",tips_committed_val" - ",tips_committed_frac" - " FROM merchant_tip_reserves" - " WHERE" - " merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - - /* For postgres_authorize_tip() */ - GNUNET_PQ_make_prepare ("lookup_reserve_status", - "SELECT" - " expiration" - ",exchange_initial_balance_val" - ",exchange_initial_balance_frac" - ",tips_committed_val" - ",tips_committed_frac" - " FROM merchant_tip_reserves" - " WHERE reserve_pub = $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_authorize_tip() */ - GNUNET_PQ_make_prepare ("update_reserve_tips_committed", - "UPDATE merchant_tip_reserves SET" - " tips_committed_val=$3" - ",tips_committed_frac=$4" - " WHERE reserve_pub = $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_authorize_tip() */ - GNUNET_PQ_make_prepare ("insert_tip", - "INSERT INTO merchant_tips" - "(reserve_serial" - ",tip_id" - ",justification" - ",next_url" - ",expiration" - ",amount_val" - ",amount_frac" - ") " - "SELECT" - " reserve_serial, $3, $4, $5, $6, $7, $8" - " FROM merchant_tip_reserves" - " WHERE reserve_pub=$2" - " AND merchant_serial = " - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_lookup_pickup() */ - GNUNET_PQ_make_prepare ("lookup_pickup", - "SELECT" - " exchange_url" - ",reserve_priv" - ",pickup_serial" - " FROM merchant_tip_pickups" - " JOIN merchant_tips USING (tip_serial)" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " JOIN merchant_tip_reserve_keys USING (reserve_serial)" - " WHERE pickup_id = $3" - " AND tip_id = $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_lookup_pickup() */ - GNUNET_PQ_make_prepare ("lookup_pickup_signatures", - "SELECT" - " coin_offset" - ",blind_sig" - " FROM merchant_tip_pickup_signatures" - " WHERE pickup_serial = $1"), - - /* For postgres_lookup_tip() */ - GNUNET_PQ_make_prepare ("lookup_tip", - "SELECT" - " amount_val" - ",amount_frac" - ",picked_up_val" - ",picked_up_frac" - ",merchant_tips.expiration" - ",exchange_url" - ",reserve_priv" - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " JOIN merchant_tip_reserve_keys USING (reserve_serial)" - " WHERE tip_id = $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* For postgres_lookup_tip() */ - GNUNET_PQ_make_prepare ("lookup_tips_inc", - "SELECT" - " tip_serial" - ",tip_id" - ",amount_val" - ",amount_frac" - ",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */ - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " WHERE merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " tip_serial > $3" - " ORDER BY tip_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_tips_dec", - "SELECT" - " tip_serial" - ",tip_id" - ",amount_val" - ",amount_frac" - ",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */ - ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " WHERE merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " tip_serial < $3" - " ORDER BY tip_serial DESC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_tips_inc_expired", - "SELECT" - " tip_serial" - ",tip_id" - ",amount_val" - ",amount_frac" - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " WHERE merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " tip_serial > $3" - " AND" - " CAST($5 as BOOL) = (merchant_tips.expiration < $4)" - " ORDER BY tip_serial ASC" - " LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_tips_dec_expired", - "SELECT" - " tip_serial" - ",tip_id" - ",amount_val" - ",amount_frac" - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " WHERE merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND" - " tip_serial < $3" - " AND" - " CAST($5 as BOOL) = (merchant_tips.expiration < $4)" - " ORDER BY tip_serial DESC" - " LIMIT $2"), - /* for postgres_lookup_tip_details() */ - GNUNET_PQ_make_prepare ("lookup_tip_details", - "SELECT" - " tip_serial" - ",amount_val" - ",amount_frac" - ",picked_up_val" - ",picked_up_frac" - ",justification" - ",merchant_tips.expiration" - ",reserve_pub" - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " WHERE tip_id = $2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_lookup_tip_details() */ - GNUNET_PQ_make_prepare ("lookup_pickup_details", - "SELECT" - " pickup_id" - ",amount_val" - ",amount_frac" - ",COUNT(blind_sig) AS num_planchets" - " FROM merchant_tip_pickups" - " JOIN merchant_tip_pickup_signatures USING (pickup_serial)" - " WHERE tip_serial = $1" - " GROUP BY pickup_serial"), - /* for postgres_insert_pickup() */ - GNUNET_PQ_make_prepare ("insert_pickup", - "INSERT INTO merchant_tip_pickups" - "(tip_serial" - ",pickup_id" - ",amount_val" - ",amount_frac" - ") " - "SELECT" - " tip_serial, $3, $4, $5" - " FROM merchant_tips" - " JOIN merchant_tip_reserves USING (reserve_serial)" - " WHERE tip_id=$2" - " AND merchant_serial = " - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_insert_pickup() */ - GNUNET_PQ_make_prepare ("update_picked_up_tip", - "UPDATE merchant_tips SET" - " picked_up_val=$2" - ",picked_up_frac=$3" - ",was_picked_up = ($2 = amount_val AND $3 = amount_frac)" - " WHERE tip_id = $1"), - /* for postgres_insert_pickup() */ - GNUNET_PQ_make_prepare ("lookup_picked_up_reserve", - "SELECT" - " reserve_serial" - ",tips_picked_up_val" - ",tips_picked_up_frac" - " FROM merchant_tip_reserves" - " JOIN merchant_tips USING (reserve_serial)" - " WHERE tip_id=$2" - " AND merchant_serial =" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)"), - /* for postgres_insert_pickup() */ - GNUNET_PQ_make_prepare ("update_picked_up_reserve", - "UPDATE merchant_tip_reserves SET" - " tips_picked_up_val=$2" - ",tips_picked_up_frac=$3" - " WHERE reserve_serial = $1"), - /* for postgres_insert_pickup_blind_signature() */ - GNUNET_PQ_make_prepare ("insert_pickup_blind_signature", - "INSERT INTO merchant_tip_pickup_signatures" - "(pickup_serial" - ",coin_offset" - ",blind_sig" - ") " - "SELECT" - " pickup_serial, $2, $3" - " FROM merchant_tip_pickups" - " WHERE pickup_id=$1"), - /* for postgres_lookup_templates() */ - GNUNET_PQ_make_prepare ("lookup_templates", - "SELECT" - " template_id" - ",template_description" - " FROM merchant_template" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1"), - /* for postgres_lookup_template() */ - GNUNET_PQ_make_prepare ("lookup_template", - "SELECT" - " template_description" - ",image" - ",template_contract" - " FROM merchant_template" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND merchant_template.template_id=$2"), - /* for postgres_delete_template() */ - GNUNET_PQ_make_prepare ("delete_template", - "DELETE" - " FROM merchant_template" - " WHERE merchant_template.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_template.template_id=$2"), - /* for postgres_insert_template() */ - GNUNET_PQ_make_prepare ("insert_template", - "INSERT INTO merchant_template" - "(merchant_serial" - ",template_id" - ",template_description" - ",image" - ",template_contract" - ")" - " SELECT merchant_serial," - " $2, $3, $4, $5" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_update_template() */ - GNUNET_PQ_make_prepare ("update_template", - "UPDATE merchant_template SET" - " template_description=$3" - ",image=$4" - ",template_contract=$5" - " WHERE merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND template_id=$2"), - /* for postgres_lookup_webhooks() */ - GNUNET_PQ_make_prepare ("lookup_webhooks", - "SELECT" - " webhook_id" - ",event_type" - " FROM merchant_webhook" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1"), - /* for postgres_lookup_webhook() */ - GNUNET_PQ_make_prepare ("lookup_webhook", - "SELECT" - " event_type" - ",url" - ",http_method" - ",header_template" - ",body_template" - " FROM merchant_webhook" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND merchant_webhook.webhook_id=$2"), - /* for postgres_delete_webhook() */ - GNUNET_PQ_make_prepare ("delete_webhook", - "DELETE" - " FROM merchant_webhook" - " WHERE merchant_webhook.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_webhook.webhook_id=$2"), - /* for postgres_insert_webhook() */ - GNUNET_PQ_make_prepare ("insert_webhook", - "INSERT INTO merchant_webhook" - "(merchant_serial" - ",webhook_id" - ",event_type" - ",url" - ",http_method" - ",header_template" - ",body_template" - ")" - " SELECT merchant_serial," - " $2, $3, $4, $5, $6, $7" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_update_webhook() */ - GNUNET_PQ_make_prepare ("update_webhook", - "UPDATE merchant_webhook SET" - " event_type=$3" - ",url=$4" - ",http_method=$5" - ",header_template=$6" - ",body_template=$7" - " WHERE merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND webhook_id=$2"), - /* for postgres_lookup_webhook_by_event() */ - GNUNET_PQ_make_prepare ("lookup_webhook_by_event", - "SELECT" - " webhook_serial" - ",event_type" - ",url" - ",http_method" - ",header_template" - ",body_template" - " FROM merchant_webhook" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND event_type=$2"), - /* for postgres_delete_pending_webhook() */ - GNUNET_PQ_make_prepare ("delete_pending_webhook", - "DELETE" - " FROM merchant_pending_webhooks" - " WHERE merchant_pending_webhooks.webhook_serial=" - " (SELECT webhook_serial " - " FROM merchant_webhook" - " WHERE webhook_serial=$1)"), - /* for postgres_insert_pending_webhook() */ - GNUNET_PQ_make_prepare ("insert_pending_webhook", - "INSERT INTO merchant_pending_webhooks" - "(merchant_serial" - ",webhook_serial" - ",url" - ",http_method" - ",header" - ",body" - ")" - " SELECT mi.merchant_serial," - " $2, $3, $4, $5, $6" - " FROM merchant_instances mi" - " WHERE mi.merchant_id=$1"), - /* for postgres_update_pending_webhook() */ - GNUNET_PQ_make_prepare ("update_pending_webhook", - "UPDATE merchant_pending_webhooks SET" - " retries=retries+1" - ",next_attempt=$2" - " WHERE webhook_serial=" - " (SELECT webhook_serial" - " FROM merchant_webhook" - " WHERE webhook_serial=$1)"), - /* for postgres_lookup_pending_webhook() */ - GNUNET_PQ_make_prepare ("lookup_pending_webhook", - "SELECT" - " next_attempt" - ",retries" - ",url" - ",http_method" - ",header" - ",body" - " FROM merchant_pending_webhooks" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND merchant_pending_webhooks.webhook_serial=$2"), - /* for postgres_lookup_pending_webhooks() */ - GNUNET_PQ_make_prepare ("lookup_pending_webhooks", - "SELECT" - " webhook_serial" - ",next_attempt" - ",retries" - ",url" - ",http_method" - ",header" - ",body" - " FROM merchant_pending_webhooks" - " WHERE next_attempt <= $1" - " ORDER BY next_attempt ASC" - ), - /* for postgres_lookup_future_webhook() */ - GNUNET_PQ_make_prepare ("lookup_future_webhook", - "SELECT" - " webhook_serial" - ",next_attempt" - ",retries" - ",url" - ",http_method" - ",header" - ",body" - " FROM merchant_pending_webhooks" - " ORDER BY next_attempt ASC LIMIT 1" - ), - /* for postgres_lookup_all_webhooks() */ - GNUNET_PQ_make_prepare ("lookup_all_webhooks", - " SELECT" - " webhook_serial" - ",next_attempt" - ",retries" - ",url" - ",http_method" - ",header" - ",body" - " FROM merchant_pending_webhooks" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND webhook_serial > $2" - " ORDER BY webhook_serial" - " ASC LIMIT $3"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; struct GNUNET_PQ_ExecuteStatement es[] = { GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"), GNUNET_PQ_EXECUTE_STATEMENT_END @@ -10515,7 +311,8 @@ postgres_connect (void *cls) "merchantdb-postgres", NULL, es, - ps); + NULL); + pg->prep_gen++; if (NULL == pg->conn) return GNUNET_SYSERR; return GNUNET_OK; @@ -10549,14 +346,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) GNUNET_free (pg); return NULL; } - if (GNUNET_OK != - TALER_config_get_currency (cfg, - &pg->currency)) - { - GNUNET_free (pg->sql_dir); - GNUNET_free (pg); - return NULL; - } plugin = GNUNET_new (struct TALER_MERCHANTDB_Plugin); plugin->cls = pg; plugin->connect = &postgres_connect; @@ -10566,111 +355,236 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->event_listen_cancel = &postgres_event_listen_cancel; plugin->event_notify = &postgres_event_notify; plugin->preflight = &postgres_preflight; - plugin->start = &postgres_start; - plugin->start_read_committed = &postgres_start_read_committed; - plugin->rollback = &postgres_rollback; - plugin->commit = &postgres_commit; - plugin->lookup_instances = &postgres_lookup_instances; - plugin->lookup_instance = &postgres_lookup_instance; - plugin->lookup_instance_auth = &postgres_lookup_instance_auth; - plugin->insert_instance = &postgres_insert_instance; - plugin->insert_account = &postgres_insert_account; + plugin->start = &TMH_PG_start; + plugin->start_read_committed = &TMH_PG_start_read_committed; + plugin->rollback = &TMH_PG_rollback; + plugin->commit = &TMH_PG_commit; + plugin->insert_login_token + = &TMH_PG_insert_login_token; + plugin->delete_login_token + = &TMH_PG_delete_login_token; + plugin->select_login_token + = &TMH_PG_select_login_token; + plugin->select_account_by_uri + = &TMH_PG_select_account_by_uri; + plugin->lookup_instance_auth + = &TMH_PG_lookup_instance_auth; + plugin->insert_instance + = &TMH_PG_insert_instance; + plugin->insert_account + = &TMH_PG_insert_account; + plugin->lookup_otp_devices + = &TMH_PG_lookup_otp_devices; + plugin->delete_template + = &TMH_PG_delete_template; + plugin->insert_template + = &TMH_PG_insert_template; + plugin->update_template + = &TMH_PG_update_template; + plugin->lookup_templates + = &TMH_PG_lookup_templates; + plugin->lookup_template + = &TMH_PG_lookup_template; + plugin->update_account + = &TMH_PG_update_account; plugin->account_kyc_set_status - = &postgres_account_kyc_set_status; + = &TMH_PG_account_kyc_set_status; plugin->account_kyc_get_status - = &postgres_account_kyc_get_status; - plugin->delete_instance_private_key = &postgres_delete_instance_private_key; - plugin->purge_instance = &postgres_purge_instance; - plugin->update_instance = &postgres_update_instance; - plugin->update_instance_auth = &postgres_update_instance_auth; - plugin->activate_account = &postgres_activate_account; - plugin->inactivate_account = &postgres_inactivate_account; - plugin->lookup_products = &postgres_lookup_products; - plugin->lookup_product = &postgres_lookup_product; - plugin->delete_product = &postgres_delete_product; - plugin->insert_product = &postgres_insert_product; - plugin->update_product = &postgres_update_product; - plugin->lock_product = &postgres_lock_product; - plugin->expire_locks = &postgres_expire_locks; - plugin->delete_order = &postgres_delete_order; - plugin->lookup_order = &postgres_lookup_order; - plugin->lookup_order_summary = &postgres_lookup_order_summary; - plugin->lookup_orders = &postgres_lookup_orders; - plugin->insert_order = &postgres_insert_order; - plugin->unlock_inventory = &postgres_unlock_inventory; - plugin->insert_order_lock = &postgres_insert_order_lock; - plugin->lookup_contract_terms = &postgres_lookup_contract_terms; - plugin->insert_contract_terms = &postgres_insert_contract_terms; - plugin->update_contract_terms = &postgres_update_contract_terms; - plugin->delete_contract_terms = &postgres_delete_contract_terms; - plugin->lookup_deposits = &postgres_lookup_deposits; - plugin->insert_exchange_signkey = &postgres_insert_exchange_signkey; - plugin->insert_deposit = &postgres_insert_deposit; - plugin->lookup_refunds = &postgres_lookup_refunds; - plugin->mark_contract_paid = &postgres_mark_contract_paid; - plugin->refund_coin = &postgres_refund_coin; - plugin->lookup_order_status = &postgres_lookup_order_status; - plugin->lookup_order_status_by_serial = - &postgres_lookup_order_status_by_serial; - plugin->lookup_payment_status = &postgres_lookup_payment_status; - plugin->lookup_deposits_by_order = &postgres_lookup_deposits_by_order; - plugin->lookup_transfer_details_by_order = - &postgres_lookup_transfer_details_by_order; - plugin->insert_deposit_to_transfer = &postgres_insert_deposit_to_transfer; - plugin->mark_order_wired = &postgres_mark_order_wired; - plugin->increase_refund = &postgres_increase_refund; - plugin->lookup_refunds_detailed = &postgres_lookup_refunds_detailed; - plugin->insert_refund_proof = &postgres_insert_refund_proof; - plugin->lookup_refund_proof = &postgres_lookup_refund_proof; - plugin->lookup_order_by_fulfillment = &postgres_lookup_order_by_fulfillment; - plugin->insert_transfer = &postgres_insert_transfer; - plugin->delete_transfer = &postgres_delete_transfer; - plugin->check_transfer_exists = &postgres_check_transfer_exists; - plugin->lookup_account = &postgres_lookup_account; - plugin->insert_transfer_details = &postgres_insert_transfer_details; - plugin->lookup_wire_fee = &postgres_lookup_wire_fee; - plugin->lookup_deposits_by_contract_and_coin = - &postgres_lookup_deposits_by_contract_and_coin; - plugin->lookup_transfer = &postgres_lookup_transfer; - plugin->set_transfer_status_to_verified = - &postgres_set_transfer_status_to_verified; - plugin->lookup_transfer_summary = &postgres_lookup_transfer_summary; - plugin->lookup_transfer_details = &postgres_lookup_transfer_details; - plugin->lookup_transfers = &postgres_lookup_transfers; - plugin->store_wire_fee_by_exchange = &postgres_store_wire_fee_by_exchange; - plugin->insert_reserve = &postgres_insert_reserve; - plugin->activate_reserve = &postgres_activate_reserve; - plugin->lookup_reserves = &postgres_lookup_reserves; - plugin->lookup_pending_reserves = &postgres_lookup_pending_reserves; - plugin->lookup_reserve = &postgres_lookup_reserve; - plugin->delete_reserve = &postgres_delete_reserve; - plugin->purge_reserve = &postgres_purge_reserve; - plugin->authorize_tip = &postgres_authorize_tip; - plugin->lookup_pickup = &postgres_lookup_pickup; - plugin->lookup_tip = &postgres_lookup_tip; - plugin->lookup_tips = &postgres_lookup_tips; - plugin->lookup_tip_details = &postgres_lookup_tip_details; - plugin->insert_pickup = &postgres_insert_pickup; - plugin->insert_pickup_blind_signature = - &postgres_insert_pickup_blind_signature; - plugin->lookup_templates = &postgres_lookup_templates; - plugin->lookup_template = &postgres_lookup_template; - plugin->delete_template = &postgres_delete_template; - plugin->insert_template = &postgres_insert_template; - plugin->update_template = &postgres_update_template; - plugin->lookup_webhooks = &postgres_lookup_webhooks; - plugin->lookup_webhook = &postgres_lookup_webhook; - plugin->delete_webhook = &postgres_delete_webhook; - plugin->insert_webhook = &postgres_insert_webhook; - plugin->update_webhook = &postgres_update_webhook; - plugin->lookup_pending_webhook = &postgres_lookup_pending_webhook; - plugin->lookup_webhook_by_event = &postgres_lookup_webhook_by_event; - plugin->lookup_all_webhooks = &postgres_lookup_all_webhooks; - plugin->lookup_future_webhook = &postgres_lookup_future_webhook; - plugin->lookup_pending_webhooks = &postgres_lookup_pending_webhooks; - plugin->delete_pending_webhook = &postgres_delete_pending_webhook; - plugin->insert_pending_webhook = &postgres_insert_pending_webhook; - plugin->update_pending_webhook = &postgres_update_pending_webhook; + = &TMH_PG_account_kyc_get_status; + plugin->delete_instance_private_key + = &TMH_PG_delete_instance_private_key; + plugin->purge_instance + = &TMH_PG_purge_instance; + plugin->update_instance + = &TMH_PG_update_instance; + plugin->update_instance_auth + = &TMH_PG_update_instance_auth; + plugin->activate_account + = &TMH_PG_activate_account; + plugin->inactivate_account + = &TMH_PG_inactivate_account; + plugin->update_transfer_status + = &TMH_PG_update_transfer_status; + plugin->lookup_products + = &TMH_PG_lookup_products; + plugin->lookup_product + = &TMH_PG_lookup_product; + plugin->delete_product + = &TMH_PG_delete_product; + plugin->insert_product + = &TMH_PG_insert_product; + plugin->update_product + = &TMH_PG_update_product; + plugin->insert_otp + = &TMH_PG_insert_otp; + plugin->delete_otp + = &TMH_PG_delete_otp; + plugin->update_otp + = &TMH_PG_update_otp; + plugin->select_otp + = &TMH_PG_select_otp; + plugin->select_otp_serial + = &TMH_PG_select_otp_serial; + plugin->lock_product + = &TMH_PG_lock_product; + plugin->expire_locks + = &TMH_PG_expire_locks; + plugin->delete_order + = &TMH_PG_delete_order; + plugin->lookup_order + = &TMH_PG_lookup_order; + plugin->lookup_order_summary + = &TMH_PG_lookup_order_summary; + plugin->lookup_orders + = &TMH_PG_lookup_orders; + plugin->insert_order + = &TMH_PG_insert_order; + plugin->unlock_inventory + = &TMH_PG_unlock_inventory; + plugin->insert_order_lock + = &TMH_PG_insert_order_lock; + plugin->lookup_contract_terms + = &TMH_PG_lookup_contract_terms; + plugin->lookup_contract_terms2 + = &TMH_PG_lookup_contract_terms2; + plugin->lookup_contract_terms3 + = &TMH_PG_lookup_contract_terms3; + plugin->insert_contract_terms + = &TMH_PG_insert_contract_terms; + plugin->update_contract_terms + = &TMH_PG_update_contract_terms; + plugin->delete_contract_terms + = &TMH_PG_delete_contract_terms; + plugin->lookup_deposits + = &TMH_PG_lookup_deposits; + plugin->insert_exchange_signkey + = &TMH_PG_insert_exchange_signkey; + plugin->insert_deposit_confirmation + = &TMH_PG_insert_deposit_confirmation; + plugin->insert_deposit + = &TMH_PG_insert_deposit; + plugin->lookup_refunds + = &TMH_PG_lookup_refunds; + plugin->mark_contract_paid + = &TMH_PG_mark_contract_paid; + plugin->refund_coin + = &TMH_PG_refund_coin; + plugin->lookup_order_status + = &TMH_PG_lookup_order_status; + plugin->lookup_order_status_by_serial + = &TMH_PG_lookup_order_status_by_serial; + plugin->lookup_deposits_by_order + = &TMH_PG_lookup_deposits_by_order; + plugin->lookup_transfer_details_by_order + = &TMH_PG_lookup_transfer_details_by_order; + plugin->mark_order_wired + = &TMH_PG_mark_order_wired; + plugin->increase_refund + = &TMH_PG_increase_refund; + plugin->lookup_refunds_detailed + = &TMH_PG_lookup_refunds_detailed; + plugin->insert_refund_proof + = &TMH_PG_insert_refund_proof; + plugin->lookup_refund_proof + = &TMH_PG_lookup_refund_proof; + plugin->lookup_order_by_fulfillment + = &TMH_PG_lookup_order_by_fulfillment; + plugin->delete_transfer + = &TMH_PG_delete_transfer; + plugin->check_transfer_exists + = &TMH_PG_check_transfer_exists; + plugin->lookup_account + = &TMH_PG_lookup_account; + plugin->lookup_wire_fee + = &TMH_PG_lookup_wire_fee; + plugin->lookup_deposits_by_contract_and_coin + = &TMH_PG_lookup_deposits_by_contract_and_coin; + plugin->lookup_transfer + = &TMH_PG_lookup_transfer; + plugin->set_transfer_status_to_confirmed + = &TMH_PG_set_transfer_status_to_confirmed; + plugin->lookup_transfer_summary + = &TMH_PG_lookup_transfer_summary; + plugin->lookup_transfer_details + = &TMH_PG_lookup_transfer_details; + plugin->lookup_instances + = &TMH_PG_lookup_instances; + plugin->lookup_instance + = &TMH_PG_lookup_instance; + plugin->lookup_transfers + = &TMH_PG_lookup_transfers; + plugin->update_wirewatch_progress + = &TMH_PG_update_wirewatch_progress; + plugin->select_wirewatch_accounts + = &TMH_PG_select_wirewatch_accounts; + plugin->select_account + = &TMH_PG_select_account; + plugin->select_accounts + = &TMH_PG_select_accounts; + plugin->select_open_transfers + = &TMH_PG_select_open_transfers; + plugin->insert_exchange_keys + = &TMH_PG_insert_exchange_keys; + plugin->select_exchange_keys + = &TMH_PG_select_exchange_keys; + plugin->insert_deposit_to_transfer + = &TMH_PG_insert_deposit_to_transfer; + plugin->insert_transfer + = &TMH_PG_insert_transfer; + plugin->insert_transfer_details + = &TMH_PG_insert_transfer_details; + plugin->store_wire_fee_by_exchange + = &TMH_PG_store_wire_fee_by_exchange; + plugin->lookup_webhooks + = &TMH_PG_lookup_webhooks; + plugin->lookup_webhook + = &TMH_PG_lookup_webhook; + plugin->delete_webhook + = &TMH_PG_delete_webhook; + plugin->insert_webhook + = &TMH_PG_insert_webhook; + plugin->update_webhook + = &TMH_PG_update_webhook; + plugin->lookup_pending_deposits + = &TMH_PG_lookup_pending_deposits; + plugin->lookup_webhook_by_event + = &TMH_PG_lookup_webhook_by_event; + plugin->lookup_all_webhooks + = &TMH_PG_lookup_all_webhooks; + plugin->lookup_future_webhook + = &TMH_PG_lookup_future_webhook; + plugin->lookup_pending_webhooks + = &TMH_PG_lookup_pending_webhooks; + plugin->delete_pending_webhook + = &TMH_PG_delete_pending_webhook; + plugin->insert_pending_webhook + = &TMH_PG_insert_pending_webhook; + plugin->update_pending_webhook + = &TMH_PG_update_pending_webhook; + plugin->delete_exchange_accounts + = &TMH_PG_delete_exchange_accounts; + plugin->select_accounts_by_exchange + = &TMH_PG_select_accounts_by_exchange; + plugin->insert_exchange_account + = &TMH_PG_insert_exchange_account; + plugin->insert_token_family + = &TMH_PG_insert_token_family; + plugin->lookup_token_family + = &TMH_PG_lookup_token_family; + plugin->lookup_token_families + = &TMH_PG_lookup_token_families; + plugin->delete_token_family + = &TMH_PG_delete_token_family; + plugin->update_token_family + = &TMH_PG_update_token_family; + plugin->insert_token_family_key + = &TMH_PG_insert_token_family_key; + plugin->lookup_token_family_key + = &TMH_PG_lookup_token_family_key; + plugin->update_deposit_confirmation_status + = &TMH_PG_update_deposit_confirmation_status; + + return plugin; } @@ -10693,7 +607,6 @@ libtaler_plugin_merchantdb_postgres_done (void *cls) pg->conn = NULL; } GNUNET_free (pg->sql_dir); - GNUNET_free (pg->currency); GNUNET_free (pg); GNUNET_free (plugin); return NULL; |