exchange

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

exchange_do_purse_merge.sql (5888B)


      1 --
      2 -- This file is part of TALER
      3 -- Copyright (C) 2014--2022 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 CREATE OR REPLACE FUNCTION exchange_do_purse_merge(
     18   IN in_purse_pub BYTEA,
     19   IN in_merge_sig BYTEA,
     20   IN in_merge_timestamp INT8,
     21   IN in_reserve_sig BYTEA,
     22   IN in_partner_url TEXT,
     23   IN in_reserve_pub BYTEA,
     24   IN in_wallet_h_payto BYTEA,
     25   IN in_expiration_date INT8,
     26   OUT out_no_partner BOOLEAN,
     27   OUT out_no_balance BOOLEAN,
     28   OUT out_conflict BOOLEAN)
     29 LANGUAGE plpgsql
     30 AS $$
     31 DECLARE
     32   my_amount taler_amount;
     33 DECLARE
     34   my_purse_fee taler_amount;
     35 DECLARE
     36   my_partner_serial_id INT8;
     37 DECLARE
     38   my_in_reserve_quota BOOLEAN;
     39 DECLARE
     40   rval RECORD;
     41 DECLARE
     42   reserve_bal RECORD;
     43 DECLARE
     44   balance taler_amount;
     45 BEGIN
     46 
     47 -- Initialize reserve, if not yet exists.
     48 INSERT INTO reserves
     49   (reserve_pub
     50   ,expiration_date
     51   ,gc_date)
     52   VALUES
     53   (in_reserve_pub
     54   ,in_expiration_date
     55   ,in_expiration_date)
     56   ON CONFLICT DO NOTHING;
     57 
     58 
     59 IF in_partner_url IS NULL
     60 THEN
     61   my_partner_serial_id=NULL;
     62 ELSE
     63   SELECT
     64     partner_serial_id
     65   INTO
     66     my_partner_serial_id
     67   FROM partners
     68   WHERE partner_base_url=in_partner_url
     69     AND start_date <= in_merge_timestamp
     70     AND end_date > in_merge_timestamp;
     71   IF NOT FOUND
     72   THEN
     73     out_no_partner=TRUE;
     74     out_conflict=FALSE;
     75     RETURN;
     76   END IF;
     77 END IF;
     78 
     79 out_no_partner=FALSE;
     80 
     81 -- Check purse is 'full'.
     82 SELECT amount_with_fee
     83       ,purse_fee
     84       ,in_reserve_quota
     85   INTO rval
     86   FROM purse_requests pr
     87   WHERE purse_pub=in_purse_pub
     88     AND (pr.balance).val >= (pr.amount_with_fee).val
     89     AND ( (pr.balance).frac >= (pr.amount_with_fee).frac OR
     90           (pr.balance).val > (pr.amount_with_fee).val );
     91 IF NOT FOUND
     92 THEN
     93   out_no_balance=TRUE;
     94   out_conflict=FALSE;
     95   RETURN;
     96 END IF;
     97 
     98 -- We use rval as workaround as we cannot select
     99 -- directly into the amount due to Postgres limitations.
    100 my_amount := rval.amount_with_fee;
    101 my_purse_fee := rval.purse_fee;
    102 my_in_reserve_quota := rval.in_reserve_quota;
    103 
    104 out_no_balance=FALSE;
    105 
    106 -- Store purse merge signature, checks for purse_pub uniqueness
    107 INSERT INTO purse_merges
    108     (partner_serial_id
    109     ,reserve_pub
    110     ,purse_pub
    111     ,merge_sig
    112     ,merge_timestamp)
    113   VALUES
    114     (my_partner_serial_id
    115     ,in_reserve_pub
    116     ,in_purse_pub
    117     ,in_merge_sig
    118     ,in_merge_timestamp)
    119   ON CONFLICT DO NOTHING;
    120 
    121 IF NOT FOUND
    122 THEN
    123   -- Idempotency check: see if an identical record exists.
    124   -- Note that by checking 'merge_sig', we implicitly check
    125   -- identity over everything that the signature covers.
    126   PERFORM
    127   FROM purse_merges
    128   WHERE purse_pub=in_purse_pub
    129      AND merge_sig=in_merge_sig;
    130   IF NOT FOUND
    131   THEN
    132      -- Purse was merged, but to some other reserve. Not allowed.
    133      out_conflict=TRUE;
    134      RETURN;
    135   END IF;
    136 
    137   -- "success"
    138   out_conflict=FALSE;
    139   RETURN;
    140 END IF;
    141 
    142 
    143 -- Remember how this purse was finished. This will conflict
    144 -- if the purse was already decided previously.
    145 INSERT INTO purse_decision
    146   (purse_pub
    147   ,action_timestamp
    148   ,refunded)
    149 VALUES
    150   (in_purse_pub
    151   ,in_merge_timestamp
    152   ,FALSE)
    153 ON CONFLICT DO NOTHING;
    154 
    155 IF NOT FOUND
    156 THEN
    157   -- Purse was already decided (possibly deleted or merged differently).
    158   out_conflict=TRUE;
    159   RETURN;
    160 END IF;
    161 
    162 out_conflict=FALSE;
    163 
    164 
    165 
    166 IF (my_in_reserve_quota)
    167 THEN
    168   UPDATE reserves
    169     SET purses_active=purses_active-1
    170   WHERE reserve_pub IN
    171     (SELECT reserve_pub
    172        FROM purse_merges
    173       WHERE purse_pub=my_purse_pub
    174      LIMIT 1);
    175 END IF;
    176 
    177 -- Store account merge signature.
    178 INSERT INTO account_merges
    179   (reserve_pub
    180   ,reserve_sig
    181   ,purse_pub
    182   ,wallet_h_payto)
    183   VALUES
    184   (in_reserve_pub
    185   ,in_reserve_sig
    186   ,in_purse_pub
    187   ,in_wallet_h_payto);
    188 
    189 -- If we need a wad transfer, mark purse ready for it.
    190 IF (0 != my_partner_serial_id)
    191 THEN
    192   -- The taler-exchange-router will take care of this.
    193   UPDATE purse_actions
    194      SET action_date=0 --- "immediately"
    195         ,partner_serial_id=my_partner_serial_id
    196    WHERE purse_pub=in_purse_pub;
    197 ELSE
    198   -- This is a local reserve, update reserve balance immediately.
    199 
    200   -- Refund the purse fee, by adding it to the purse value:
    201   my_amount.val = my_amount.val + my_purse_fee.val;
    202   my_amount.frac = my_amount.frac + my_purse_fee.frac;
    203   -- normalize result
    204   my_amount.val = my_amount.val + my_amount.frac / 100000000;
    205   my_amount.frac = my_amount.frac % 100000000;
    206 
    207   SELECT current_balance
    208     INTO reserve_bal
    209     FROM reserves
    210    WHERE reserve_pub=in_reserve_pub;
    211 
    212   balance = reserve_bal.current_balance;
    213   balance.val=balance.val+my_amount.val
    214      + CASE
    215        WHEN balance.frac + my_amount.frac >= 100000000
    216        THEN 1
    217        ELSE 0
    218        END;
    219   balance.frac=balance.frac+my_amount.frac
    220      - CASE
    221        WHEN balance.frac + my_amount.frac >= 100000000
    222        THEN 100000000
    223        ELSE 0
    224        END;
    225 
    226   UPDATE reserves
    227      SET current_balance=balance
    228    WHERE reserve_pub=in_reserve_pub;
    229 
    230 END IF;
    231 
    232 RETURN;
    233 
    234 END $$;
    235 
    236 COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, TEXT, BYTEA, BYTEA, INT8)
    237   IS 'Checks that the partner exists, the purse has not been merged with a different reserve and that the purse is full. If so, persists the merge data and either merges the purse with the reserve or marks it as ready for the taler-exchange-router. Caller MUST abort the transaction on failures so as to not persist data by accident.';