exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

get_pending_aggregation.c (6302B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2026 Taler Systems SA
      4 
      5    TALER is free software; you can redistribute it and/or modify it under the
      6    terms of the GNU General Public License as published by the Free Software
      7    Foundation; either version 3, or (at your option) any later version.
      8 
      9    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13    You should have received a copy of the GNU General Public License along with
     14    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 /**
     17  * @file src/exchangedb/get_pending_aggregation.c
     18  * @brief Implementation of the get_pending_aggregation function for Postgres
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/taler_pq_lib.h"
     22 #include "helper.h"
     23 #include "exchange-database/get_pending_aggregation.h"
     24 
     25 
     26 enum GNUNET_DB_QueryStatus
     27 TALER_EXCHANGEDB_get_pending_aggregation (
     28   struct TALER_EXCHANGEDB_PostgresContext *pg,
     29   const struct TALER_WireTransferIdentifierRawP *wtid,
     30   struct TALER_FullPaytoHashP *wire_target_h_payto,
     31   struct TALER_FullPayto *payto_uri,
     32   struct TALER_Amount *total_deposited,
     33   struct TALER_Amount *total_refunded,
     34   struct TALER_Amount *total_deposit_fee)
     35 {
     36   struct GNUNET_PQ_QueryParam params[] = {
     37     GNUNET_PQ_query_param_auto_from_type (wtid),
     38     GNUNET_PQ_query_param_end
     39   };
     40   struct GNUNET_PQ_ResultSpec rs[] = {
     41     GNUNET_PQ_result_spec_auto_from_type ("wire_target_h_payto",
     42                                           wire_target_h_payto),
     43     GNUNET_PQ_result_spec_string ("payto_uri",
     44                                   &payto_uri->full_payto),
     45     TALER_PQ_RESULT_SPEC_AMOUNT ("total_deposited",
     46                                  total_deposited),
     47     TALER_PQ_RESULT_SPEC_AMOUNT ("total_refunded",
     48                                  total_refunded),
     49     TALER_PQ_RESULT_SPEC_AMOUNT ("total_deposit_fee",
     50                                  total_deposit_fee),
     51     GNUNET_PQ_result_spec_end
     52   };
     53 
     54   /* This mirrors the arithmetic of `exchange_do_aggregate' (see
     55      do_aggregate.c) so that the result can be compared with what the exchange
     56      itself computed.  The one subtlety worth preserving is
     57      `fully_refunded_coins': the exchange does not keep the deposit fee of a
     58      coin whose deposit was refunded in full, so neither may we.
     59 
     60      The `NOT EXISTS' on wire_out is what makes this a *pending* aggregation;
     61      once the transfer is executed the query returns no rows and the caller
     62      drops the hold. */
     63   PREPARE (pg,
     64            "get_pending_aggregation",
     65            "WITH bd AS ("
     66            "  SELECT bdep.batch_deposit_serial_id"
     67            "        ,bdep.wire_target_h_payto"
     68            "    FROM aggregation_tracking atr"
     69            "    JOIN batch_deposits bdep"
     70            "      USING (batch_deposit_serial_id)"
     71            "   WHERE atr.wtid_raw=$1)"
     72            " ,cd AS ("
     73            "  SELECT cdep.coin_pub"
     74            "        ,cdep.batch_deposit_serial_id"
     75            "        ,cdep.amount_with_fee AS amount"
     76            "    FROM coin_deposits cdep"
     77            "   WHERE cdep.batch_deposit_serial_id IN"
     78            "         (SELECT batch_deposit_serial_id FROM bd))"
     79            " ,ref AS ("
     80            "  SELECT r.amount_with_fee AS refund"
     81            "        ,r.coin_pub"
     82            "        ,r.batch_deposit_serial_id"
     83            "    FROM refunds r"
     84            "   WHERE r.batch_deposit_serial_id IN"
     85            "         (SELECT batch_deposit_serial_id FROM bd))"
     86            " ,ref_by_coin AS ("
     87            "  SELECT SUM((ref.refund).val) AS sum_val"
     88            "        ,SUM((ref.refund).frac) AS sum_frac"
     89            "        ,coin_pub"
     90            "        ,batch_deposit_serial_id"
     91            "    FROM ref"
     92            "   GROUP BY coin_pub, batch_deposit_serial_id)"
     93            " ,fully_refunded_coins AS ("
     94            "  SELECT cd.coin_pub"
     95            "        ,cd.batch_deposit_serial_id"
     96            "    FROM ref_by_coin n"
     97            "    JOIN cd"
     98            "      ON (n.coin_pub = cd.coin_pub"
     99            "      AND n.batch_deposit_serial_id = cd.batch_deposit_serial_id"
    100            "      AND n.sum_val + n.sum_frac / 100000000 = (cd.amount).val"
    101            "      AND n.sum_frac % 100000000 = (cd.amount).frac))"
    102            " ,fees AS ("
    103            "  SELECT denom.fee_deposit AS fee"
    104            "    FROM cd"
    105            "    JOIN known_coins kc"
    106            "      USING (coin_pub)"
    107            "    JOIN denominations denom"
    108            "      USING (denominations_serial)"
    109            "   WHERE (cd.coin_pub, cd.batch_deposit_serial_id) NOT IN"
    110            "         (SELECT coin_pub, batch_deposit_serial_id"
    111            "            FROM fully_refunded_coins))"
    112            "SELECT"
    113            "  (SELECT wire_target_h_payto FROM bd LIMIT 1)"
    114            "    AS wire_target_h_payto"
    115            " ,(SELECT wt.payto_uri"
    116            "     FROM wire_targets wt"
    117            "    WHERE wt.wire_target_h_payto="
    118            "          (SELECT wire_target_h_payto FROM bd LIMIT 1))"
    119            "    AS payto_uri"
    120            " ,ROW(COALESCE(SUM((cd.amount).val),0)"
    121            "      + COALESCE(SUM((cd.amount).frac),0) / 100000000"
    122            "     ,COALESCE(SUM((cd.amount).frac),0) % 100000000)::taler_amount"
    123            "    AS total_deposited"
    124            " ,(SELECT ROW(COALESCE(SUM((ref.refund).val),0)"
    125            "              + COALESCE(SUM((ref.refund).frac),0) / 100000000"
    126            "             ,COALESCE(SUM((ref.refund).frac),0)"
    127            "              % 100000000)::taler_amount"
    128            "     FROM ref)"
    129            "    AS total_refunded"
    130            " ,(SELECT ROW(COALESCE(SUM((fees.fee).val),0)"
    131            "              + COALESCE(SUM((fees.fee).frac),0) / 100000000"
    132            "             ,COALESCE(SUM((fees.fee).frac),0)"
    133            "              % 100000000)::taler_amount"
    134            "     FROM fees)"
    135            "    AS total_deposit_fee"
    136            "  FROM cd"
    137            " WHERE NOT EXISTS ("
    138            "   SELECT 1 FROM wire_out wo WHERE wo.wtid_raw=$1)"
    139            " HAVING COUNT(*) > 0;");
    140   return GNUNET_PQ_eval_prepared_singleton_select (
    141     pg->conn,
    142     "get_pending_aggregation",
    143     params,
    144     rs);
    145 }