exchange

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

do_purse_merge.sql (6120B)


      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     -- Must be assigned: the C result spec reads it without allow_null, so
     76     -- leaving it NULL turns the 404 the handler has ready for an unknown
     77     -- partner into a failed extraction and an HTTP 500.
     78     out_no_balance=FALSE;
     79     RETURN;
     80   END IF;
     81 END IF;
     82 
     83 out_no_partner=FALSE;
     84 
     85 -- Check purse is 'full'.
     86 SELECT amount_with_fee
     87       ,purse_fee
     88       ,in_reserve_quota
     89   INTO rval
     90   FROM purse_requests pr
     91   WHERE purse_pub=in_purse_pub
     92     AND (pr.balance).val >= (pr.amount_with_fee).val
     93     AND ( (pr.balance).frac >= (pr.amount_with_fee).frac OR
     94           (pr.balance).val > (pr.amount_with_fee).val );
     95 IF NOT FOUND
     96 THEN
     97   out_no_balance=TRUE;
     98   out_conflict=FALSE;
     99   RETURN;
    100 END IF;
    101 
    102 -- We use rval as workaround as we cannot select
    103 -- directly into the amount due to Postgres limitations.
    104 my_amount := rval.amount_with_fee;
    105 my_purse_fee := rval.purse_fee;
    106 my_in_reserve_quota := rval.in_reserve_quota;
    107 
    108 out_no_balance=FALSE;
    109 
    110 -- Store purse merge signature, checks for purse_pub uniqueness
    111 INSERT INTO purse_merges
    112     (partner_serial_id
    113     ,reserve_pub
    114     ,purse_pub
    115     ,merge_sig
    116     ,merge_timestamp)
    117   VALUES
    118     (my_partner_serial_id
    119     ,in_reserve_pub
    120     ,in_purse_pub
    121     ,in_merge_sig
    122     ,in_merge_timestamp)
    123   ON CONFLICT DO NOTHING;
    124 
    125 IF NOT FOUND
    126 THEN
    127   -- Idempotency check: see if an identical record exists.
    128   -- Note that by checking 'merge_sig', we implicitly check
    129   -- identity over everything that the signature covers.
    130   PERFORM
    131   FROM purse_merges
    132   WHERE purse_pub=in_purse_pub
    133      AND merge_sig=in_merge_sig;
    134   IF NOT FOUND
    135   THEN
    136      -- Purse was merged, but to some other reserve. Not allowed.
    137      out_conflict=TRUE;
    138      RETURN;
    139   END IF;
    140 
    141   -- "success"
    142   out_conflict=FALSE;
    143   RETURN;
    144 END IF;
    145 
    146 
    147 -- Remember how this purse was finished. This will conflict
    148 -- if the purse was already decided previously.
    149 INSERT INTO purse_decision
    150   (purse_pub
    151   ,action_timestamp
    152   ,refunded)
    153 VALUES
    154   (in_purse_pub
    155   ,in_merge_timestamp
    156   ,FALSE)
    157 ON CONFLICT DO NOTHING;
    158 
    159 IF NOT FOUND
    160 THEN
    161   -- Purse was already decided (possibly deleted or merged differently).
    162   out_conflict=TRUE;
    163   RETURN;
    164 END IF;
    165 
    166 out_conflict=FALSE;
    167 
    168 
    169 
    170 IF (my_in_reserve_quota)
    171 THEN
    172   UPDATE reserves
    173     SET purses_active=purses_active-1
    174   WHERE reserve_pub IN
    175     (SELECT reserve_pub
    176        FROM purse_merges
    177       WHERE purse_pub=in_purse_pub
    178      LIMIT 1);
    179 END IF;
    180 
    181 -- Store account merge signature.
    182 INSERT INTO account_merges
    183   (reserve_pub
    184   ,reserve_sig
    185   ,purse_pub
    186   ,wallet_h_payto)
    187   VALUES
    188   (in_reserve_pub
    189   ,in_reserve_sig
    190   ,in_purse_pub
    191   ,in_wallet_h_payto);
    192 
    193 -- If we need a wad transfer, mark purse ready for it.
    194 IF (0 != my_partner_serial_id)
    195 THEN
    196   -- The taler-exchange-router will take care of this.
    197   UPDATE purse_actions
    198      SET action_date=0 --- "immediately"
    199         ,partner_serial_id=my_partner_serial_id
    200    WHERE purse_pub=in_purse_pub;
    201 ELSE
    202   -- This is a local reserve, update reserve balance immediately.
    203 
    204   -- Refund the purse fee, by adding it to the purse value:
    205   my_amount.val = my_amount.val + my_purse_fee.val;
    206   my_amount.frac = my_amount.frac + my_purse_fee.frac;
    207   -- normalize result
    208   my_amount.val = my_amount.val + my_amount.frac / 100000000;
    209   my_amount.frac = my_amount.frac % 100000000;
    210 
    211   SELECT current_balance
    212     INTO reserve_bal
    213     FROM reserves
    214    WHERE reserve_pub=in_reserve_pub;
    215 
    216   balance = reserve_bal.current_balance;
    217   balance.val=balance.val+my_amount.val
    218      + CASE
    219        WHEN balance.frac + my_amount.frac >= 100000000
    220        THEN 1
    221        ELSE 0
    222        END;
    223   balance.frac=balance.frac+my_amount.frac
    224      - CASE
    225        WHEN balance.frac + my_amount.frac >= 100000000
    226        THEN 100000000
    227        ELSE 0
    228        END;
    229 
    230   UPDATE reserves
    231      SET current_balance=balance
    232    WHERE reserve_pub=in_reserve_pub;
    233 
    234 END IF;
    235 
    236 RETURN;
    237 
    238 END $$;
    239 
    240 COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, TEXT, BYTEA, BYTEA, INT8)
    241   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.';