exchange

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

0002-withdraw.sql (7319B)


      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 CREATE FUNCTION create_table_withdraw(
     19   IN partition_suffix TEXT DEFAULT NULL
     20 )
     21 RETURNS VOID
     22 LANGUAGE plpgsql
     23 AS $$
     24 DECLARE
     25   table_name TEXT DEFAULT 'withdraw';
     26 BEGIN
     27   PERFORM create_partitioned_table(
     28     'CREATE TABLE %I'
     29       '(withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
     30       ',planchets_h BYTEA CONSTRAINT planchets_h_length CHECK(LENGTH(planchets_h)=64)'
     31       ',execution_date INT8 NOT NULL'
     32       ',amount_with_fee taler_amount NOT NULL'
     33       ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)'
     34       ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)'
     35       ',max_age SMALLINT CONSTRAINT max_age_positive CHECK(max_age>=0)'
     36       ',noreveal_index SMALLINT CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
     37       ',selected_h BYTEA CONSTRAINT selected_h_length CHECK(LENGTH(selected_h)=64)'
     38       ',blinding_seed BYTEA CONSTRAINT blinding_seed_length CHECK(LENGTH(blinding_seed)>=32)'
     39       ',cs_r_values BYTEA[]'
     40       ',cs_r_choices INT8'
     41       ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
     42       ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
     43     ') %s ;'
     44     ,table_name
     45     ,'PARTITION BY HASH (reserve_pub)'
     46     ,partition_suffix
     47   );
     48   PERFORM comment_partitioned_table(
     49      'Commitments made when withdrawing coins and, in case of required proof of age restriction, the gamma value chosen by the exchange. '
     50      'It also contains the blindly signed coins, their signatures and denominations.'
     51     ,table_name
     52     ,partition_suffix
     53   );
     54   PERFORM comment_partitioned_column(
     55      'If the client explicitly commits to age-restricted coins, the gamma value chosen by the exchange in the cut-and-choose protocol; NULL if we did not use age-withdraw.'
     56     ,'noreveal_index'
     57     ,table_name
     58     ,partition_suffix
     59   );
     60   PERFORM comment_partitioned_column(
     61      'The running hash over all committed blinded planchets. Needed for recoup and (when a proof of age-restriction was required); NULL if we did not use age-withdraw.'
     62      ' in the subsequent cut-and-choose protocol.'
     63     ,'planchets_h'
     64     ,table_name
     65     ,partition_suffix
     66   );
     67   PERFORM comment_partitioned_column(
     68      'The date of execution of this withdrawal, according to the exchange'
     69     ,'execution_date'
     70     ,table_name
     71     ,partition_suffix
     72   );
     73   PERFORM comment_partitioned_column(
     74      'If the clients commits to age-restricted coins, the maximum age (in years) that the client explicitly commits to with this request; NULL if we did not use age-withdraw.'
     75     ,'max_age'
     76     ,table_name
     77     ,partition_suffix
     78   );
     79   PERFORM comment_partitioned_column(
     80      'Reference to the public key of the reserve from which the coins are going to be withdrawn'
     81     ,'reserve_pub'
     82     ,table_name
     83     ,partition_suffix
     84   );
     85   PERFORM comment_partitioned_column(
     86      'Signature of the reserve''s private key over the withdraw request'
     87     ,'reserve_sig'
     88     ,table_name
     89     ,partition_suffix
     90   );
     91   PERFORM comment_partitioned_column(
     92      'Array of references to the denominations'
     93     ,'denom_serials'
     94     ,table_name
     95     ,partition_suffix
     96   );
     97   PERFORM comment_partitioned_column(
     98      'In case of age restriction, the hash of the chosen (noreveal_index) blinded envelopes; NULL if we did not use age-withdraw.'
     99     ,'selected_h'
    100     ,table_name
    101     ,partition_suffix
    102   );
    103   PERFORM comment_partitioned_column(
    104      'Array of signatures over each blinded envelope.  If age-proof was not required, the signed envelopes are the ones'
    105      ' hashed into planchet_h.  Otherwise (when age-proof is required) the selected planchets (noreveal_index) were signed,'
    106      ' hashed into selected_h.'
    107     ,'denom_sigs'
    108     ,table_name
    109     ,partition_suffix
    110   );
    111   PERFORM comment_partitioned_column(
    112      'The master seed for the blinding nonces, needed for blind CS signatures; NULL if we did not use age-withdraw or CS'
    113     ,'blinding_seed'
    114     ,table_name
    115     ,partition_suffix
    116   );
    117  PERFORM comment_partitioned_column(
    118      'The pairs of R-values (calculated by the exchange) for the coins of cipher type Clause-Schnorr, based on the blinding_seed; maybe NULL if we did not use CS.'
    119     ,'cs_r_values'
    120     ,table_name
    121     ,partition_suffix
    122   );
    123   PERFORM comment_partitioned_column(
    124      'The bitvector of choices made by the exchange for each of the pairs in cs_r_values; NULL if we did not use CS.'
    125      'The vector is stored in network byte order and the lowest bit corresponds to the 0-th entry in cs_r_values (pair)'
    126     ,'cs_r_choices'
    127     ,table_name
    128     ,partition_suffix
    129   );
    130 END
    131 $$;
    132 
    133 
    134 CREATE FUNCTION constrain_table_withdraw(
    135   IN partition_suffix TEXT
    136 )
    137 RETURNS void
    138 LANGUAGE plpgsql
    139 AS $$
    140 DECLARE
    141   table_name TEXT DEFAULT 'withdraw';
    142 BEGIN
    143   table_name = concat_ws('_', table_name, partition_suffix);
    144   EXECUTE FORMAT (
    145     'CREATE INDEX ' || table_name || '_by_reserve_pub_index '
    146     'ON ' || table_name || ' '
    147     '(reserve_pub);'
    148   );
    149   EXECUTE FORMAT (
    150     'ALTER TABLE ' || table_name ||
    151     ' ADD PRIMARY KEY (reserve_pub, planchets_h);'
    152   );
    153   EXECUTE FORMAT (
    154     'ALTER TABLE ' || table_name ||
    155     ' ADD CONSTRAINT ' || table_name || '_withdraw_id_key'
    156     ' UNIQUE (withdraw_id);'
    157   );
    158 END
    159 $$;
    160 
    161 
    162 CREATE FUNCTION foreign_table_withdraw()
    163 RETURNS void
    164 LANGUAGE plpgsql
    165 AS $$
    166 DECLARE
    167   table_name TEXT DEFAULT 'withdraw';
    168 BEGIN
    169   EXECUTE FORMAT (
    170     'ALTER TABLE ' || table_name ||
    171     ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
    172     ' FOREIGN KEY (reserve_pub)'
    173     ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
    174   );
    175 END
    176 $$;
    177 
    178 -- Put the triggers into the master table
    179 CREATE FUNCTION master_table_withdraw()
    180   RETURNS void
    181   LANGUAGE plpgsql
    182   AS $$
    183 BEGIN
    184   CREATE TRIGGER withdraw_on_insert
    185   AFTER INSERT
    186   ON withdraw
    187   FOR EACH ROW EXECUTE FUNCTION withdraw_insert_trigger();
    188 
    189   CREATE TRIGGER withdraw_on_delete
    190   AFTER DELETE
    191   ON withdraw
    192   FOR EACH ROW EXECUTE FUNCTION withdraw_delete_trigger();
    193 END $$;
    194 COMMENT ON FUNCTION master_table_withdraw()
    195   IS 'Setup triggers to replicate withdraw into reserve_history and delete blinding_seed from unique_withdraw_blinding_seed.';
    196 
    197 
    198 INSERT INTO exchange_tables
    199   (name
    200   ,version
    201   ,action
    202   ,partitioned
    203   ,by_range)
    204 VALUES
    205   ('withdraw', 'exchange-0002', 'create',   TRUE ,FALSE),
    206   ('withdraw', 'exchange-0002', 'constrain',TRUE ,FALSE),
    207   ('withdraw', 'exchange-0002', 'foreign',  TRUE ,FALSE),
    208   ('withdraw', 'exchange-0002', 'master',   TRUE ,FALSE);