exchange

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

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:
Msrc/exchange/taler-exchange-httpd_reserves_history.c | 28----------------------------
Dsrc/exchangedb/0002-age_withdraw.sql | 157-------------------------------------------------------------------------------
Msrc/exchangedb/0002-recoup.sql | 41+++++++++++++++++++++--------------------
Msrc/exchangedb/0002-recoup_refresh.sql | 26++++++++++++--------------
Asrc/exchangedb/0002-refresh.sql | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/exchangedb/0002-refresh_commitments.sql | 166-------------------------------------------------------------------------------
Dsrc/exchangedb/0002-refresh_revealed_coins.sql | 169-------------------------------------------------------------------------------
Dsrc/exchangedb/0002-refresh_transfer_keys.sql | 127-------------------------------------------------------------------------------
Asrc/exchangedb/0002-unique_refresh_blinding_seed.sql | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchangedb/0002-unique_withdraw_blinding_seed.sql | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchangedb/0002-withdraw.sql | 241+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/exchangedb/0009-age_withdraw.sql | 43-------------------------------------------
Dsrc/exchangedb/0009-recoup.sql | 295-------------------------------------------------------------------------------
Dsrc/exchangedb/0009-recoup_refresh.sql | 225-------------------------------------------------------------------------------
Dsrc/exchangedb/0009-refresh.sql | 246-------------------------------------------------------------------------------
Dsrc/exchangedb/0009-unique_refresh_blinding_seed.sql | 72------------------------------------------------------------------------
Dsrc/exchangedb/0009-unique_withdraw_blinding_seed.sql | 72------------------------------------------------------------------------
Dsrc/exchangedb/0009-withdraw.sql | 241-------------------------------------------------------------------------------
Msrc/exchangedb/exchange-0002.sql.in | 8++++----
Msrc/exchangedb/exchange-0009.sql.in | 7-------
Msrc/exchangedb/perf_deposits_get_ready.c | 44+++++++++++++++++++-------------------------
Msrc/exchangedb/pg_get_reserve_history.c | 65-----------------------------------------------------------------
Msrc/exchangedb/plugin_exchangedb_common.c | 9---------
Msrc/exchangedb/plugin_exchangedb_postgres.c | 8--------
Msrc/include/taler_exchangedb_plugin.h | 96-------------------------------------------------------------------------------
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.