From 054e157af8c768062dd0a8e66614da18407fca28 Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Fri, 14 Oct 2022 18:56:59 +0200 Subject: WIP: policy_details handling continued - policy details generated on deposit/batch-deposit requests - insert or update of policy details in the DB - accumulation of amounts of multiple deposits for the same policy_details --- src/exchangedb/plugin_exchangedb_postgres.c | 302 +++++++++++++++++++++++----- 1 file changed, 256 insertions(+), 46 deletions(-) (limited to 'src/exchangedb/plugin_exchangedb_postgres.c') diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 415417cb..062133e6 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -822,8 +822,8 @@ prepare_statements (struct PostgresClosure *pg) ",out_balance_ok AS balance_ok" ",out_conflict AS conflicted" " FROM exchange_do_deposit" - " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20);", - 20), + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);", + 17), /* used in postgres_do_purse_deposit() */ GNUNET_PQ_make_prepare ( "call_purse_deposit", @@ -3927,37 +3927,45 @@ prepare_statements (struct PostgresClosure *pg) "($1, $2, $3);", 3), GNUNET_PQ_make_prepare ( - "insert_into_table_policy_details", - "INSERT INTO policy_details" - "(policy_details_serial_id" - ",serial_id" - ",policy_options" - ",deadline" - ",timeout_fulfilment_state" - ",fulfilment_state" - ") VALUES " - "($1, $2, $3, $4, $5, $6);", - 6), - GNUNET_PQ_make_prepare ( - "insert_into_table_policy_fulfilments", - "INSERT INTO policy_fulfilments" - "(fulfilment_id" - ",fulfilment_timestamp" - ",fulfilment_proof" + "call_insert_or_update_policy_details", + "SELECT " + " out_policy_details_serial_id as policy_serial_id" + ",out_accumulated_total_val as accumulated_total_val" + ",out_accumulated_total_frac as accumulated_total_frac" + ",out_fulfillment_state as fulfillment_state" + " FROM insert_or_update_policy_details" + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);", + 12), + GNUNET_PQ_make_prepare ( + "insert_proof_into_policy_fulfillments", + "INSERT INTO policy_fulfillments " + "(fulfillment_timestamp " + ",fulfillment_proof" + ",h_fulfillment_proof" + ",policy_hash_codes" ") VALUES " - "($1, $2, $3);", - 3), + "($1, $2, $3, $4) " + "RETURNING fulfillment_id;", + 4), GNUNET_PQ_make_prepare ( - "insert_into_table_policy_details_fulfilments", - "INSERT INTO policy_details_fulfilments" - "(fulfilment_id" - ",serial_id" - ") VALUES " - "($1, $2);", - 2), + "update_policy_details", + "UPDATE policy_details " + "SET " + " deadline=$2," + " commitment_val=$3," + " commitment_frac=$4," + " accumulated_total_val=$5," + " accumulated_total_frac=$6," + " fee_val=$7," + " fee_frac=$8," + " transferable_val=$9," + " transferable_frac=$10," + " fulfillment_state=$11 " + "WHERE policy_hash_code=$1;", + 11), GNUNET_PQ_make_prepare ( "insert_into_table_purse_requests", - "INSERT INTO purse_requests" + "INSERT INTO purse_requests " "(purse_requests_serial_id" ",purse_pub" ",merge_pub" @@ -6250,7 +6258,7 @@ compute_shard (const struct TALER_MerchantPublicKeyP *merchant_pub) * @param deposit deposit operation details * @param known_coin_id row of the coin in the known_coins table * @param h_payto hash of the merchant's bank account details - * @param policy_blocked true if an extension is blocking the wire transfer + * @param policy_details_serial_id (pointer to) the row ID in the policy_details table, maybe NULL. * @param[in,out] exchange_timestamp time to use for the deposit (possibly updated) * @param[out] balance_ok set to true if the balance was sufficient * @param[out] in_conflict set to true if the deposit conflicted @@ -6262,6 +6270,7 @@ postgres_do_deposit ( const struct TALER_EXCHANGEDB_Deposit *deposit, uint64_t known_coin_id, const struct TALER_PaytoHashP *h_payto, + uint64_t *policy_details_serial_id, struct GNUNET_TIME_Timestamp *exchange_timestamp, bool *balance_ok, bool *in_conflict) @@ -6283,19 +6292,10 @@ postgres_do_deposit ( GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub), GNUNET_PQ_query_param_auto_from_type (&deposit->csig), GNUNET_PQ_query_param_uint64 (&deposit_shard), - GNUNET_PQ_query_param_bool (deposit->has_policy_details), - (deposit->has_policy_details) - ? TALER_PQ_query_param_json (deposit->policy_details) - : GNUNET_PQ_query_param_null (), - (deposit->has_policy_details) - ? GNUNET_PQ_query_param_auto_from_type (&deposit->policy_serial_id) - : GNUNET_PQ_query_param_null (), - (deposit->has_policy_details) - ? GNUNET_PQ_query_param_timestamp (&deposit->policy_deadline) - : GNUNET_PQ_query_param_null (), - (deposit->has_policy_details) - ? GNUNET_PQ_query_param_uint16 (&deposit->policy_state_on_timeout) - : GNUNET_PQ_query_param_null (), + GNUNET_PQ_query_param_bool (deposit->has_policy), + (NULL == policy_details_serial_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_uint64 (policy_details_serial_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -6315,6 +6315,101 @@ postgres_do_deposit ( } +/* Get the details of a policy, referenced by its hash code + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param hc The hash code under which the details to a particular policy should be found + * @param[out] details The found details + * @return query execution status + * */ +static enum GNUNET_DB_QueryStatus +postgres_get_policy_details ( + void *cls, + const struct GNUNET_HashCode *hc, + struct TALER_PolicyDetails *details) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (hc), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("deadline", + &details->deadline), + TALER_PQ_RESULT_SPEC_AMOUNT ("commitment", + &details->commitment), + TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total", + &details->accumulated_total), + TALER_PQ_RESULT_SPEC_AMOUNT ("policy_fee", + &details->policy_fee), + TALER_PQ_RESULT_SPEC_AMOUNT ("transferable_amount", + &details->transferable_amount), + GNUNET_PQ_result_spec_auto_from_type ("state", + &details->fulfillment_state), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint64 ("policy_fulfillment_id", + &details->policy_fulfillment_id), + &details->no_policy_fulfillment_id), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "get_policy_details", + params, + rs); +} + + +/* Persist the details to a policy in the policy_details table. If there + * already exists a policy, update the fields accordingly. + * + * @param details The policy details that should be persisted. If an entry for + * the given details->hash_code exists, the values will be updated. + * @param[out] policy_details_serial_id The row ID of the policy details + * @param[out] accumulated_total The total amount accumulated in that policy + * @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready. + * @return query execution status + */ +static enum GNUNET_DB_QueryStatus +postgres_persist_policy_details ( + void *cls, + const struct TALER_PolicyDetails *details, + uint64_t *policy_details_serial_id, + struct TALER_Amount *accumulated_total, + enum TALER_PolicyFulfillmentState *fulfillment_state) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (&details->hash_code), + TALER_PQ_query_param_json (details->policy_json), + GNUNET_PQ_query_param_timestamp (&details->deadline), + TALER_PQ_query_param_amount (&details->commitment), + TALER_PQ_query_param_amount (&details->accumulated_total), + TALER_PQ_query_param_amount (&details->policy_fee), + TALER_PQ_query_param_amount (&details->transferable_amount), + GNUNET_PQ_query_param_auto_from_type (&details->fulfillment_state), + (details->no_policy_fulfillment_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_uint64 (&details->policy_fulfillment_id), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("policy_details_serial_id", + policy_details_serial_id), + TALER_PQ_RESULT_SPEC_AMOUNT ("accumulated_total", + accumulated_total), + GNUNET_PQ_result_spec_uint32 ("fulfillment_state", + fulfillment_state), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "call_insert_or_update_policy_details", + params, + rs); +} + + /** * Perform melt operation, checking for sufficient balance * of the coin and possibly persisting the melt details. @@ -6569,6 +6664,118 @@ postgres_do_recoup_refresh ( } +/* + * Compares two indices into an array of hash codes according to + * GNUNET_CRYPTO_hash_cmp of the content at those index positions. + * + * Used in a call qsort_t in order to generate sorted policy_hash_codes. + */ +static int +hash_code_cmp ( + const void *hc1, + const void *hc2, + void *arg) +{ + size_t i1 = *(size_t *) hc1; + size_t i2 = *(size_t *) hc2; + const struct TALER_PolicyDetails *d = arg; + + return GNUNET_CRYPTO_hash_cmp (&d[i1].hash_code, + &d[i2].hash_code); +} + + +/** + * Add a proof of fulfillment into the policy_fulfillments table + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param[out] proof_id set record id for the proof + * @return query execution status + */ +static enum GNUNET_DB_QueryStatus +postgres_add_policy_fulfillment_proof ( + void *cls, + struct TALER_PolicyFulfillmentTransactionData *fulfillment) +{ + enum GNUNET_DB_QueryStatus qs; + struct PostgresClosure *pg = cls; + size_t count = fulfillment->details_count; + struct GNUNET_HashCode hcs[count]; + + /* Create the sorted policy_hash_codes */ + { + size_t idx[count]; + for (size_t i = 0; i < count; i++) + idx[i] = i; + + /* Sort the indices according to the hash codes of the corresponding + * details. */ + qsort_r (idx, + count, + sizeof(size_t), + hash_code_cmp, + fulfillment->details); + + /* Finally, concatenate all hash_codes in sorted order */ + for (size_t i = 0; i < count; i++) + hcs[i] = fulfillment->details[idx[i]].hash_code; + } + + + /* Now, add the proof to the policy_fulfillments table, retrieve the + * record_id */ + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_timestamp (&fulfillment->timestamp), + TALER_PQ_query_param_json (fulfillment->proof), + GNUNET_PQ_query_param_auto_from_type (&fulfillment->h_proof), + GNUNET_PQ_query_param_fixed_size (hcs, + count * sizeof(struct GNUNET_HashCode)), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("fulfillment_id", + &fulfillment->fulfillment_id), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "insert_proof_into_policy_fulfillments", + params, + rs); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + return qs; + } + + /* Now, set the states of each entry corresponding to the hash_codes in + * policy_details accordingly */ + for (size_t i = 0; i < count; i++) + { + struct TALER_PolicyDetails *pos = &fulfillment->details[i]; + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (&pos->hash_code), + GNUNET_PQ_query_param_timestamp (&pos->deadline), + TALER_PQ_query_param_amount (&pos->commitment), + TALER_PQ_query_param_amount (&pos->accumulated_total), + TALER_PQ_query_param_amount (&pos->policy_fee), + TALER_PQ_query_param_amount (&pos->transferable_amount), + GNUNET_PQ_query_param_auto_from_type (&pos->fulfillment_state), + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "update_policy_details", + params); + if (qs < 0) + return qs; + } + } + + return qs; +} + + /** * Closure for callbacks invoked via #postgres_get_reserve_history. */ @@ -14470,8 +14677,8 @@ postgres_lookup_records_by_table (void *cls, rh = &lrbt_cb_table_policy_details; break; case TALER_EXCHANGEDB_RT_POLICY_FULFILMENTS: - statement = "select_above_serial_by_table_policy_fulfilments"; - rh = &lrbt_cb_table_policy_fulfilments; + statement = "select_above_serial_by_table_policy_fulfillments"; + rh = &lrbt_cb_table_policy_fulfillments; break; case TALER_EXCHANGEDB_RT_PURSE_REQUESTS: statement = "select_above_serial_by_table_purse_requests"; @@ -14655,7 +14862,7 @@ postgres_insert_records_by_table (void *cls, case TALER_EXCHANGEDB_RT_POLICY_DETAILS: rh = &irbt_cb_table_policy_details; break; - /* TODO: policy_details_fulfilments and policy_fulfilments */ + /* TODO: policy_details_fulfillments and policy_fulfillments */ case TALER_EXCHANGEDB_RT_PURSE_REQUESTS: rh = &irbt_cb_table_purse_requests; break; @@ -17110,7 +17317,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->do_withdraw = &postgres_do_withdraw; plugin->do_batch_withdraw = &postgres_do_batch_withdraw; plugin->do_batch_withdraw_insert = &postgres_do_batch_withdraw_insert; + plugin->get_policy_details = &postgres_get_policy_details; + plugin->persist_policy_details = &postgres_persist_policy_details; plugin->do_deposit = &postgres_do_deposit; + plugin->add_policy_fulfillment_proof = &postgres_add_policy_fulfillment_proof; plugin->do_melt = &postgres_do_melt; plugin->do_refund = &postgres_do_refund; plugin->do_recoup = &postgres_do_recoup; -- cgit v1.2.3