exchange

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

exchange_do_deposit.sql (7911B)


      1 --
      2 -- This file is part of TALER
      3 -- Copyright (C) 2014--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 DROP FUNCTION IF EXISTS exchange_do_deposit;
     18 CREATE FUNCTION exchange_do_deposit(
     19   -- For batch_deposits
     20   IN in_shard INT8,
     21   IN in_merchant_pub BYTEA,
     22   IN in_merchant_sig BYTEA,
     23   IN in_wallet_timestamp INT8,
     24   IN in_exchange_timestamp INT8,
     25   IN in_refund_deadline INT8,
     26   IN in_wire_deadline INT8,
     27   IN in_h_contract_terms BYTEA,
     28   IN in_wallet_data_hash BYTEA, -- can be NULL
     29   IN in_wire_salt BYTEA,
     30   IN in_wire_target_h_payto BYTEA,
     31   IN in_h_normalized_payto BYTEA,
     32   IN in_policy_details_serial_id INT8, -- can be NULL
     33   IN in_policy_blocked BOOLEAN,
     34   -- For wire_targets
     35   IN in_receiver_wire_account TEXT,
     36   -- For coin_deposits
     37   IN ina_coin_pub BYTEA[],
     38   IN ina_coin_sig BYTEA[],
     39   IN ina_amount_with_fee taler_amount[],
     40   IN ina_deposit_fee taler_amount[],
     41   IN in_total_amount taler_amount,
     42   IN in_total_without_fee taler_amount,
     43   IN in_is_wallet BOOL,
     44   IN in_extra_wire_subject_metadata TEXT,
     45   OUT out_exchange_timestamp INT8,
     46   OUT out_accumulated_total_without_fee taler_amount,
     47   OUT out_insufficient_balance_coin_index INT4, -- index of coin with bad balance, NULL if none
     48   OUT out_conflict BOOL
     49  )
     50 LANGUAGE plpgsql
     51 AS $$
     52 DECLARE
     53   wtsi INT8; -- wire target serial id
     54   bdsi INT8; -- batch_deposits serial id
     55   i INT4;
     56   my_record RECORD;
     57   my_diff RECORD;
     58   ini_amount_with_fee taler_amount;
     59   ini_deposit_fee taler_amount;
     60   ini_coin_pub BYTEA;
     61   ini_coin_sig BYTEA;
     62   my_update BOOL := FALSE;
     63   my_total taler_amount;
     64   my_total_without_fee taler_amount;
     65 BEGIN
     66 -- Shards:
     67 --         INSERT wire_targets (by h_payto), ON CONFLICT DO NOTHING;
     68 --         INSERT batch_deposits (by shard, merchant_pub), ON CONFLICT idempotency check;
     69 --         INSERT[] coin_deposits (by coin_pub), ON CONFLICT idempotency check;
     70 --         UPDATE[] known_coins (by coin_pub)
     71 
     72 
     73 -- Make sure the kyc_target entry exists
     74 INSERT INTO kyc_targets
     75   (h_normalized_payto
     76   ,is_wallet
     77   ) VALUES (
     78    in_h_normalized_payto
     79   ,in_is_wallet
     80   )
     81   ON CONFLICT DO NOTHING;
     82 
     83 -- First, get or create the 'wtsi'
     84 INSERT INTO wire_targets
     85   (wire_target_h_payto
     86   ,h_normalized_payto
     87   ,payto_uri
     88   ) VALUES (
     89    in_wire_target_h_payto
     90   ,in_h_normalized_payto
     91   ,in_receiver_wire_account
     92   )
     93   ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto)
     94   RETURNING wire_target_serial_id
     95        INTO wtsi;
     96 
     97 IF NOT FOUND
     98 THEN
     99   SELECT wire_target_serial_id
    100     INTO wtsi
    101     FROM wire_targets
    102    WHERE wire_target_h_payto=in_wire_target_h_payto;
    103 END IF;
    104 
    105 
    106 -- Second, create the batch_deposits entry
    107 INSERT INTO batch_deposits
    108   (shard
    109   ,merchant_pub
    110   ,merchant_sig
    111   ,wallet_timestamp
    112   ,exchange_timestamp
    113   ,refund_deadline
    114   ,wire_deadline
    115   ,h_contract_terms
    116   ,wallet_data_hash
    117   ,wire_salt
    118   ,wire_target_h_payto
    119   ,policy_details_serial_id
    120   ,policy_blocked
    121   ,total_amount
    122   ,total_without_fee
    123   ,extra_wire_subject_metadata
    124   ) VALUES (
    125    in_shard
    126   ,in_merchant_pub
    127   ,in_merchant_sig
    128   ,in_wallet_timestamp
    129   ,in_exchange_timestamp
    130   ,in_refund_deadline
    131   ,in_wire_deadline
    132   ,in_h_contract_terms
    133   ,in_wallet_data_hash
    134   ,in_wire_salt
    135   ,in_wire_target_h_payto
    136   ,in_policy_details_serial_id
    137   ,in_policy_blocked
    138   ,in_total_amount
    139   ,in_total_without_fee
    140   ,in_extra_wire_subject_metadata)
    141   ON CONFLICT DO NOTHING -- for CONFLICT ON (merchant_pub, h_contract_terms)
    142   RETURNING
    143     batch_deposit_serial_id
    144   INTO
    145     bdsi;
    146 
    147 IF FOUND
    148 THEN
    149   -- First batch deposit, we can skip all the 'my_update' logic!
    150   out_accumulated_total_without_fee = in_total_without_fee;
    151 ELSE
    152   -- Idempotency check: see if an identical record exists.
    153   -- We do select over merchant_pub, h_contract_terms and wire_target_h_payto
    154   -- first to maximally increase the chance of using the existing index.
    155   SELECT
    156       exchange_timestamp
    157      ,batch_deposit_serial_id
    158      ,total_amount
    159      ,total_without_fee
    160    INTO
    161       my_record
    162    FROM batch_deposits
    163    WHERE shard=in_shard
    164      AND merchant_pub=in_merchant_pub
    165      AND h_contract_terms=in_h_contract_terms
    166      AND wire_target_h_payto=in_wire_target_h_payto
    167      -- now check the rest, too
    168      AND ( (wallet_data_hash=in_wallet_data_hash) OR
    169            (wallet_data_hash IS NULL AND in_wallet_data_hash IS NULL) )
    170      AND wire_salt=in_wire_salt
    171      AND wallet_timestamp=in_wallet_timestamp
    172      AND refund_deadline=in_refund_deadline
    173      AND wire_deadline=in_wire_deadline
    174      AND ( (policy_details_serial_id=in_policy_details_serial_id) OR
    175            (policy_details_serial_id IS NULL AND in_policy_details_serial_id IS NULL) );
    176   IF NOT FOUND
    177   THEN
    178     -- Deposit exists, but with *strange* differences. Not allowed.
    179     out_conflict=TRUE;
    180     RETURN;
    181   END IF;
    182   out_exchange_timestamp = my_record.exchange_timestamp;
    183   bdsi = my_record.batch_deposit_serial_id;
    184   my_total = my_record.total_amount;
    185   my_total_without_fee = my_record.total_without_fee;
    186   my_update = TRUE;
    187 END IF;
    188 
    189 out_conflict=FALSE;
    190 
    191 -- Deposit each coin
    192 
    193 FOR i IN 1..array_length(ina_coin_pub,1)
    194 LOOP
    195   ini_coin_pub = ina_coin_pub[i];
    196   ini_coin_sig = ina_coin_sig[i];
    197   ini_amount_with_fee = ina_amount_with_fee[i];
    198   ini_deposit_fee = ina_deposit_fee[i];
    199 
    200   INSERT INTO coin_deposits
    201     (batch_deposit_serial_id
    202     ,coin_pub
    203     ,coin_sig
    204     ,amount_with_fee
    205     ) VALUES (
    206      bdsi
    207     ,ini_coin_pub
    208     ,ini_coin_sig
    209     ,ini_amount_with_fee
    210     )
    211     ON CONFLICT DO NOTHING;
    212 
    213   IF FOUND
    214   THEN
    215     -- Insert did happen, update balance in known_coins!
    216     UPDATE known_coins kc
    217       SET
    218         remaining.frac=(kc.remaining).frac-ini_amount_with_fee.frac
    219           + CASE
    220               WHEN (kc.remaining).frac < ini_amount_with_fee.frac
    221               THEN 100000000
    222               ELSE 0
    223             END,
    224         remaining.val=(kc.remaining).val-ini_amount_with_fee.val
    225           - CASE
    226               WHEN (kc.remaining).frac < ini_amount_with_fee.frac
    227               THEN 1
    228               ELSE 0
    229             END
    230       WHERE coin_pub=ini_coin_pub
    231         AND ( ((kc.remaining).val > ini_amount_with_fee.val) OR
    232               ( ((kc.remaining).frac >= ini_amount_with_fee.frac) AND
    233                 ((kc.remaining).val >= ini_amount_with_fee.val) ) );
    234 
    235     IF NOT FOUND
    236     THEN
    237       -- Insufficient balance.
    238       -- Note: C arrays are 0 indexed, but i started at 1
    239       out_insufficient_balance_coin_index=i-1;
    240       RETURN;
    241     END IF;
    242 
    243     IF my_update
    244     THEN
    245       -- Prepare to update totals in batch_deposits
    246       SELECT *
    247         INTO my_total
    248         FROM amount_add (my_total,
    249                          ini_amount_with_fee);
    250       SELECT *
    251         INTO my_total_without_fee
    252         FROM amount_add (my_total_without_fee,
    253                          ini_amount_with_fee);
    254       SELECT *
    255         INTO my_diff
    256         FROM amount_left_minus_right (my_total_without_fee,
    257                                       ini_deposit_fee);
    258         my_total_without_fee = my_diff.diff;
    259     END IF;
    260   END IF;
    261 END LOOP; -- end FOR all coins
    262 
    263 IF my_update
    264 THEN
    265   UPDATE batch_deposits
    266      SET total_amount = my_total
    267         ,total_without_fee = my_total_without_fee
    268    WHERE batch_deposit_serial_id = bdsi
    269      AND merchant_pub = in_merchant_pub
    270      AND h_contract_terms = in_h_contract_terms;
    271   out_accumulated_total_without_fee = my_total_without_fee;
    272 END IF;
    273 
    274 END $$;