diff options
Diffstat (limited to 'src/backenddb/plugin_merchantdb_postgres.c')
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 375 |
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 |