exchange

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

exchange_do_refresh.sql (4818B)


      1 --
      2 -- This file is part of TALER
      3 -- Copyright (C) 2025 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 -- @author Özgür Kesim
     17 
     18 DROP FUNCTION IF EXISTS exchange_do_refresh;
     19 
     20 CREATE FUNCTION exchange_do_refresh(
     21   IN in_rc BYTEA,
     22   IN in_now INT8,
     23   IN in_refresh_seed BYTEA,
     24   IN in_planchets_h BYTEA,
     25   IN in_amount_with_fee taler_amount,
     26   IN in_blinding_seed BYTEA,
     27   IN in_cs_r_values BYTEA[],
     28   IN in_cs_r_choices INT8,
     29   IN in_selected_h BYTEA,
     30   IN in_denom_sigs BYTEA[],
     31   IN in_denom_serials INT8[],
     32   IN in_old_coin_pub BYTEA,
     33   IN in_old_coin_sig BYTEA,
     34   IN in_noreveal_index INT4,
     35   IN in_zombie_required BOOLEAN,
     36   OUT out_coin_found BOOLEAN,
     37   OUT out_balance_ok BOOLEAN,
     38   OUT out_zombie_bad BOOLEAN,
     39   OUT out_nonce_reuse BOOLEAN,
     40   OUT out_idempotent BOOLEAN,
     41   OUT out_noreveal_index INT4,
     42   OUT out_coin_balance taler_amount)
     43 LANGUAGE plpgsql
     44 AS $$
     45 DECLARE
     46   known_coin RECORD;
     47   difference RECORD;
     48 BEGIN
     49 -- Shards: INSERT refresh (by rc)
     50 -- (rare:) SELECT refresh (by old_coin_pub) -- crosses shards!
     51 -- (rare:) SELECT refresh_revealed_coins (by refresh_id)
     52 -- (rare:) PERFORM recoup_refresh (by rrc_serial) -- crosses shards!
     53 --         UPDATE known_coins (by coin_pub)
     54 
     55 -- First, find old coin
     56 SELECT known_coin_id
     57      ,remaining
     58   INTO known_coin
     59   FROM known_coins
     60   WHERE coin_pub = in_old_coin_pub;
     61 
     62 IF NOT FOUND
     63 THEN
     64   out_coin_found = FALSE;
     65   out_balance_ok = TRUE;
     66   out_zombie_bad = FALSE;
     67   out_nonce_reuse = FALSE;
     68   out_idempotent = FALSE;
     69   out_noreveal_index = -1 ;
     70   out_coin_balance.val = 0;
     71   out_coin_balance.frac = 0;
     72   RETURN;
     73 END IF;
     74 
     75 out_coin_found = TRUE;
     76 out_coin_balance = known_coin.remaining;
     77 
     78 -- Next, check for idempotency
     79 SELECT TRUE, noreveal_index
     80 INTO out_idempotent, out_noreveal_index
     81 FROM exchange.refresh
     82 WHERE rc=in_rc;
     83 
     84 IF out_idempotent
     85 THEN
     86   -- out_idempotent is set
     87   -- out_noreveal_index is set
     88   -- out_coin_found is set
     89   -- out_coin_balance is set
     90   out_balance_ok = TRUE;
     91   out_zombie_bad = FALSE; -- zombie is OK
     92   out_nonce_reuse = FALSE;
     93 RETURN;
     94 END IF;
     95 
     96 out_idempotent = FALSE;
     97 out_noreveal_index = in_noreveal_index;
     98 
     99 -- Ensure the uniqueness of the blinding_seed
    100 IF in_blinding_seed IS NOT NULL
    101 THEN
    102   INSERT INTO unique_refresh_blinding_seed
    103     (blinding_seed)
    104   VALUES
    105     (in_blinding_seed)
    106   ON CONFLICT DO NOTHING;
    107 
    108   IF NOT FOUND
    109   THEN
    110       out_nonce_reuse = TRUE;
    111       out_balance_ok = TRUE;
    112       out_zombie_bad = FALSE; -- zombie is OK
    113       RETURN;
    114   END IF;
    115 END IF;
    116 
    117 out_nonce_reuse = FALSE;
    118 
    119 INSERT INTO exchange.refresh
    120   (rc
    121   ,execution_date
    122   ,old_coin_pub
    123   ,old_coin_sig
    124   ,planchets_h
    125   ,amount_with_fee
    126   ,noreveal_index
    127   ,refresh_seed
    128   ,blinding_seed
    129   ,cs_r_values
    130   ,cs_r_choices
    131   ,selected_h
    132   ,denom_sigs
    133   ,denom_serials
    134   )
    135   VALUES
    136   (in_rc
    137   ,in_now
    138   ,in_old_coin_pub
    139   ,in_old_coin_sig
    140   ,in_planchets_h
    141   ,in_amount_with_fee
    142   ,in_noreveal_index
    143   ,in_refresh_seed
    144   ,in_blinding_seed
    145   ,in_cs_r_values
    146   ,in_cs_r_choices
    147   ,in_selected_h
    148   ,in_denom_sigs
    149   ,in_denom_serials
    150   )
    151   ON CONFLICT DO NOTHING;
    152 
    153 IF NOT FOUND
    154 THEN
    155   RAISE EXCEPTION 'Conflict in refresh despite idempotency check for rc(%)!', rc;
    156   RETURN;
    157 END IF;
    158 
    159 IF in_zombie_required
    160 THEN
    161   -- Check if this coin was part of a refresh
    162   -- operation that was subsequently involved
    163   -- in a recoup operation.  We begin by all
    164   -- refresh operations our coin was involved
    165   -- with, then find all associated reveal
    166   -- operations, and then see if any of these
    167   -- reveal operations was involved in a recoup.
    168   PERFORM
    169    FROM recoup_refresh
    170    WHERE refresh_id IN
    171       (SELECT refresh_id
    172        FROM refresh
    173        WHERE old_coin_pub=in_old_coin_pub);
    174   IF NOT FOUND
    175   THEN
    176     out_zombie_bad=TRUE;
    177     out_balance_ok=FALSE;
    178     RETURN;
    179   END IF;
    180 END IF;
    181 
    182 out_zombie_bad=FALSE; -- zombie is OK
    183 
    184 -- Check coin balance is sufficient.
    185 SELECT *
    186 INTO difference
    187 FROM amount_left_minus_right(out_coin_balance
    188                             ,in_amount_with_fee);
    189 
    190 out_balance_ok = difference.ok;
    191 
    192 IF NOT out_balance_ok
    193 THEN
    194   RETURN;
    195 END IF;
    196 
    197 out_coin_balance = difference.diff;
    198 
    199 -- Check and update balance of the coin.
    200 UPDATE known_coins
    201   SET
    202     remaining = out_coin_balance
    203   WHERE
    204     known_coin_id = known_coin.known_coin_id;
    205 
    206 END $$;