commit f94c1ce254e3b8f2af82b4864c3580ec4c7aebb4
parent e26d0c1c289ecce245b5652dd1d8931ba8dee70d
Author: Özgür Kesim <oec@codeblau.de>
Date: Wed, 7 May 2025 14:01:44 +0200
[exchangedb] WIP: refactor; recoup, refresh, withdraw from 0009 to 0002
Fold all changes from 0009 regarding
- withdraw
- refresh
- recoup, recoup_refresh
to 0002.
Also: remove traces of batch_withdraw from plugin and history handling.
Diffstat:
25 files changed, 687 insertions(+), 2089 deletions(-)
diff --git a/src/exchange/taler-exchange-httpd_reserves_history.c b/src/exchange/taler-exchange-httpd_reserves_history.c
@@ -221,34 +221,6 @@ compile_reserve_history (
}
break;
- case TALER_EXCHANGEDB_RO_BATCH_WITHDRAW_COIN:
- {
- const struct TALER_EXCHANGEDB_CollectableBlindcoin *batch_withdraw
- = pos->details.batch_withdraw;
-
- if (0 !=
- json_array_append_new (
- json_history,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("type",
- "BATCH_WITHDRAW"),
- GNUNET_JSON_pack_data_auto ("reserve_sig",
- &batch_withdraw->reserve_sig),
- GNUNET_JSON_pack_data_auto ("h_coin_envelope",
- &batch_withdraw->h_coin_envelope),
- GNUNET_JSON_pack_data_auto ("h_denom_pub",
- &batch_withdraw->denom_pub_hash),
- TALER_JSON_pack_amount ("withdraw_fee",
- &batch_withdraw->withdraw_fee),
- TALER_JSON_pack_amount ("amount",
- &batch_withdraw->amount_with_fee))))
- {
- GNUNET_break (0);
- json_decref (json_history);
- return NULL;
- }
- }
- break;
case TALER_EXCHANGEDB_RO_RECOUP_COIN:
{
const struct TALER_EXCHANGEDB_Recoup *recoup
diff --git a/src/exchangedb/0002-age_withdraw.sql b/src/exchangedb/0002-age_withdraw.sql
@@ -1,157 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2023 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
--- @author Özgür Kesim
-
-CREATE FUNCTION create_table_age_withdraw(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'age_withdraw';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(age_withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',h_commitment BYTEA NOT NULL CONSTRAINT h_commitment_length CHECK(LENGTH(h_commitment)=64)'
- ',max_age SMALLINT NOT NULL CONSTRAINT max_age_positive CHECK(max_age>=0)'
- ',amount_with_fee taler_amount NOT NULL'
- ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)'
- ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)'
- ',noreveal_index SMALLINT NOT NULL CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
- ',h_blind_evs BYTEA[] NOT NULL CONSTRAINT h_blind_evs_length CHECK(cardinality(h_blind_evs)=cardinality(denom_serials))'
- ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
- ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (reserve_pub)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Commitments made when withdrawing coins with age restriction and the gamma value chosen by the exchange. '
- 'It also contains the blindly signed coins, their signatures and denominations.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The gamma value chosen by the exchange in the cut-and-choose protocol'
- ,'noreveal_index'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The maximum age (in years) that the client commits to with this request'
- ,'max_age'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Commitment made by the client, hash over the various client inputs in the cut-and-choose protocol'
- ,'h_commitment'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Reference to the public key of the reserve from which the coins are going to be withdrawn'
- ,'reserve_pub'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Signature of the reserve''s private key over the age-withdraw request'
- ,'reserve_sig'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of references to the denominations'
- ,'denom_serials'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of the blinded envelopes of the chosen fresh coins, with value as given by the denomination in the corresponding slot in denom_serials'
- ,'h_blind_evs'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of signatures over each blinded envelope'
- ,'denom_sigs'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_age_withdraw(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'age_withdraw';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD PRIMARY KEY (h_commitment);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_h_commitment_reserve_pub_key'
- ' UNIQUE (h_commitment, reserve_pub);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_age_withdraw_id_key'
- ' UNIQUE (age_withdraw_id);'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_age_withdraw()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'age_withdraw';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
- ' FOREIGN KEY (reserve_pub)'
- ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
- );
-END
-$$;
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
-VALUES
- ('age_withdraw', 'exchange-0002', 'create', TRUE ,FALSE),
- ('age_withdraw', 'exchange-0002', 'constrain',TRUE ,FALSE),
- ('age_withdraw', 'exchange-0002', 'foreign', TRUE ,FALSE);
-
diff --git a/src/exchangedb/0002-recoup.sql b/src/exchangedb/0002-recoup.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2023 Taler Systems SA
+-- Copyright (C) 2025 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
@@ -23,6 +23,7 @@ AS $$
DECLARE
table_name TEXT DEFAULT 'recoup';
BEGIN
+
PERFORM create_partitioned_table(
'CREATE TABLE %I'
'(recoup_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY'
@@ -31,7 +32,7 @@ BEGIN
',coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)'
',amount taler_amount NOT NULL'
',recoup_timestamp INT8 NOT NULL'
- ',reserve_out_serial_id INT8 NOT NULL'
+ ',withdraw_id INT8 NOT NULL'
') %s ;'
,table_name
,'PARTITION BY HASH (coin_pub);'
@@ -49,8 +50,8 @@ BEGIN
,partition_suffix
);
PERFORM comment_partitioned_column(
- 'Identifies the h_blind_ev of the recouped coin and provides the link to the credited reserve.'
- ,'reserve_out_serial_id'
+ 'Identifies the h_commitment of the recouped coin and provides the link to the credited reserve.'
+ ,'withdraw_id'
,table_name
,partition_suffix
);
@@ -94,7 +95,7 @@ END
$$;
-CREATE FUNCTION foreign_table_recoup()
+CREATE OR REPLACE FUNCTION foreign_table_recoup()
RETURNS VOID
LANGUAGE plpgsql
AS $$
@@ -103,9 +104,9 @@ DECLARE
BEGIN
EXECUTE FORMAT (
'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_reserves_out'
- ' FOREIGN KEY (reserve_out_serial_id) '
- ' REFERENCES reserves_out (reserve_out_serial_id) ON DELETE CASCADE'
+ ' ADD CONSTRAINT ' || table_name || '_foreign_withdraw'
+ ' FOREIGN KEY (withdraw_id) '
+ ' REFERENCES withdraw (withdraw_id) ON DELETE CASCADE'
',ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
' FOREIGN KEY (coin_pub) '
' REFERENCES known_coins (coin_pub)'
@@ -125,15 +126,15 @@ DECLARE
BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE %I'
- '(reserve_out_serial_id INT8 NOT NULL' -- REFERENCES reserves (reserve_out_serial_id) ON DELETE CASCADE
+ '(withdraw_id INT8 NOT NULL' -- REFERENCES withdraw (withdraw_id) ON DELETE CASCADE
',coin_pub BYTEA CHECK (LENGTH(coin_pub)=32)' -- REFERENCES known_coins (coin_pub)
') %s ;'
,table_name
- ,'PARTITION BY HASH (reserve_out_serial_id)'
+ ,'PARTITION BY HASH (withdraw_id)'
,partition_suffix
);
PERFORM comment_partitioned_table(
- 'Information in this table is strictly redundant with that of recoup, but saved by a different primary key for fast lookups by reserve_out_serial_id.'
+ 'Information in this table is strictly redundant with that of recoup, but saved by a different primary key for fast lookups by withdraw_id.'
,table_name
,partition_suffix
);
@@ -154,22 +155,22 @@ BEGIN
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_main_index '
'ON ' || table_name || ' '
- '(reserve_out_serial_id);'
+ '(withdraw_id);'
);
END
$$;
-CREATE FUNCTION recoup_insert_trigger()
+CREATE OR REPLACE FUNCTION recoup_insert_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO recoup_by_reserve
- (reserve_out_serial_id
+ (withdraw_id
,coin_pub)
VALUES
- (NEW.reserve_out_serial_id
+ (NEW.withdraw_id
,NEW.coin_pub);
INSERT INTO coin_history
(coin_pub
@@ -187,23 +188,23 @@ BEGIN
res.reserve_pub
,'recoup'
,NEW.recoup_uuid
- FROM reserves_out rout
+ FROM withdraw wd
JOIN reserves res
- USING (reserve_uuid)
- WHERE rout.reserve_out_serial_id = NEW.reserve_out_serial_id;
+ USING (reserve_pub)
+ WHERE wd.withdraw_id = NEW.withdraw_id;
RETURN NEW;
END $$;
COMMENT ON FUNCTION recoup_insert_trigger()
IS 'Replicates recoup inserts into recoup_by_reserve table and updates the coin_history table.';
-CREATE FUNCTION recoup_delete_trigger()
+CREATE OR REPLACE FUNCTION recoup_delete_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM recoup_by_reserve
- WHERE reserve_out_serial_id = OLD.reserve_out_serial_id
+ WHERE withdraw_id = OLD.withdraw_id
AND coin_pub = OLD.coin_pub;
RETURN OLD;
END $$;
diff --git a/src/exchangedb/0002-recoup_refresh.sql b/src/exchangedb/0002-recoup_refresh.sql
@@ -1,6 +1,6 @@
--
-- This file is part of TALER
--- Copyright (C) 2014--2023 Taler Systems SA
+-- Copyright (C) 2025 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
@@ -33,7 +33,7 @@ BEGIN
',coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)'
',amount taler_amount NOT NULL'
',recoup_timestamp INT8 NOT NULL'
- ',rrc_serial INT8 NOT NULL'
+ ',refresh_id INT8 NOT NULL'
') %s ;'
,table_name
,'PARTITION BY HASH (coin_pub)'
@@ -58,7 +58,7 @@ BEGIN
);
PERFORM comment_partitioned_column(
'Link to the refresh operation. Also identifies the h_blind_ev of the recouped coin (as h_coin_ev).'
- ,'rrc_serial'
+ ,'refresh_id'
,table_name
,partition_suffix
);
@@ -84,12 +84,12 @@ BEGIN
table_name = concat_ws('_', table_name, partition_suffix);
EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_rrc_serial_index'
+ 'CREATE INDEX ' || table_name || '_by_refresh_id_index'
' ON ' || table_name || ' '
- '(rrc_serial);'
+ '(refresh_id);'
);
EXECUTE FORMAT (
- 'COMMENT ON INDEX ' || table_name || '_by_rrc_serial_index '
+ 'COMMENT ON INDEX ' || table_name || '_by_refresh_id_index '
'IS ' || quote_literal('used in exchange_do_melt for zombie coins (rare)') || ';'
);
@@ -122,9 +122,9 @@ BEGIN
',ADD CONSTRAINT ' || table_name || '_foreign_known_coin_id'
' FOREIGN KEY (known_coin_id) '
' REFERENCES known_coins (known_coin_id) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_rrc_serial'
- ' FOREIGN KEY (rrc_serial) '
- ' REFERENCES refresh_revealed_coins (rrc_serial) ON DELETE CASCADE'
+ ',ADD CONSTRAINT ' || table_name || '_foreign_refresh_id'
+ ' FOREIGN KEY (refresh_id) '
+ ' REFERENCES refresh (refresh_id) ON DELETE CASCADE'
);
END
$$;
@@ -148,13 +148,11 @@ BEGIN
,table_name
,serial_id)
SELECT
- melt.old_coin_pub
+ refresh.old_coin_pub
,'recoup_refresh::OLD'
,NEW.recoup_refresh_uuid
- FROM refresh_revealed_coins rrc
- JOIN refresh_commitments melt
- USING (melt_serial_id)
- WHERE rrc.rrc_serial = NEW.rrc_serial;
+ FROM refresh
+ WHERE refresh.refresh_id = NEW.refresh_id;
RETURN NEW;
END $$;
COMMENT ON FUNCTION coin_deposits_insert_trigger()
diff --git a/src/exchangedb/0002-refresh.sql b/src/exchangedb/0002-refresh.sql
@@ -0,0 +1,246 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2025 Taler Systems SA
+--
+-- TALER is free software; you can redistribute it and/or modify it under the
+-- terms of the GNU General Public License as published by the Free Software
+-- Foundation; either version 3, or (at your option) any later version.
+--
+-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+--
+-- @author Özgür Kesim
+
+CREATE FUNCTION create_table_refresh(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'refresh';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(refresh_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',rc BYTEA PRIMARY KEY CONSTRAINT rc_length CHECK(LENGTH(rc)=64)'
+ ',execution_date INT8 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
+ ',old_coin_pub BYTEA NOT NULL'
+ ',old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)'
+ ',refresh_seed BYTEA NOT NULL'
+ ',noreveal_index INT4 NOT NULL CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
+ ',planchets_h BYTEA CONSTRAINT planchets_h_length CHECK(LENGTH(planchets_h)=64)'
+ ',selected_h BYTEA CONSTRAINT selected_h_length CHECK(LENGTH(selected_h)=64)'
+ ',blinding_seed BYTEA CONSTRAINT blinding_seed_length CHECK(LENGTH(blinding_seed)>=32)'
+ ',cs_r_values BYTEA[]'
+ ',cs_r_choices INT8'
+ ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
+ ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (rc)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'The data provided by the client for the melting operation of an old coin and he choices made by the exchange '
+ ' with respect to the cut-and-choose protocol: nonreveal_index and the corresponding chosen'
+ ' blinded coin envelope along with the denomination signatures at the moment of the melting.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The hash over the refresh request, which serves as the primary key'
+ ' for the lookup during the reveal phase.'
+ ,'rc'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The publice nonce from which all other nonces for all n*kappa coin candidates are derived for which'
+ ' the old coin proves ownership via signatures'
+ ,'refresh_seed'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The gamma value chosen by the exchange in the cut-and-choose protocol'
+ ,'noreveal_index'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The date of execution of the melting operation, according to the exchange'
+ ,'execution_date'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Reference to the public key of the old coin which is melted'
+ ,'old_coin_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Signature of the old coin''s private key over the melt request'
+ ,'old_coin_sig'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of references to the denominations'
+ ,'denom_serials'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The master seed for the blinding nonces, needed for blind CS signatures; maybe NULL'
+ ,'blinding_seed'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The public pairs of R-values provided by the exchange for the CS denominations; might be NULL'
+ ,'cs_r_values'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The bitvector of choices made by the exchange for each of the pairs in cs_r_values; maybe NULL.'
+ 'The vector is stored in network byte order and the lowest bit corresponds to the 0-th entry in cs_r_values (pair)'
+ ,'cs_r_choices'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The hash over all kappa*n blinded planchets that were provided by the client'
+ ,'planchets_h'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The hash over the n blinded planchets that were selected by the exchange.'
+ ,'selected_h'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of signatures, one for each blinded envelope'
+ ,'denom_sigs'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_refresh(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'refresh';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ -- Note: index spans partitions, may need to be materialized.
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_by_old_coin_pub_index '
+ 'ON ' || table_name || ' '
+ '(old_coin_pub);'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_refresh_id_key'
+ ' UNIQUE (refresh_id);'
+ );
+END
+$$;
+
+
+CREATE FUNCTION foreign_table_refresh()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'refresh';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
+ ' FOREIGN KEY (old_coin_pub) '
+ ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
+ );
+END
+$$;
+
+
+-- Trigger to update the reserve_history table
+CREATE FUNCTION refresh_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO coin_history
+ (coin_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.old_coin_pub
+ ,'refresh'
+ ,NEW.refresh_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION refresh_insert_trigger()
+ IS 'Keep track of a particular refresh in the coin_history table.';
+
+-- Trigger to update the unique_refresh_blinding_seed table
+CREATE FUNCTION refresh_delete_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ DELETE FROM unique_refresh_blinding_seed
+ WHERE blinding_seed = OLD.blinding_seed;
+ RETURN OLD;
+END $$;
+COMMENT ON FUNCTION refresh_delete_trigger()
+ IS 'Delete blinding_seed from unique_refresh_blinding_seed table.';
+
+-- Put the triggers into the master table
+CREATE FUNCTION master_table_refresh()
+ RETURNS void
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ CREATE TRIGGER refresh_on_insert
+ AFTER INSERT
+ ON refresh
+ FOR EACH ROW EXECUTE FUNCTION refresh_insert_trigger();
+
+ CREATE TRIGGER refresh_on_delete
+ AFTER DELETE
+ ON refresh
+ FOR EACH ROW EXECUTE FUNCTION refresh_delete_trigger();
+END $$;
+COMMENT ON FUNCTION master_table_refresh()
+ IS 'Setup triggers to replicate refresh into coin_history and delete blinding_seed from unique_refresh_blinding_seed.';
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('refresh', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('refresh', 'exchange-0002', 'constrain',TRUE ,FALSE),
+ ('refresh', 'exchange-0002', 'foreign', TRUE ,FALSE),
+ ('refresh', 'exchange-0002', 'master', TRUE ,FALSE);
+
diff --git a/src/exchangedb/0002-refresh_commitments.sql b/src/exchangedb/0002-refresh_commitments.sql
@@ -1,166 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--2023 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
-
-CREATE FUNCTION create_table_refresh_commitments(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_commitments';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(melt_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64)'
- ',old_coin_pub BYTEA NOT NULL'
- ',old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)'
- ',amount_with_fee taler_amount NOT NULL'
- ',noreveal_index INT4 NOT NULL'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (rc)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Commitments made when melting coins and the gamma value chosen by the exchange.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The gamma value chosen by the exchange in the cut-and-choose protocol'
- ,'noreveal_index'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Commitment made by the client, hash over the various client inputs in the cut-and-choose protocol'
- ,'rc'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Coin being melted in the refresh process.'
- ,'old_coin_pub'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_refresh_commitments(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_commitments';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
-
- -- Note: index spans partitions, may need to be materialized.
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_old_coin_pub_index '
- 'ON ' || table_name || ' '
- '(old_coin_pub);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_melt_serial_id_key'
- ' UNIQUE (melt_serial_id)'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_refresh_commitments()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_commitments';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
- ' FOREIGN KEY (old_coin_pub) '
- ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
- );
-END
-$$;
-
-
-CREATE OR REPLACE FUNCTION refresh_commitments_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO exchange.coin_history
- (coin_pub
- ,table_name
- ,serial_id)
- VALUES
- (NEW.old_coin_pub
- ,'refresh_commitments'
- ,NEW.melt_serial_id);
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION refresh_commitments_insert_trigger()
- IS 'Automatically generate coin history entry.';
-
-
-CREATE FUNCTION master_table_refresh_commitments()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- CREATE TRIGGER refresh_commitments_on_insert
- AFTER INSERT
- ON refresh_commitments
- FOR EACH ROW EXECUTE FUNCTION refresh_commitments_insert_trigger();
-END $$;
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('refresh_commitments'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,FALSE),
- ('refresh_commitments'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('refresh_commitments'
- ,'exchange-0002'
- ,'foreign'
- ,TRUE
- ,FALSE),
- ('refresh_commitments'
- ,'exchange-0002'
- ,'master'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0002-refresh_revealed_coins.sql b/src/exchangedb/0002-refresh_revealed_coins.sql
@@ -1,169 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
-
-CREATE FUNCTION create_table_refresh_revealed_coins(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_revealed_coins';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(rrc_serial BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',melt_serial_id INT8 NOT NULL'
- ',freshcoin_index INT4 NOT NULL'
- ',link_sig BYTEA NOT NULL CHECK(LENGTH(link_sig)=64)'
- ',denominations_serial INT8 NOT NULL'
- ',coin_ev BYTEA NOT NULL'
- ',h_coin_ev BYTEA NOT NULL CHECK(LENGTH(h_coin_ev)=64)'
- ',ev_sig BYTEA NOT NULL'
- ',ewv BYTEA NOT NULL'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (melt_serial_id)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Revelations about the new coins that are to be created during a melting session.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'needed for exchange-auditor replication logic'
- ,'rrc_serial'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Identifies the refresh commitment (rc) of the melt operation.'
- ,'melt_serial_id'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'index of the fresh coin being created (one melt operation may result in multiple fresh coins)'
- ,'freshcoin_index'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Signature of type WALLET_COIN_LINK, proves exchange did not tamper with the link data'
- ,'link_sig'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'envelope of the new coin to be signed'
- ,'coin_ev'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'exchange contributed values in the creation of the fresh coin (see /csr)'
- ,'ewv'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'hash of the envelope of the new coin to be signed (for lookups)'
- ,'h_coin_ev'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'exchange signature over the envelope'
- ,'ev_sig'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_refresh_revealed_coins(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_revealed_coins';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_coins_by_melt_serial_id_index '
- 'ON ' || table_name || ' '
- '(melt_serial_id);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_rrc_serial_key'
- ' UNIQUE (rrc_serial) '
- ',ADD CONSTRAINT ' || table_name || '_coin_ev_key'
- ' UNIQUE (coin_ev) '
- ',ADD CONSTRAINT ' || table_name || '_h_coin_ev_key'
- ' UNIQUE (h_coin_ev) '
- ',ADD PRIMARY KEY (melt_serial_id, freshcoin_index)'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_refresh_revealed_coins()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_revealed_coins';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_melt'
- ' FOREIGN KEY (melt_serial_id)'
- ' REFERENCES refresh_commitments (melt_serial_id) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_denom'
- ' FOREIGN KEY (denominations_serial)'
- ' REFERENCES denominations (denominations_serial) ON DELETE CASCADE'
- );
-END
-$$;
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('refresh_revealed_coins'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,FALSE),
- ('refresh_revealed_coins'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('refresh_revealed_coins'
- ,'exchange-0002'
- ,'foreign'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0002-refresh_transfer_keys.sql b/src/exchangedb/0002-refresh_transfer_keys.sql
@@ -1,127 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2014--2022 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
-
-CREATE FUNCTION create_table_refresh_transfer_keys(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_transfer_keys';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(rtc_serial BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',melt_serial_id INT8 PRIMARY KEY'
- ',transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)'
- ',transfer_privs BYTEA NOT NULL'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (melt_serial_id)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Transfer keys of a refresh operation (the data revealed to the exchange).'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'needed for exchange-auditor replication logic'
- ,'rtc_serial'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Identifies the refresh commitment (rc) of the operation.'
- ,'melt_serial_id'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'transfer public key for the gamma index'
- ,'transfer_pub'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'array of TALER_CNC_KAPPA-1 transfer private keys that have been revealed, with the gamma entry being skipped'
- ,'transfer_privs'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_refresh_transfer_keys(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_transfer_keys';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_rtc_serial_key'
- ' UNIQUE (rtc_serial)'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_refresh_transfer_keys()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh_transfer_keys';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || 'foreign_melt_serial_id'
- ' FOREIGN KEY (melt_serial_id)'
- ' REFERENCES refresh_commitments (melt_serial_id) ON DELETE CASCADE'
- );
-END
-$$;
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('refresh_transfer_keys'
- ,'exchange-0002'
- ,'create'
- ,TRUE
- ,FALSE),
- ('refresh_transfer_keys'
- ,'exchange-0002'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('refresh_transfer_keys'
- ,'exchange-0002'
- ,'foreign'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0002-unique_refresh_blinding_seed.sql b/src/exchangedb/0002-unique_refresh_blinding_seed.sql
@@ -0,0 +1,72 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2025 Taler Systems SA
+--
+-- TALER is free software; you can redistribute it and/or modify it under the
+-- terms of the GNU General Public License as published by the Free Software
+-- Foundation; either version 3, or (at your option) any later version.
+--
+-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+--
+-- @author Özgür Kesim
+
+CREATE FUNCTION create_table_unique_refresh_blinding_seed(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'unique_refresh_blinding_seed';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(unique_refresh_blinding_seed_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',blinding_seed BYTEA PRIMARY KEY'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (blinding_seed)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Table to ensure uniqueness of the blinding_seed for CS signatures across all refresh operations. '
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_unique_refresh_blinding_seed(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'unique_refresh_blinding_seed';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_refresh_id_key'
+ ' UNIQUE (unique_refresh_blinding_seed_id);'
+ );
+END
+$$;
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('unique_refresh_blinding_seed', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('unique_refresh_blinding_seed', 'exchange-0002', 'constrain',TRUE ,FALSE);
+
diff --git a/src/exchangedb/0002-unique_withdraw_blinding_seed.sql b/src/exchangedb/0002-unique_withdraw_blinding_seed.sql
@@ -0,0 +1,72 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2025 Taler Systems SA
+--
+-- TALER is free software; you can redistribute it and/or modify it under the
+-- terms of the GNU General Public License as published by the Free Software
+-- Foundation; either version 3, or (at your option) any later version.
+--
+-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+--
+-- @author Özgür Kesim
+
+CREATE FUNCTION create_table_unique_withdraw_blinding_seed(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'unique_withdraw_blinding_seed';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(unique_withdraw_blinding_seed_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',blinding_seed BYTEA PRIMARY KEY'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (blinding_seed)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Table to ensure uniqueness of the blinding_seed for CS signatures across all withdraw operations. '
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_unique_withdraw_blinding_seed(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'unique_withdraw_blinding_seed';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_withdraw_id_key'
+ ' UNIQUE (unique_withdraw_blinding_seed_id);'
+ );
+END
+$$;
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('unique_withdraw_blinding_seed', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('unique_withdraw_blinding_seed', 'exchange-0002', 'constrain',TRUE ,FALSE);
+
diff --git a/src/exchangedb/0002-withdraw.sql b/src/exchangedb/0002-withdraw.sql
@@ -0,0 +1,241 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2025 Taler Systems SA
+--
+-- TALER is free software; you can redistribute it and/or modify it under the
+-- terms of the GNU General Public License as published by the Free Software
+-- Foundation; either version 3, or (at your option) any later version.
+--
+-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+--
+-- @author Özgür Kesim
+
+CREATE FUNCTION create_table_withdraw(
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'withdraw';
+BEGIN
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE %I'
+ '(withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
+ ',planchets_h BYTEA CONSTRAINT planchets_h_length CHECK(LENGTH(planchets_h)=64)'
+ ',execution_date INT8 NOT NULL'
+ ',amount_with_fee taler_amount NOT NULL'
+ ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)'
+ ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)'
+ ',max_age SMALLINT CONSTRAINT max_age_positive CHECK(max_age>=0)'
+ ',noreveal_index SMALLINT CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
+ ',selected_h BYTEA CONSTRAINT selected_h_length CHECK(LENGTH(selected_h)=64)'
+ ',blinding_seed BYTEA CONSTRAINT blinding_seed_length CHECK(LENGTH(blinding_seed)>=32)'
+ ',cs_r_values BYTEA[]'
+ ',cs_r_choices INT8'
+ ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
+ ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (reserve_pub)'
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_table(
+ 'Commitments made when withdrawing coins and, in case of required proof of age restriction, the gamma value chosen by the exchange. '
+ 'It also contains the blindly signed coins, their signatures and denominations.'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'If the client explicitly commits to age-restricted coins, the gamma value chosen by the exchange in the cut-and-choose protocol; might be NULL.'
+ ,'noreveal_index'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The running hash over all committed blinded planchets. Needed for recoup and (when a proof of age-restriction was required)'
+ ' in the subsequent cut-and-choose protocol.'
+ ,'planchets_h'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The date of execution of this withdrawal, according to the exchange'
+ ,'execution_date'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'If the clients commits to age-restricted coins, the maximum age (in years) that the client explicitly commits to with this request; might be NULL.'
+ ,'max_age'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Reference to the public key of the reserve from which the coins are going to be withdrawn'
+ ,'reserve_pub'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Signature of the reserve''s private key over the withdraw request'
+ ,'reserve_sig'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of references to the denominations'
+ ,'denom_serials'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'In case of age restriction, the hash of the chosen (noreveal_index) blinded envelopes.'
+ ,'selected_h'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'Array of signatures over each blinded envelope. If age-proof was not required, the signed envelopes are the ones'
+ ' hashed into planchet_h. Otherwise (when age-proof is required) the selected planchets (noreveal_index) were signed,'
+ ' hashed into selected_h.'
+ ,'denom_sigs'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The master seed for the blinding nonces, needed for blind CS signatures; maybe NULL'
+ ,'blinding_seed'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The pairs of R-values (calculated by the exchange) for the coins of cipher type Clause-Schnorr, based on the blinding_seed; maybe NULL.'
+ ,'cs_r_values'
+ ,table_name
+ ,partition_suffix
+ );
+ PERFORM comment_partitioned_column(
+ 'The bitvector of choices made by the exchange for each of the pairs in cs_r_values; maybe NULL.'
+ 'The vector is stored in network byte order and the lowest bit corresponds to the 0-th entry in cs_r_values (pair)'
+ ,'cs_r_choices'
+ ,table_name
+ ,partition_suffix
+ );
+END
+$$;
+
+
+CREATE FUNCTION constrain_table_withdraw(
+ IN partition_suffix TEXT
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'withdraw';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+ EXECUTE FORMAT (
+ 'CREATE INDEX ' || table_name || '_by_reserve_pub_index '
+ 'ON ' || table_name || ' '
+ '(reserve_pub);'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD PRIMARY KEY (reserve_pub, planchets_h);'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_withdraw_id_key'
+ ' UNIQUE (withdraw_id);'
+ );
+END
+$$;
+
+
+CREATE FUNCTION foreign_table_withdraw()
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'withdraw';
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
+ ' FOREIGN KEY (reserve_pub)'
+ ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
+ );
+END
+$$;
+
+-- Trigger to update the reserve_history table
+CREATE FUNCTION withdraw_insert_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ INSERT INTO reserve_history
+ (reserve_pub
+ ,table_name
+ ,serial_id)
+ VALUES
+ (NEW.reserve_pub
+ ,'withdraw'
+ ,NEW.withdraw_id);
+ RETURN NEW;
+END $$;
+COMMENT ON FUNCTION withdraw_insert_trigger()
+ IS 'Keep track of a particular withdraw in the reserve_history table.';
+
+-- Trigger to update the unique_withdraw_blinding_seed table
+CREATE FUNCTION withdraw_delete_trigger()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ DELETE FROM unique_withdraw_blinding_seed
+ WHERE blinding_seed = OLD.blinding_seed;
+ RETURN OLD;
+END $$;
+COMMENT ON FUNCTION withdraw_delete_trigger()
+ IS 'Delete blinding_seed from unique_withdraw_blinding_seed table.';
+
+-- Put the triggers into the master table
+CREATE FUNCTION master_table_withdraw()
+ RETURNS void
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ CREATE TRIGGER withdraw_on_insert
+ AFTER INSERT
+ ON withdraw
+ FOR EACH ROW EXECUTE FUNCTION withdraw_insert_trigger();
+
+ CREATE TRIGGER withdraw_on_delete
+ AFTER DELETE
+ ON withdraw
+ FOR EACH ROW EXECUTE FUNCTION withdraw_delete_trigger();
+END $$;
+COMMENT ON FUNCTION master_table_withdraw()
+ IS 'Setup triggers to replicate withdraw into reserve_history and delete blinding_seed from unique_withdraw_blinding_seed.';
+
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+VALUES
+ ('withdraw', 'exchange-0002', 'create', TRUE ,FALSE),
+ ('withdraw', 'exchange-0002', 'constrain',TRUE ,FALSE),
+ ('withdraw', 'exchange-0002', 'foreign', TRUE ,FALSE),
+ ('withdraw', 'exchange-0002', 'master', TRUE ,FALSE);
+
diff --git a/src/exchangedb/0009-age_withdraw.sql b/src/exchangedb/0009-age_withdraw.sql
@@ -1,43 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
--- @author Özgür Kesim
-
-
-CREATE FUNCTION alter_table_age_withdraw9(
- IN partition_suffix TEXT DEFAULT NULL)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- DROP table age_withdraw;
-END;
-$$;
-COMMENT ON FUNCTION alter_table_age_withdraw9
- IS 'Drop the table age_withdraw';
-
-
-INSERT INTO exchange.exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
-VALUES
- ('age_withdraw9'
- ,'exchange-0009'
- ,'alter'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0009-recoup.sql b/src/exchangedb/0009-recoup.sql
@@ -1,295 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
-
-CREATE FUNCTION alter_table_recoup2()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- -- We first drop the existing tables
- RAISE NOTICE 'Dropping old recoup tables';
- DROP TABLE recoup_by_reserve;
- DROP TABLE recoup;
-
- -- Altering the function names below for creating the
- -- new tables to become the functions to be called
- -- by the versioning framework, as it expects the
- -- suffix to be the same as the table name.
- ALTER FUNCTION create_table_recoup9(TEXT)
- RENAME TO create_table_recoup;
- ALTER FUNCTION create_table_recoup_by_reserve9(TEXT)
- RENAME TO create_table_recoup_by_reserve;
-END
-$$;
-
-
-CREATE FUNCTION create_table_recoup9(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup';
-BEGIN
-
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(recoup_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
- ',coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)'
- ',coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)'
- ',amount taler_amount NOT NULL'
- ',recoup_timestamp INT8 NOT NULL'
- ',withdraw_id INT8 NOT NULL'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (coin_pub);'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Information about recoups that were executed between a coin and a reserve. In this type of recoup, the amount is credited back to the reserve from which the coin originated.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Coin that is being debited in the recoup. Do not CASCADE ON DROP on the coin_pub, as we may keep the coin alive!'
- ,'coin_pub'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Identifies the h_commitment of the recouped coin and provides the link to the credited reserve.'
- ,'withdraw_id'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Signature by the coin affirming the recoup, of type TALER_SIGNATURE_WALLET_COIN_RECOUP'
- ,'coin_sig'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Denomination blinding key used when creating the blinded coin from the planchet. Secret revealed during the recoup to provide the linkage between the coin and the withdraw operation.'
- ,'coin_blind'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_recoup9(
- IN partition_suffix TEXT
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_coin_pub_index '
- 'ON ' || table_name || ' '
- '(coin_pub);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_recoup_uuid_key'
- ' UNIQUE (recoup_uuid) '
- );
-END
-$$;
-
-
-CREATE OR REPLACE FUNCTION foreign_table_recoup9()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_withdraw'
- ' FOREIGN KEY (withdraw_id) '
- ' REFERENCES withdraw (withdraw_id) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
- ' FOREIGN KEY (coin_pub) '
- ' REFERENCES known_coins (coin_pub)'
- );
-END
-$$;
-
-
-CREATE FUNCTION create_table_recoup_by_reserve9(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup_by_reserve';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(withdraw_id INT8 NOT NULL' -- REFERENCES withdraw (withdraw_id) ON DELETE CASCADE
- ',coin_pub BYTEA CHECK (LENGTH(coin_pub)=32)' -- REFERENCES known_coins (coin_pub)
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (withdraw_id)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Information in this table is strictly redundant with that of recoup, but saved by a different primary key for fast lookups by withdraw_id.'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_recoup_by_reserve9(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup_by_reserve';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_main_index '
- 'ON ' || table_name || ' '
- '(withdraw_id);'
- );
-END
-$$;
-
-
-CREATE OR REPLACE FUNCTION recoup_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO recoup_by_reserve
- (withdraw_id
- ,coin_pub)
- VALUES
- (NEW.withdraw_id
- ,NEW.coin_pub);
- INSERT INTO coin_history
- (coin_pub
- ,table_name
- ,serial_id)
- VALUES
- (NEW.coin_pub
- ,'recoup'
- ,NEW.recoup_uuid);
- INSERT INTO reserve_history
- (reserve_pub
- ,table_name
- ,serial_id)
- SELECT
- res.reserve_pub
- ,'recoup'
- ,NEW.recoup_uuid
- FROM withdraw wd
- JOIN reserves res
- USING (reserve_pub)
- WHERE wd.withdraw_id = NEW.withdraw_id;
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION recoup_insert_trigger()
- IS 'Replicates recoup inserts into recoup_by_reserve table and updates the coin_history table.';
-
-
-CREATE OR REPLACE FUNCTION recoup_delete_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- DELETE FROM recoup_by_reserve
- WHERE withdraw_id = OLD.withdraw_id
- AND coin_pub = OLD.coin_pub;
- RETURN OLD;
-END $$;
-COMMENT ON FUNCTION recoup_delete_trigger()
- IS 'Replicate recoup deletions into recoup_by_reserve table.';
-
-
-CREATE FUNCTION master_table_recoup9()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- CREATE TRIGGER recoup_on_insert
- AFTER INSERT
- ON recoup
- FOR EACH ROW EXECUTE FUNCTION recoup_insert_trigger();
- CREATE TRIGGER recoup_on_delete
- AFTER DELETE
- ON recoup
- FOR EACH ROW EXECUTE FUNCTION recoup_delete_trigger();
-END
-$$;
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('recoup2'
- ,'exchange-0009'
- ,'alter'
- ,TRUE
- ,FALSE),
- ('recoup' -- Note: actual table name needed for create by the versioning framework
- ,'exchange-0009'
- ,'create'
- ,TRUE
- ,FALSE),
- ('recoup9'
- ,'exchange-0009'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('recoup9'
- ,'exchange-0009'
- ,'foreign'
- ,TRUE
- ,FALSE),
- ('recoup_by_reserve' -- Note: actual table name needed for create by the versioning framework
- ,'exchange-0009'
- ,'create'
- ,TRUE
- ,FALSE),
- ('recoup_by_reserve9'
- ,'exchange-0009'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('recoup9'
- ,'exchange-0009'
- ,'master'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0009-recoup_refresh.sql b/src/exchangedb/0009-recoup_refresh.sql
@@ -1,225 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
-
-
-CREATE FUNCTION alter_table_recoup_refresh2()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- -- We first drop the existing tables
- RAISE NOTICE 'Dropping old recoup_refresh table';
- DROP TABLE recoup_refresh;
-
- -- Altering the function names below for creating the
- -- new tables to become the functions to be called
- -- by the versioning framework, as it expects the
- -- suffix to be the same as the table name.
- ALTER FUNCTION create_table_recoup_refresh9(TEXT)
- RENAME TO create_table_recoup_refresh;
-END
-$$;
-
-
-CREATE FUNCTION create_table_recoup_refresh9(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup_refresh';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(recoup_refresh_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
- ',known_coin_id BIGINT NOT NULL'
- ',coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)'
- ',coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)'
- ',amount taler_amount NOT NULL'
- ',recoup_timestamp INT8 NOT NULL'
- ',refresh_id INT8 NOT NULL'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (coin_pub)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Table of coins that originated from a refresh operation and that were recouped. Links the (fresh) coin to the melted operation (and thus the old coin). A recoup on a refreshed coin credits the old coin and debits the fresh coin.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Refreshed coin of a revoked denomination where the residual value is credited to the old coin. Do not CASCADE ON DROP on the coin_pub, as we may keep the coin alive!'
- ,'coin_pub'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Used for garbage collection (in the absence of foreign constraints, in the future)'
- ,'known_coin_id'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Link to the refresh operation. Also identifies the h_blind_ev of the recouped coin (as h_coin_ev).'
- ,'refresh_id'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Denomination blinding key used when creating the blinded coin from the planchet. Secret revealed during the recoup to provide the linkage between the coin and the refresh operation.'
- ,'coin_blind'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_recoup_refresh9(
- IN partition_suffix TEXT
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup_refresh';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
-
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_refresh_id_index'
- ' ON ' || table_name || ' '
- '(refresh_id);'
- );
- EXECUTE FORMAT (
- 'COMMENT ON INDEX ' || table_name || '_by_refresh_id_index '
- 'IS ' || quote_literal('used in exchange_do_melt for zombie coins (rare)') || ';'
- );
-
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_coin_pub_index'
- ' ON ' || table_name || ' '
- '(coin_pub);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_recoup_refresh_uuid_key'
- ' UNIQUE (recoup_refresh_uuid) '
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_recoup_refresh9()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'recoup_refresh';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
- ' FOREIGN KEY (coin_pub) '
- ' REFERENCES known_coins (coin_pub)'
- ',ADD CONSTRAINT ' || table_name || '_foreign_known_coin_id'
- ' FOREIGN KEY (known_coin_id) '
- ' REFERENCES known_coins (known_coin_id) ON DELETE CASCADE'
- ',ADD CONSTRAINT ' || table_name || '_foreign_refresh_id'
- ' FOREIGN KEY (refresh_id) '
- ' REFERENCES refresh (refresh_id) ON DELETE CASCADE'
- );
-END
-$$;
-
-
-CREATE OR REPLACE FUNCTION recoup_refresh_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO exchange.coin_history
- (coin_pub
- ,table_name
- ,serial_id)
- VALUES
- (NEW.coin_pub
- ,'recoup_refresh::NEW'
- ,NEW.recoup_refresh_uuid);
- INSERT INTO exchange.coin_history
- (coin_pub
- ,table_name
- ,serial_id)
- SELECT
- refresh.old_coin_pub
- ,'recoup_refresh::OLD'
- ,NEW.recoup_refresh_uuid
- FROM refresh
- WHERE refresh.refresh_id = NEW.refresh_id;
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION coin_deposits_insert_trigger()
- IS 'Automatically generate coin history entry.';
-
-
-CREATE FUNCTION master_table_recoup_refresh9()
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- CREATE TRIGGER recoup_refresh_on_insert
- AFTER INSERT
- ON recoup_refresh
- FOR EACH ROW EXECUTE FUNCTION recoup_refresh_insert_trigger();
-END $$;
-
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
- VALUES
- ('recoup_refresh2'
- ,'exchange-0009'
- ,'alter'
- ,TRUE
- ,FALSE),
- ('recoup_refresh' -- Note: actual table name needed for create by the versioning framework
- ,'exchange-0009'
- ,'create'
- ,TRUE
- ,FALSE),
- ('recoup_refresh9'
- ,'exchange-0009'
- ,'constrain'
- ,TRUE
- ,FALSE),
- ('recoup_refresh9'
- ,'exchange-0009'
- ,'foreign'
- ,TRUE
- ,FALSE),
- ('recoup_refresh9'
- ,'exchange-0009'
- ,'master'
- ,TRUE
- ,FALSE);
diff --git a/src/exchangedb/0009-refresh.sql b/src/exchangedb/0009-refresh.sql
@@ -1,246 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
--- @author Özgür Kesim
-
-CREATE FUNCTION create_table_refresh(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(refresh_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',rc BYTEA PRIMARY KEY CONSTRAINT rc_length CHECK(LENGTH(rc)=64)'
- ',execution_date INT8 NOT NULL'
- ',amount_with_fee taler_amount NOT NULL'
- ',old_coin_pub BYTEA NOT NULL'
- ',old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)'
- ',refresh_seed BYTEA NOT NULL'
- ',noreveal_index INT4 NOT NULL CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
- ',planchets_h BYTEA CONSTRAINT planchets_h_length CHECK(LENGTH(planchets_h)=64)'
- ',selected_h BYTEA CONSTRAINT selected_h_length CHECK(LENGTH(selected_h)=64)'
- ',blinding_seed BYTEA CONSTRAINT blinding_seed_length CHECK(LENGTH(blinding_seed)>=32)'
- ',cs_r_values BYTEA[]'
- ',cs_r_choices INT8'
- ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
- ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (rc)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'The data provided by the client for the melting operation of an old coin and he choices made by the exchange '
- ' with respect to the cut-and-choose protocol: nonreveal_index and the corresponding chosen'
- ' blinded coin envelope along with the denomination signatures at the moment of the melting.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The hash over the refresh request, which serves as the primary key'
- ' for the lookup during the reveal phase.'
- ,'rc'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The publice nonce from which all other nonces for all n*kappa coin candidates are derived for which'
- ' the old coin proves ownership via signatures'
- ,'refresh_seed'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The gamma value chosen by the exchange in the cut-and-choose protocol'
- ,'noreveal_index'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The date of execution of the melting operation, according to the exchange'
- ,'execution_date'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Reference to the public key of the old coin which is melted'
- ,'old_coin_pub'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Signature of the old coin''s private key over the melt request'
- ,'old_coin_sig'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of references to the denominations'
- ,'denom_serials'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The master seed for the blinding nonces, needed for blind CS signatures; maybe NULL'
- ,'blinding_seed'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The public pairs of R-values provided by the exchange for the CS denominations; might be NULL'
- ,'cs_r_values'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The bitvector of choices made by the exchange for each of the pairs in cs_r_values; maybe NULL.'
- 'The vector is stored in network byte order and the lowest bit corresponds to the 0-th entry in cs_r_values (pair)'
- ,'cs_r_choices'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The hash over all kappa*n blinded planchets that were provided by the client'
- ,'planchets_h'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The hash over the n blinded planchets that were selected by the exchange.'
- ,'selected_h'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of signatures, one for each blinded envelope'
- ,'denom_sigs'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_refresh(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- -- Note: index spans partitions, may need to be materialized.
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_old_coin_pub_index '
- 'ON ' || table_name || ' '
- '(old_coin_pub);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_refresh_id_key'
- ' UNIQUE (refresh_id);'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_refresh()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'refresh';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub'
- ' FOREIGN KEY (old_coin_pub) '
- ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE'
- );
-END
-$$;
-
-
--- Trigger to update the reserve_history table
-CREATE FUNCTION refresh_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO coin_history
- (coin_pub
- ,table_name
- ,serial_id)
- VALUES
- (NEW.old_coin_pub
- ,'refresh'
- ,NEW.refresh_id);
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION refresh_insert_trigger()
- IS 'Keep track of a particular refresh in the coin_history table.';
-
--- Trigger to update the unique_refresh_blinding_seed table
-CREATE FUNCTION refresh_delete_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- DELETE FROM unique_refresh_blinding_seed
- WHERE blinding_seed = OLD.blinding_seed;
- RETURN OLD;
-END $$;
-COMMENT ON FUNCTION refresh_delete_trigger()
- IS 'Delete blinding_seed from unique_refresh_blinding_seed table.';
-
--- Put the triggers into the master table
-CREATE FUNCTION master_table_refresh()
- RETURNS void
- LANGUAGE plpgsql
- AS $$
-BEGIN
- CREATE TRIGGER refresh_on_insert
- AFTER INSERT
- ON refresh
- FOR EACH ROW EXECUTE FUNCTION refresh_insert_trigger();
-
- CREATE TRIGGER refresh_on_delete
- AFTER DELETE
- ON refresh
- FOR EACH ROW EXECUTE FUNCTION refresh_delete_trigger();
-END $$;
-COMMENT ON FUNCTION master_table_refresh()
- IS 'Setup triggers to replicate refresh into coin_history and delete blinding_seed from unique_refresh_blinding_seed.';
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
-VALUES
- ('refresh', 'exchange-0009', 'create', TRUE ,FALSE),
- ('refresh', 'exchange-0009', 'constrain',TRUE ,FALSE),
- ('refresh', 'exchange-0009', 'foreign', TRUE ,FALSE),
- ('refresh', 'exchange-0009', 'master', TRUE ,FALSE);
-
diff --git a/src/exchangedb/0009-unique_refresh_blinding_seed.sql b/src/exchangedb/0009-unique_refresh_blinding_seed.sql
@@ -1,72 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
--- @author Özgür Kesim
-
-CREATE FUNCTION create_table_unique_refresh_blinding_seed(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'unique_refresh_blinding_seed';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(unique_refresh_blinding_seed_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',blinding_seed BYTEA PRIMARY KEY'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (blinding_seed)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Table to ensure uniqueness of the blinding_seed for CS signatures across all refresh operations. '
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_unique_refresh_blinding_seed(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'unique_refresh_blinding_seed';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_refresh_id_key'
- ' UNIQUE (unique_refresh_blinding_seed_id);'
- );
-END
-$$;
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
-VALUES
- ('unique_refresh_blinding_seed', 'exchange-0009', 'create', TRUE ,FALSE),
- ('unique_refresh_blinding_seed', 'exchange-0009', 'constrain',TRUE ,FALSE);
-
diff --git a/src/exchangedb/0009-unique_withdraw_blinding_seed.sql b/src/exchangedb/0009-unique_withdraw_blinding_seed.sql
@@ -1,72 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
--- @author Özgür Kesim
-
-CREATE FUNCTION create_table_unique_withdraw_blinding_seed(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'unique_withdraw_blinding_seed';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(unique_withdraw_blinding_seed_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',blinding_seed BYTEA PRIMARY KEY'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (blinding_seed)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Table to ensure uniqueness of the blinding_seed for CS signatures across all withdraw operations. '
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_unique_withdraw_blinding_seed(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'unique_withdraw_blinding_seed';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_withdraw_id_key'
- ' UNIQUE (unique_withdraw_blinding_seed_id);'
- );
-END
-$$;
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
-VALUES
- ('unique_withdraw_blinding_seed', 'exchange-0009', 'create', TRUE ,FALSE),
- ('unique_withdraw_blinding_seed', 'exchange-0009', 'constrain',TRUE ,FALSE);
-
diff --git a/src/exchangedb/0009-withdraw.sql b/src/exchangedb/0009-withdraw.sql
@@ -1,241 +0,0 @@
---
--- This file is part of TALER
--- Copyright (C) 2025 Taler Systems SA
---
--- TALER is free software; you can redistribute it and/or modify it under the
--- terms of the GNU General Public License as published by the Free Software
--- Foundation; either version 3, or (at your option) any later version.
---
--- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
--- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
--- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License along with
--- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
---
--- @author Özgür Kesim
-
-CREATE FUNCTION create_table_withdraw(
- IN partition_suffix TEXT DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'withdraw';
-BEGIN
- PERFORM create_partitioned_table(
- 'CREATE TABLE %I'
- '(withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
- ',planchets_h BYTEA CONSTRAINT planchets_h_length CHECK(LENGTH(planchets_h)=64)'
- ',execution_date INT8 NOT NULL'
- ',amount_with_fee taler_amount NOT NULL'
- ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)'
- ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)'
- ',max_age SMALLINT CONSTRAINT max_age_positive CHECK(max_age>=0)'
- ',noreveal_index SMALLINT CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)'
- ',selected_h BYTEA CONSTRAINT selected_h_length CHECK(LENGTH(selected_h)=64)'
- ',blinding_seed BYTEA CONSTRAINT blinding_seed_length CHECK(LENGTH(blinding_seed)>=32)'
- ',cs_r_values BYTEA[]'
- ',cs_r_choices INT8'
- ',denom_serials INT8[] NOT NULL CONSTRAINT denom_serials_array_length CHECK(cardinality(denom_serials)=cardinality(denom_sigs))'
- ',denom_sigs BYTEA[] NOT NULL CONSTRAINT denom_sigs_array_length CHECK(cardinality(denom_sigs)=cardinality(denom_serials))'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (reserve_pub)'
- ,partition_suffix
- );
- PERFORM comment_partitioned_table(
- 'Commitments made when withdrawing coins and, in case of required proof of age restriction, the gamma value chosen by the exchange. '
- 'It also contains the blindly signed coins, their signatures and denominations.'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'If the client explicitly commits to age-restricted coins, the gamma value chosen by the exchange in the cut-and-choose protocol; might be NULL.'
- ,'noreveal_index'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The running hash over all committed blinded planchets. Needed for recoup and (when a proof of age-restriction was required)'
- ' in the subsequent cut-and-choose protocol.'
- ,'planchets_h'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The date of execution of this withdrawal, according to the exchange'
- ,'execution_date'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'If the clients commits to age-restricted coins, the maximum age (in years) that the client explicitly commits to with this request; might be NULL.'
- ,'max_age'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Reference to the public key of the reserve from which the coins are going to be withdrawn'
- ,'reserve_pub'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Signature of the reserve''s private key over the withdraw request'
- ,'reserve_sig'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of references to the denominations'
- ,'denom_serials'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'In case of age restriction, the hash of the chosen (noreveal_index) blinded envelopes.'
- ,'selected_h'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'Array of signatures over each blinded envelope. If age-proof was not required, the signed envelopes are the ones'
- ' hashed into planchet_h. Otherwise (when age-proof is required) the selected planchets (noreveal_index) were signed,'
- ' hashed into selected_h.'
- ,'denom_sigs'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The master seed for the blinding nonces, needed for blind CS signatures; maybe NULL'
- ,'blinding_seed'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The pairs of R-values (calculated by the exchange) for the coins of cipher type Clause-Schnorr, based on the blinding_seed; maybe NULL.'
- ,'cs_r_values'
- ,table_name
- ,partition_suffix
- );
- PERFORM comment_partitioned_column(
- 'The bitvector of choices made by the exchange for each of the pairs in cs_r_values; maybe NULL.'
- 'The vector is stored in network byte order and the lowest bit corresponds to the 0-th entry in cs_r_values (pair)'
- ,'cs_r_choices'
- ,table_name
- ,partition_suffix
- );
-END
-$$;
-
-
-CREATE FUNCTION constrain_table_withdraw(
- IN partition_suffix TEXT
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'withdraw';
-BEGIN
- table_name = concat_ws('_', table_name, partition_suffix);
- EXECUTE FORMAT (
- 'CREATE INDEX ' || table_name || '_by_reserve_pub_index '
- 'ON ' || table_name || ' '
- '(reserve_pub);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD PRIMARY KEY (reserve_pub, planchets_h);'
- );
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_withdraw_id_key'
- ' UNIQUE (withdraw_id);'
- );
-END
-$$;
-
-
-CREATE FUNCTION foreign_table_withdraw()
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name TEXT DEFAULT 'withdraw';
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE ' || table_name ||
- ' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
- ' FOREIGN KEY (reserve_pub)'
- ' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
- );
-END
-$$;
-
--- Trigger to update the reserve_history table
-CREATE FUNCTION withdraw_insert_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- INSERT INTO reserve_history
- (reserve_pub
- ,table_name
- ,serial_id)
- VALUES
- (NEW.reserve_pub
- ,'withdraw'
- ,NEW.withdraw_id);
- RETURN NEW;
-END $$;
-COMMENT ON FUNCTION withdraw_insert_trigger()
- IS 'Keep track of a particular withdraw in the reserve_history table.';
-
--- Trigger to update the unique_withdraw_blinding_seed table
-CREATE FUNCTION withdraw_delete_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- DELETE FROM unique_withdraw_blinding_seed
- WHERE blinding_seed = OLD.blinding_seed;
- RETURN OLD;
-END $$;
-COMMENT ON FUNCTION withdraw_delete_trigger()
- IS 'Delete blinding_seed from unique_withdraw_blinding_seed table.';
-
--- Put the triggers into the master table
-CREATE FUNCTION master_table_withdraw()
- RETURNS void
- LANGUAGE plpgsql
- AS $$
-BEGIN
- CREATE TRIGGER withdraw_on_insert
- AFTER INSERT
- ON withdraw
- FOR EACH ROW EXECUTE FUNCTION withdraw_insert_trigger();
-
- CREATE TRIGGER withdraw_on_delete
- AFTER DELETE
- ON withdraw
- FOR EACH ROW EXECUTE FUNCTION withdraw_delete_trigger();
-END $$;
-COMMENT ON FUNCTION master_table_withdraw()
- IS 'Setup triggers to replicate withdraw into reserve_history and delete blinding_seed from unique_withdraw_blinding_seed.';
-
-
-INSERT INTO exchange_tables
- (name
- ,version
- ,action
- ,partitioned
- ,by_range)
-VALUES
- ('withdraw', 'exchange-0009', 'create', TRUE ,FALSE),
- ('withdraw', 'exchange-0009', 'constrain',TRUE ,FALSE),
- ('withdraw', 'exchange-0009', 'foreign', TRUE ,FALSE),
- ('withdraw', 'exchange-0009', 'master', TRUE ,FALSE);
-
diff --git a/src/exchangedb/exchange-0002.sql.in b/src/exchangedb/exchange-0002.sql.in
@@ -78,15 +78,16 @@ COMMENT ON TYPE exchange_do_select_deposits_missing_wire_return_type
#include "0002-reserves_out.sql"
#include "0002-known_coins.sql"
#include "0002-coin_history.sql"
-#include "0002-refresh_commitments.sql"
-#include "0002-refresh_revealed_coins.sql"
-#include "0002-refresh_transfer_keys.sql"
#include "0002-batch_deposits.sql"
#include "0002-coin_deposits.sql"
#include "0002-refunds.sql"
#include "0002-wire_out.sql"
#include "0002-aggregation_transient.sql"
#include "0002-aggregation_tracking.sql"
+#include "0002-unique_refresh_blinding_seed.sql"
+#include "0002-refresh.sql"
+#include "0002-unique_withdraw_blinding_seed.sql"
+#include "0002-withdraw.sql"
#include "0002-recoup.sql"
#include "0002-recoup_refresh.sql"
#include "0002-prewire.sql"
@@ -112,7 +113,6 @@ COMMENT ON TYPE exchange_do_select_deposits_missing_wire_return_type
#include "0002-aml_status.sql"
#include "0002-aml_staff.sql"
#include "0002-aml_history.sql"
-#include "0002-age_withdraw.sql"
COMMIT;
diff --git a/src/exchangedb/exchange-0009.sql.in b/src/exchangedb/exchange-0009.sql.in
@@ -25,14 +25,7 @@ COMMENT ON INDEX exchange_tables_by_pending
IS 'Used by exchange_do_create_tables';
-#include "0009-age_withdraw.sql"
#include "0009-batch_deposits.sql"
-#include "0009-withdraw.sql"
-#include "0009-unique_withdraw_blinding_seed.sql"
-#include "0009-refresh.sql"
-#include "0009-unique_refresh_blinding_seed.sql"
-#include "0009-recoup.sql"
-#include "0009-recoup_refresh.sql"
#include "0009-statistics.sql"
#include "0009-aggregation_transient.sql"
diff --git a/src/exchangedb/perf_deposits_get_ready.c b/src/exchangedb/perf_deposits_get_ready.c
@@ -16,7 +16,7 @@
/**
* @file exchangedb/perf_deposits_get_ready.c
* @brief benchmark for deposits_get_ready
-git * @author Joseph Xu
+ * @author Joseph Xu
*/
#include "platform.h"
#include "taler_exchangedb_lib.h"
@@ -370,39 +370,33 @@ run (void *cls)
TALER_amount_set_zero (CURRENCY,
&cbc.withdraw_fee));
{
- bool found;
bool nonce_reuse;
bool balance_ok;
bool age_ok;
- bool conflict;
- bool denom_unknown;
+ bool idempotent;
+ uint16_t noreveal_index;
struct TALER_Amount reserve_balance;
uint16_t allowed_minimum_age;
- uint64_t ruuid;
+ uint32_t reserve_birthday;
struct GNUNET_TIME_Timestamp now;
+ struct TALER_EXCHANGEDB_Withdraw withdraw = {
+ .amount_with_fee = value,
+ .reserve_pub = reserve_pub,
+ };
now = GNUNET_TIME_timestamp_get ();
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->do_batch_withdraw (plugin->cls,
- now,
- &reserve_pub,
- &value,
- true,
- &found,
- &balance_ok,
- &reserve_balance,
- &age_ok,
- &allowed_minimum_age,
- &ruuid));
- FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- plugin->do_batch_withdraw_insert (plugin->cls,
- NULL,
- &cbc,
- now,
- ruuid,
- &denom_unknown,
- &conflict,
- &nonce_reuse));
+ plugin->do_withdraw (plugin->cls,
+ &withdraw,
+ &now,
+ &balance_ok,
+ &reserve_balance,
+ &age_ok,
+ &allowed_minimum_age,
+ &reserve_birthday,
+ &idempotent,
+ &noreveal_index,
+ &nonce_reuse));
}
{
/* ENSURE_COIN_KNOWN */
diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c
@@ -266,67 +266,6 @@ add_withdraw (void *cls,
/**
- * Add pre26 coin batch-withdrawals to result set for #TEH_PG_get_reserve_history.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param result SQL result
- * @param num_results number of rows in @a result
- */
-static void
-add_batch_withdraw_coin (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct ReserveHistoryContext *rhc = cls;
- struct PostgresClosure *pg = rhc->pg;
-
- while (0 < num_results)
- {
- struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
- struct TALER_EXCHANGEDB_ReserveHistory *tail;
-
- cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin);
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
- &cbc->h_coin_envelope),
- GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
- &cbc->denom_pub_hash),
- TALER_PQ_result_spec_blinded_denom_sig ("denom_sig",
- &cbc->sig),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
- &cbc->reserve_sig),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &cbc->amount_with_fee),
- TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw",
- &cbc->withdraw_fee),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- --num_results))
- {
- GNUNET_break (0);
- GNUNET_free (cbc);
- rhc->failed = true;
- return;
- }
- }
- GNUNET_assert (0 <=
- TALER_amount_add (&rhc->balance_out,
- &rhc->balance_out,
- &cbc->amount_with_fee));
- cbc->reserve_pub = *rhc->reserve_pub;
- tail = append_rh (rhc);
- tail->type = TALER_EXCHANGEDB_RO_BATCH_WITHDRAW_COIN;
- tail->details.batch_withdraw = cbc;
- }
-}
-
-
-/**
* Add recoups to result set for #TEH_PG_get_reserve_history.
*
* @param cls a `struct ReserveHistoryContext *`
@@ -706,10 +645,6 @@ handle_history_entry (void *cls,
{ "close_requests",
"close_request_by_reserve",
&add_close_requests },
- /** #TALER_EXCHANGEDB_RO_BATCH_WITHDRAW_COIN */
- { "reserves_out",
- "get_reserves_out",
- &add_batch_withdraw_coin },
/* List terminator */
{ NULL, NULL, NULL }
};
diff --git a/src/exchangedb/plugin_exchangedb_common.c b/src/exchangedb/plugin_exchangedb_common.c
@@ -50,15 +50,6 @@ TEH_COMMON_free_reserve_history (
GNUNET_free (wd);
break;
}
- case TALER_EXCHANGEDB_RO_BATCH_WITHDRAW_COIN:
- {
- struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc;
-
- cbc = rh->details.batch_withdraw;
- TALER_blinded_denom_sig_free (&cbc->sig);
- GNUNET_free (cbc);
- break;
- }
case TALER_EXCHANGEDB_RO_RECOUP_COIN:
{
struct TALER_EXCHANGEDB_Recoup *recoup;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -46,8 +46,6 @@
#include "pg_delete_shard_locks.h"
#include "pg_disable_rules.h"
#include "pg_do_withdraw.h"
-#include "pg_do_batch_withdraw.h"
-#include "pg_do_batch_withdraw_insert.h"
#include "pg_do_check_deposit_idempotent.h"
#include "pg_do_deposit.h"
#include "pg_do_melt.h"
@@ -502,8 +500,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_start_read_only;
plugin->insert_denomination_info
= &TEH_PG_insert_denomination_info;
- plugin->do_batch_withdraw_insert
- = &TEH_PG_do_batch_withdraw_insert;
plugin->lookup_wire_fee_by_time
= &TEH_PG_lookup_wire_fee_by_time;
plugin->start
@@ -540,10 +536,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_drain_kyc_alert;
plugin->reserves_in_insert
= &TEH_PG_reserves_in_insert;
- plugin->get_batch_withdraw_info
- = &TEH_PG_get_batch_withdraw_info;
- plugin->do_batch_withdraw
- = &TEH_PG_do_batch_withdraw;
plugin->do_withdraw
= &TEH_PG_do_withdraw;
plugin->get_withdraw
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
@@ -1741,11 +1741,6 @@ enum TALER_EXCHANGEDB_ReserveOperation
*/
TALER_EXCHANGEDB_RO_CLOSE_REQUEST = 7,
- /**
- * A Coin was withdrawn from the reserve using pre26 version /batch-withdraw.
- */
- TALER_EXCHANGEDB_RO_BATCH_WITHDRAW_COIN = 8,
-
};
@@ -1781,11 +1776,6 @@ struct TALER_EXCHANGEDB_ReserveHistory
struct TALER_EXCHANGEDB_BankTransfer *bank;
/**
- * Details about a pre26 /batch-withdraw operation.
- */
- struct TALER_EXCHANGEDB_CollectableBlindcoin *batch_withdraw;
-
- /**
* Details about a /withdraw operation.
*/
struct TALER_EXCHANGEDB_Withdraw *withdraw;
@@ -4360,92 +4350,6 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_DenominationHashP *denom_pub_hash,
const union TALER_EXCHANGEDB_NonceLockTargetP *target);
-
- /**
- * Locate the response for a batch withdraw request under a hash that uniquely
- * identifies the withdraw operation. Used to ensure idempotency of the
- * request.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param bch hash that uniquely identifies the withdraw operation
- * @param[out] collectable corresponding collectable coin (blind signature)
- * if a coin is found
- * @return statement execution status
- */
- enum GNUNET_DB_QueryStatus
- (*get_batch_withdraw_info)(void *cls,
- const struct TALER_BlindedCoinHashP *bch,
- struct TALER_EXCHANGEDB_CollectableBlindcoin *
- collectable);
-
-
- /**
- * FIXME: merge do_batch_withdraw and do_batch_withdraw_insert into one API,
- * which takes as input (among others)
- * - denom_serial[]
- * - blinded_coin_evs[]
- * - denom_sigs[]
- * The implementation should persist the data as _arrays_ in the DB.
- */
-
- /**
- * Perform reserve update as part of a batch withdraw operation, checking
- * for sufficient balance. Persisting the withdrawal details is done
- * separately!
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param now current time (rounded)
- * @param reserve_pub public key of the reserve to debit
- * @param amount total amount to withdraw
- * @param do_age_check if set, the batch-withdrawal can only succeed when the reserve has no age restriction (birthday) set.
- * @param[out] found set to true if the reserve was found
- * @param[out] balance_ok set to true if the balance was sufficient
- * @param[out] reserve_balance set to original balance of the reserve
- * @param[out] age_ok set to true if no age requirements were defined on the reserve or @e do_age_check was false
- * @param[out] allowed_maximum_age when @e age_ok is false, set to the allowed maximum age for withdrawal from the reserve. The client MUST then use the age-withdraw endpoint
- * @param[out] ruuid set to the reserve's UUID (reserves table row)
- * @return query execution status
- */
- enum GNUNET_DB_QueryStatus
- (*do_batch_withdraw)(
- void *cls,
- struct GNUNET_TIME_Timestamp now,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_Amount *amount,
- bool do_age_check,
- bool *found,
- bool *balance_ok,
- struct TALER_Amount *reserve_balance,
- bool *age_ok,
- uint16_t *allowed_maximum_age,
- uint64_t *ruuid);
-
-
- /**
- * Perform insert as part of a batch withdraw operation, and persisting the
- * withdrawal details.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param nonce client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
- * @param collectable corresponding collectable coin (blind signature)
- * @param now current time (rounded)
- * @param ruuid reserve UUID
- * @param[out] denom_unknown set if the denomination is unknown in the DB
- * @param[out] conflict if the envelope was already in the DB
- * @param[out] nonce_reuse if @a nonce was non-NULL and reused
- * @return query execution status
- */
- enum GNUNET_DB_QueryStatus
- (*do_batch_withdraw_insert)(
- void *cls,
- const union GNUNET_CRYPTO_BlindSessionNonce *nonce,
- const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
- struct GNUNET_TIME_Timestamp now,
- uint64_t ruuid,
- bool *denom_unknown,
- bool *conflict,
- bool *nonce_reuse);
-
/**
* Locate the response for a withdraw request under a hash of the
* commitment that uniquely identifies the withdraw operation.