summaryrefslogtreecommitdiff
path: root/src/backenddb/plugin_merchantdb_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backenddb/plugin_merchantdb_postgres.c')
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c375
1 files changed, 196 insertions, 179 deletions
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index 97c9c3ab..8f962953 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -4203,6 +4203,51 @@ postgres_purge_reserve (void *cls,
/**
+ * 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_Absolute expiration;
+};
+
+
+/**
+ * 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_result 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;
+}
+
+
+/**
* 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.
@@ -4234,22 +4279,20 @@ postgres_authorize_tip (void *cls,
struct GNUNET_TIME_Absolute *expiration)
{
struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (instance_id),
- // FIXME: not so easy: reserve_pub MAY be NULL!
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- TALER_PQ_query_param_amount (amount),
- GNUNET_PQ_query_param_string (justification),
- GNUNET_PQ_query_param_string (next_url),
- GNUNET_PQ_query_param_auto_from_type (tip_id),
- GNUNET_PQ_query_param_end
- };
+ 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_ABS
+ };
- retries = 0;
check_connection (pg);
RETRY:
+ reserve_pubp = reserve_pub;
if (MAX_RETRIES < ++retries)
return GNUNET_DB_STATUS_SOFT_ERROR; // FIXME: wrong EC!
if (GNUNET_OK !=
@@ -4259,7 +4302,7 @@ RETRY:
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: wrong EC!
}
- if (NULL == reserve_pub)
+ if (NULL == reserve_pubp)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
@@ -4267,29 +4310,152 @@ RETRY:
GNUNET_PQ_query_param_end
};
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "lookup_reserve",
+ "lookup_reserve", // FIXME: write SQL!
params,
- &lookup_accounts_cb,
- lic);
-
+ &lookup_reserve_for_tip_cb,
+ &lac);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ postgres_rollback (pg);
+ goto RETRY;
+ }
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: wrong EC!
+ }
+ if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us ==
+ lac.expiration.abs_value_us)
+ {
+ postgres_rollback (pg);
+ return 0; // FIXME: EC for 'no reserve available!'
+ }
+ 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),
+ TALER_PQ_query_param_amount (amount),
+ GNUNET_PQ_query_param_string (justification),
+ GNUNET_PQ_query_param_string (next_url),
+ GNUNET_PQ_query_param_auto_from_type (tip_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_absolute_time ("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
+ };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_absolute_time ("expiration",
- &old_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", // FIXME: write SQL!
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ postgres_rollback (pg);
+ goto RETRY;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: ec!
+ }
+ }
+ {
+ struct TALER_Amount remaining;
+ if (0 >=
+ TALER_amount_subtract (&remaining,
+ &exchange_initial_balance,
+ &tips_committed))
+ {
+ GNUNET_break (0);
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: EC: invariant failure!
+ }
+ if (0 <
+ TALER_amount_cmp (&remaining,
+ amount))
+ {
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: EC: 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
+ };
- check_connection (pg);
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "purge_reserve",
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_reserve_tips_committed", // FIXME: write SQL!
params);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ postgres_rollback (pg);
+ goto RETRY;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: ec!
+ }
+ }
+ 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),
+ TALER_PQ_query_param_amount (amount),
+ GNUNET_PQ_query_param_string (justification),
+ GNUNET_PQ_query_param_string (next_url),
+ GNUNET_PQ_query_param_auto_from_type (tip_id),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_reserve_tips_committed",// FIXME: write SQL!
+ params);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ postgres_rollback (pg);
+ goto RETRY;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: ec!
+ }
+ }
+ qs = postgres_commit (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ postgres_rollback (pg);
+ return GNUNET_DB_STATUS_HARD_ERROR; // FIXME: ec!
+ }
+ return TALER_EC_NONE;
}
@@ -4364,8 +4530,8 @@ postgres_lookup_tip (void *cls,
expiration),
GNUNET_PQ_result_spec_string ("exchange_url",
exchange_url),
- GNUNET_PQ_result_spec_fixed_auto ("reserve_priv",
- reserve_priv),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
+ reserve_priv),
GNUNET_PQ_result_spec_end
};
@@ -4944,155 +5110,6 @@ RETRY:
/**
- * Authorize a tip over @a amount from reserve @a reserve_priv. Remember
- * the authorization under @a tip_id for later, together with the
- * @a justification.
- *
- * @param cls closure, typically a connection to the db
- * @param justification why was the tip approved
- * @param extra extra data for the customer's wallet
- * @param amount how high is the tip (with fees)
- * @param reserve_priv which reserve is debited
- * @param exchange_url which exchange manages the tip
- * @param[out] expiration set to when the tip expires
- * @param[out] tip_id set to the unique ID for the tip
- * @return taler error code
- * #TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
- * #TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN if the reserve is not known
- * #TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
- * #TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR on hard DB errors
- * #TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR on soft DB errors (client should retry)
- * #TALER_EC_NONE upon success
- */
-static enum TALER_ErrorCode
-postgres_authorize_tip (void *cls,
- const char *justification,
- const json_t *extra,
- const struct TALER_Amount *amount,
- const struct TALER_ReservePrivateKeyP *reserve_priv,
- const char *exchange_url,
- struct GNUNET_TIME_Absolute *expiration,
- struct GNUNET_HashCode *tip_id)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_priv),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_TIME_Absolute old_expiration;
- struct TALER_Amount old_balance;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_absolute_time ("expiration",
- &old_expiration),
- TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
- &old_balance),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_Amount new_balance;
- unsigned int retries;
-
- retries = 0;
- check_connection (pg);
-RETRY:
- if (MAX_RETRIES < ++retries)
- return TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR;
- if (GNUNET_OK !=
- postgres_start (pg,
- "authorize tip"))
- {
- GNUNET_break (0);
- return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
- }
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "lookup_tip_reserve_balance",
- params,
- rs);
- if (0 >= qs)
- {
- /* reserve unknown */
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
- return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
- }
- if (0 == GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us)
- {
- /* reserve expired, can't be used */
- postgres_rollback (pg);
- return TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED;
- }
- if (0 >
- TALER_amount_subtract (&new_balance,
- &old_balance,
- amount))
- {
- /* insufficient funds left in reserve */
- postgres_rollback (pg);
- return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
- }
- /* Update reserve balance */
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_priv),
- GNUNET_PQ_query_param_absolute_time (&old_expiration),
- TALER_PQ_query_param_amount (&new_balance),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_tip_reserve_balance",
- params);
- if (0 > qs)
- {
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
- }
- }
- /* Generate and store tip ID */
- *expiration = old_expiration;
- GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG,
- tip_id);
- {
- struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_priv),
- GNUNET_PQ_query_param_auto_from_type (tip_id),
- GNUNET_PQ_query_param_string (exchange_url),
- GNUNET_PQ_query_param_string (justification),
- TALER_PQ_query_param_json (extra),
- GNUNET_PQ_query_param_absolute_time (&now),
- TALER_PQ_query_param_amount (amount), /* overall amount */
- TALER_PQ_query_param_amount (amount), /* amount left */
- GNUNET_PQ_query_param_end
- };
-
- (void) GNUNET_TIME_round_abs (&now);
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_tip_justification",
- params);
- if (0 > qs)
- {
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
- }
- }
- qs = postgres_commit (pg);
- if (0 <= qs)
- return TALER_EC_NONE; /* success! */
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
-}
-
-
-/**
* Find out tip authorization details associated with @a tip_id
*
* @param cls closure, typically a connection to the d