diff options
Diffstat (limited to 'src/exchangedb')
42 files changed, 1301 insertions, 1729 deletions
diff --git a/src/exchangedb/0002-aggregation_tracking.sql b/src/exchangedb/0002-aggregation_tracking.sql index 7df495a41..d07960247 100644 --- a/src/exchangedb/0002-aggregation_tracking.sql +++ b/src/exchangedb/0002-aggregation_tracking.sql @@ -26,11 +26,11 @@ BEGIN PERFORM create_partitioned_table( 'CREATE TABLE %I' '(aggregation_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' - ',deposit_serial_id INT8 PRIMARY KEY' + ',batch_deposit_serial_id INT8 PRIMARY KEY' ',wtid_raw BYTEA NOT NULL' ') %s ;' ,table_name - ,'PARTITION BY HASH (deposit_serial_id)' + ,'PARTITION BY HASH (batch_deposit_serial_id)' ,partition_suffix ); PERFORM comment_partitioned_table( @@ -86,8 +86,9 @@ BEGIN EXECUTE FORMAT ( 'ALTER TABLE ' || table_name || ' ADD CONSTRAINT ' || table_name || '_foreign_deposit' - ' FOREIGN KEY (deposit_serial_id) ' - ' REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE' -- FIXME change to coin_pub + deposit_serial_id for more efficient deposit??? + ' FOREIGN KEY (batch_deposit_serial_id)' + ' REFERENCES batch_deposits (batch_deposit_serial_id)' + ' ON DELETE CASCADE' ); END $$; diff --git a/src/exchangedb/0002-batch_deposits.sql b/src/exchangedb/0002-batch_deposits.sql new file mode 100644 index 000000000..af0764aa7 --- /dev/null +++ b/src/exchangedb/0002-batch_deposits.sql @@ -0,0 +1,154 @@ +-- +-- 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_batch_deposits( + IN partition_suffix TEXT DEFAULT NULL +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'batch_deposits'; +BEGIN + PERFORM create_partitioned_table( + 'CREATE TABLE %I' + '(batch_deposit_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY' + ',shard INT8 NOT NULL' + ',merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)' + ',wallet_timestamp INT8 NOT NULL' + ',exchange_timestamp INT8 NOT NULL' + ',refund_deadline INT8 NOT NULL' + ',wire_deadline INT8 NOT NULL' + ',h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)' + ',wallet_data_hash BYTEA CHECK (LENGTH(wallet_data_hash)=64) DEFAULT NULL' + ',wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)' + ',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)' + ',policy_details_serial_id INT8' + ',policy_blocked BOOLEAN NOT NULL DEFAULT FALSE' + ',done BOOLEAN NOT NULL DEFAULT FALSE' + ') %s ;' + ,table_name + ,'PARTITION BY HASH (shard)' + ,partition_suffix + ); + PERFORM comment_partitioned_table( + 'Information about the contracts for which we have received (batch) deposits.' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Used for load sharding in the materialized indices. Should be set based on merchant_pub. 64-bit value because we need an *unsigned* 32-bit value.' + ,'shard' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Unsalted hash of the target bank account; also used to lookup the KYC status' + ,'wire_target_h_payto' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'hash over data provided by the wallet upon payment to select a more specific contract' + ,'wallet_data_hash' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Salt used when hashing the payto://-URI to get the h_wire that was used by the coin deposit signatures; not used to calculate wire_target_h_payto (as that one is unsalted)' + ,'wire_salt' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Set to TRUE once we have included this (batch) deposit (and all associated coins) in some aggregate wire transfer to the merchant' + ,'done' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'True if the aggregation of the (batch) deposit is currently blocked by some policy extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.' + ,'policy_blocked' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'References policy extensions table, NULL if extensions are not used' + ,'policy_details_serial_id' + ,table_name + ,partition_suffix + ); +END +$$; + + +CREATE FUNCTION constrain_table_batch_deposits( + IN partition_suffix TEXT +) +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'batch_deposits'; +BEGIN + table_name = concat_ws('_', table_name, partition_suffix); + EXECUTE FORMAT ( + 'ALTER TABLE ' || table_name || + ' ADD CONSTRAINT ' || table_name || '_batch_deposit_serial_id_pkey' + ' PRIMARY KEY (batch_deposit_serial_id) ' + ',ADD CONSTRAINT ' || table_name || '_merchant_pub_h_contract_terms' + ' UNIQUE (shard, merchant_pub, h_contract_terms)' + ',ADD CONSTRAINT ' || table_name || '_foreign_policy_details' + ' FOREIGN KEY (policy_details_serial_id) ' + ' REFERENCES policy_details (policy_details_serial_id) ON DELETE RESTRICT' + ); + EXECUTE FORMAT ( + 'CREATE INDEX ' || table_name || '_by_ready ' + 'ON ' || table_name || ' ' + '(shard ASC' + ',wire_deadline ASC' + ') WHERE NOT (done OR policy_blocked);' + ); + EXECUTE FORMAT ( + 'CREATE INDEX ' || table_name || '_for_matching ' + 'ON ' || table_name || ' ' + '(shard ASC' + ',refund_deadline ASC' + ',wire_target_h_payto' + ') WHERE NOT (done OR policy_blocked);' + ); +END +$$; + + +INSERT INTO exchange_tables + (name + ,version + ,action + ,partitioned + ,by_range) + VALUES + ('batch_deposits' + ,'exchange-0002' + ,'create' + ,TRUE + ,FALSE), + ('batch_deposits' + ,'exchange-0002' + ,'constrain' + ,TRUE + ,FALSE) + ; diff --git a/src/exchangedb/0002-coin_deposits.sql b/src/exchangedb/0002-coin_deposits.sql new file mode 100644 index 000000000..c3eef6e5a --- /dev/null +++ b/src/exchangedb/0002-coin_deposits.sql @@ -0,0 +1,157 @@ +-- +-- 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_coin_deposits( + IN partition_suffix TEXT DEFAULT NULL +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'coin_deposits'; +BEGIN + PERFORM create_partitioned_table( + 'CREATE TABLE %I' + '(coin_deposit_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY' + ',batch_deposit_serial_id INT8 NOT NULL' + ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' + ',coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)' + ',amount_with_fee taler_amount NOT NULL' + ') %s ;' + ,table_name + ,'PARTITION BY HASH (coin_pub)' + ,partition_suffix + ); + PERFORM comment_partitioned_table( + 'Coins which have been deposited with the respective per-coin signatures.' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Link to information about the batch deposit this coin was used for' + ,'batch_deposit_serial_id' + ,table_name + ,partition_suffix + ); +END +$$; + + +CREATE FUNCTION constrain_table_coin_deposits( + IN partition_suffix TEXT +) +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'coin_deposits'; +BEGIN + table_name = concat_ws('_', table_name, partition_suffix); + EXECUTE FORMAT ( + 'ALTER TABLE ' || table_name || + ' ADD CONSTRAINT ' || table_name || '_coin_deposit_serial_id_pkey' + ' PRIMARY KEY (coin_deposit_serial_id) ' + ',ADD CONSTRAINT ' || table_name || '_unique_coin_sig' + ' UNIQUE (coin_pub, coin_sig)' + ); + EXECUTE FORMAT ( + 'CREATE INDEX ' || table_name || '_by_batch ' + 'ON ' || table_name || ' ' + '(batch_deposit_serial_id);' + ); +END +$$; + + +CREATE FUNCTION foreign_table_coin_deposits() +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'coin_deposits'; +BEGIN + EXECUTE FORMAT ( + 'ALTER TABLE ' || table_name || + ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub' + ' FOREIGN KEY (coin_pub) ' + ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE' + ',ADD CONSTRAINT ' || table_name || '_foreign_batch_deposits_id' + ' FOREIGN KEY (batch_deposit_serial_id) ' + ' REFERENCES batch_deposits (batch_deposit_serial_id) ON DELETE CASCADE' + ); +END +$$; + + +CREATE OR REPLACE FUNCTION coin_deposits_insert_trigger() + RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO exchange.coin_history + (coin_pub + ,table_name + ,serial_id) + VALUES + (NEW.coin_pub + ,'coin_deposits' + ,NEW.coin_deposit_serial_id); + RETURN NEW; +END $$; +COMMENT ON FUNCTION coin_deposits_insert_trigger() + IS 'Automatically generate coin history entry.'; + + +CREATE FUNCTION master_table_coin_deposits() +RETURNS VOID +LANGUAGE plpgsql +AS $$ +BEGIN + CREATE TRIGGER coin_deposits_on_insert + AFTER INSERT + ON coin_deposits + FOR EACH ROW EXECUTE FUNCTION coin_deposits_insert_trigger(); +END $$; + + +INSERT INTO exchange_tables + (name + ,version + ,action + ,partitioned + ,by_range) + VALUES + ('coin_deposits' + ,'exchange-0002' + ,'create' + ,TRUE + ,FALSE), + ('coin_deposits' + ,'exchange-0002' + ,'constrain' + ,TRUE + ,FALSE), + ('coin_deposits' + ,'exchange-0002' + ,'foreign' + ,TRUE + ,FALSE), + ('coin_deposits' + ,'exchange-0002' + ,'master' + ,TRUE + ,FALSE) + ; diff --git a/src/exchangedb/0002-coin_history.sql b/src/exchangedb/0002-coin_history.sql new file mode 100644 index 000000000..9b5efdcb6 --- /dev/null +++ b/src/exchangedb/0002-coin_history.sql @@ -0,0 +1,138 @@ +-- +-- 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_coin_history ( + IN partition_suffix TEXT DEFAULT NULL +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'coin_history'; +BEGIN + PERFORM create_partitioned_table( + 'CREATE TABLE %I' + '(coin_history_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY' + ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' + ',table_name TEXT NOT NULL' + ',serial_id INT8 NOT NULL' + ') %s ;' + ,table_name + ,'PARTITION BY HASH (coin_pub)' + ,partition_suffix + ); + PERFORM comment_partitioned_table( + 'Links to tables with entries that affected the transaction history of a coin.' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'For which coin is this a history entry' + ,'coin_pub' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'In which table is the history entry' + ,'table_name' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Which is the generated serial ID of the entry in the table' + ,'serial_id' + ,table_name + ,partition_suffix + ); + PERFORM comment_partitioned_column( + 'Monotonic counter, used to generate Etags for caching' + ,'coin_history_serial_id' + ,table_name + ,partition_suffix + ); +END +$$; + + +CREATE FUNCTION constrain_table_coin_history( + IN partition_suffix TEXT +) +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'coin_history'; +BEGIN + table_name = concat_ws('_', table_name, partition_suffix); + EXECUTE FORMAT ( + 'ALTER TABLE ' || table_name || + ' ADD CONSTRAINT ' || table_name || '_coin_history_serial_id_pkey' + ' PRIMARY KEY (coin_history_serial_id) ' + ',ADD CONSTRAINT ' || table_name || '_coin_entry_key' + ' UNIQUE (coin_pub, table_name, serial_id)' + ); + EXECUTE FORMAT ( + 'CREATE INDEX ' || table_name || '_coin_by_time' + ' ON ' || table_name || ' ' + '(coin_pub' + ',coin_history_serial_id DESC' + ');' + ); +END +$$; + + +CREATE FUNCTION foreign_table_coin_history() +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'coin_history'; +BEGIN + EXECUTE FORMAT ( + 'ALTER TABLE ' || table_name || + ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub' + ' FOREIGN KEY (coin_pub) ' + ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE' + ); +END +$$; + + + +INSERT INTO exchange_tables + (name + ,version + ,action + ,partitioned + ,by_range) + VALUES + ('coin_history' + ,'exchange-0002' + ,'create' + ,TRUE + ,FALSE), + ('coin_history' + ,'exchange-0002' + ,'constrain' + ,TRUE + ,FALSE), + ('coin_history' + ,'exchange-0002' + ,'foreign' + ,TRUE + ,FALSE) + ; diff --git a/src/exchangedb/0002-deposits.sql b/src/exchangedb/0002-deposits.sql deleted file mode 100644 index f15b869cc..000000000 --- a/src/exchangedb/0002-deposits.sql +++ /dev/null @@ -1,426 +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_deposits( - IN partition_suffix TEXT DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits'; -BEGIN - PERFORM create_partitioned_table( - 'CREATE TABLE %I' - '(deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' - ',shard INT8 NOT NULL' - ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' - ',known_coin_id INT8 NOT NULL' -- FIXME: column needed!? - ',amount_with_fee taler_amount NOT NULL' - ',wallet_timestamp INT8 NOT NULL' - ',exchange_timestamp INT8 NOT NULL' - ',refund_deadline INT8 NOT NULL' - ',wire_deadline INT8 NOT NULL' - ',merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)' - ',h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)' - ',wallet_data_hash BYTEA CHECK (LENGTH(wallet_data_hash)=64) DEFAULT NULL' - ',subcontract_id INT4 NOT NULL DEFAULT 0' - ',coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)' - ',wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)' - ',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)' - ',done BOOLEAN NOT NULL DEFAULT FALSE' - ',policy_blocked BOOLEAN NOT NULL DEFAULT FALSE' - ',policy_details_serial_id INT8' - ') %s ;' - ,table_name - ,'PARTITION BY HASH (coin_pub)' - ,partition_suffix - ); - PERFORM comment_partitioned_table( - 'Deposits we have received and for which we need to make (aggregate) wire transfers (and manage refunds).' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'Used for load sharding in the materialized indices. Should be set based on merchant_pub. 64-bit value because we need an *unsigned* 32-bit value.' - ,'shard' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'Used for garbage collection' - ,'known_coin_id' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'Identifies the target bank account and KYC status' - ,'wire_target_h_payto' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'hash over data provided by the wallet upon payment to select a more specific contract' - ,'wallet_data_hash' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'Salt used when hashing the payto://-URI to get the h_wire' - ,'wire_salt' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'Set to TRUE once we have included this deposit in some aggregate wire transfer to the merchant' - ,'done' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'True if the aggregation of the deposit is currently blocked by some policy extension mechanism. Used to filter out deposits that must not be processed by the canonical deposit logic.' - ,'policy_blocked' - ,table_name - ,partition_suffix - ); - PERFORM comment_partitioned_column( - 'References policy extensions table, NULL if extensions are not used' - ,'policy_details_serial_id' - ,table_name - ,partition_suffix - ); -END -$$; - - -CREATE FUNCTION constrain_table_deposits( - IN partition_suffix TEXT -) -RETURNS void -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits'; -BEGIN - table_name = concat_ws('_', table_name, partition_suffix); - EXECUTE FORMAT ( - 'ALTER TABLE ' || table_name || - ' ADD CONSTRAINT ' || table_name || '_deposit_serial_id_pkey' - ' PRIMARY KEY (deposit_serial_id) ' - ',ADD CONSTRAINT ' || table_name || '_coin_pub_merchant_pub_h_contract_terms_key' - ' UNIQUE (coin_pub, merchant_pub, h_contract_terms)' - ); - EXECUTE FORMAT ( - 'CREATE INDEX ' || table_name || '_by_ready ' - 'ON ' || table_name || ' ' - '(wire_deadline ASC' - ',shard ASC' - ',coin_pub' - ') WHERE NOT (done OR policy_blocked);' - ); - EXECUTE FORMAT ( - 'CREATE INDEX ' || table_name || '_for_matching ' - 'ON ' || table_name || ' ' - '(refund_deadline ASC' - ',merchant_pub' - ',coin_pub' - ') WHERE NOT (done OR policy_blocked);' - ); -END -$$; - - -CREATE FUNCTION foreign_table_deposits() -RETURNS void -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits'; -BEGIN - EXECUTE FORMAT ( - 'ALTER TABLE ' || table_name || - ' ADD CONSTRAINT ' || table_name || '_foreign_coin_pub' - ' FOREIGN KEY (coin_pub) ' - ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE' - ',ADD CONSTRAINT ' || table_name || '_foreign_coin_id' - ' FOREIGN KEY (known_coin_id) ' - ' REFERENCES known_coins (known_coin_id) ON DELETE CASCADE' - ',ADD CONSTRAINT ' || table_name || '_foreign_policy_details' - ' FOREIGN KEY (policy_details_serial_id) ' - ' REFERENCES policy_details (policy_details_serial_id) ON DELETE CASCADE' - ); -END -$$; - - -CREATE FUNCTION create_table_deposits_by_ready( - IN partition_suffix TEXT DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits_by_ready'; -BEGIN - PERFORM create_partitioned_table( - 'CREATE TABLE %I' - '(wire_deadline INT8 NOT NULL' - ',shard INT8 NOT NULL' - ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' - ',deposit_serial_id INT8' - ') %s ;' - ,table_name - ,'PARTITION BY RANGE (wire_deadline)' - ,partition_suffix - ); - PERFORM comment_partitioned_table( - 'Enables fast lookups for deposits_get_ready, auto-populated via TRIGGER below' - ,table_name - ,partition_suffix - ); -END -$$; - - -CREATE FUNCTION constrain_table_deposits_by_ready( - IN partition_suffix TEXT DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits_by_ready'; -BEGIN - table_name = concat_ws('_', table_name, partition_suffix); - EXECUTE FORMAT ( - 'CREATE INDEX ' || table_name || '_main_index ' - 'ON ' || table_name || ' ' - '(wire_deadline ASC, shard ASC, coin_pub);' - ); -END -$$; - - -CREATE FUNCTION create_table_deposits_for_matching( - IN partition_suffix TEXT DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits_for_matching'; -BEGIN - PERFORM create_partitioned_table( - 'CREATE TABLE %I' - '(refund_deadline INT8 NOT NULL' - ',merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)' - ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' -- REFERENCES known_coins (coin_pub) ON DELETE CASCADE - ',deposit_serial_id INT8' - ') %s ;' - ,table_name - ,'PARTITION BY RANGE (refund_deadline)' - ,partition_suffix - ); - PERFORM comment_partitioned_table( - 'Enables fast lookups for deposits_iterate_matching, auto-populated via TRIGGER below' - ,table_name - ,partition_suffix - ); -END -$$; - - -CREATE FUNCTION constrain_table_deposits_for_matching( - IN partition_suffix TEXT DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name TEXT DEFAULT 'deposits_for_matching'; -BEGIN - table_name = concat_ws('_', table_name, partition_suffix); - EXECUTE FORMAT ( - 'CREATE INDEX ' || table_name || '_main_index' - ' ON ' || table_name || ' ' - '(refund_deadline ASC, merchant_pub, coin_pub);' - ); -END -$$; - - -CREATE OR REPLACE FUNCTION deposits_insert_trigger() - RETURNS trigger - LANGUAGE plpgsql - AS $$ -DECLARE - is_ready BOOLEAN; -BEGIN - is_ready = NOT (NEW.done OR NEW.policy_blocked); - - IF (is_ready) - THEN - INSERT INTO exchange.deposits_by_ready - (wire_deadline - ,shard - ,coin_pub - ,deposit_serial_id) - VALUES - (NEW.wire_deadline - ,NEW.shard - ,NEW.coin_pub - ,NEW.deposit_serial_id); - INSERT INTO exchange.deposits_for_matching - (refund_deadline - ,merchant_pub - ,coin_pub - ,deposit_serial_id) - VALUES - (NEW.refund_deadline - ,NEW.merchant_pub - ,NEW.coin_pub - ,NEW.deposit_serial_id); - END IF; - RETURN NEW; -END $$; -COMMENT ON FUNCTION deposits_insert_trigger() - IS 'Replicate deposit inserts into materialized indices.'; - - -CREATE OR REPLACE FUNCTION deposits_update_trigger() - RETURNS trigger - LANGUAGE plpgsql - AS $$ -DECLARE - was_ready BOOLEAN; -DECLARE - is_ready BOOLEAN; -BEGIN - was_ready = NOT (OLD.done OR OLD.policy_blocked); - is_ready = NOT (NEW.done OR NEW.policy_blocked); - IF (was_ready AND NOT is_ready) - THEN - DELETE FROM exchange.deposits_by_ready - WHERE wire_deadline = OLD.wire_deadline - AND shard = OLD.shard - AND coin_pub = OLD.coin_pub - AND deposit_serial_id = OLD.deposit_serial_id; - DELETE FROM exchange.deposits_for_matching - WHERE refund_deadline = OLD.refund_deadline - AND merchant_pub = OLD.merchant_pub - AND coin_pub = OLD.coin_pub - AND deposit_serial_id = OLD.deposit_serial_id; - END IF; - IF (is_ready AND NOT was_ready) - THEN - INSERT INTO exchange.deposits_by_ready - (wire_deadline - ,shard - ,coin_pub - ,deposit_serial_id) - VALUES - (NEW.wire_deadline - ,NEW.shard - ,NEW.coin_pub - ,NEW.deposit_serial_id); - INSERT INTO exchange.deposits_for_matching - (refund_deadline - ,merchant_pub - ,coin_pub - ,deposit_serial_id) - VALUES - (NEW.refund_deadline - ,NEW.merchant_pub - ,NEW.coin_pub - ,NEW.deposit_serial_id); - END IF; - RETURN NEW; -END $$; -COMMENT ON FUNCTION deposits_update_trigger() - IS 'Replicate deposits changes into materialized indices.'; - - -CREATE OR REPLACE FUNCTION deposits_delete_trigger() - RETURNS trigger - LANGUAGE plpgsql - AS $$ -DECLARE - was_ready BOOLEAN; -BEGIN - was_ready = NOT (OLD.done OR OLD.policy_blocked); - - IF (was_ready) - THEN - DELETE FROM exchange.deposits_by_ready - WHERE wire_deadline = OLD.wire_deadline - AND shard = OLD.shard - AND coin_pub = OLD.coin_pub - AND deposit_serial_id = OLD.deposit_serial_id; - DELETE FROM exchange.deposits_for_matching - WHERE refund_deadline = OLD.refund_deadline - AND merchant_pub = OLD.merchant_pub - AND coin_pub = OLD.coin_pub - AND deposit_serial_id = OLD.deposit_serial_id; - END IF; - RETURN NEW; -END $$; -COMMENT ON FUNCTION deposits_delete_trigger() - IS 'Replicate deposit deletions into materialized indices.'; - - -CREATE FUNCTION master_table_deposits() -RETURNS VOID -LANGUAGE plpgsql -AS $$ -BEGIN - CREATE TRIGGER deposits_on_insert - AFTER INSERT - ON deposits - FOR EACH ROW EXECUTE FUNCTION deposits_insert_trigger(); - CREATE TRIGGER deposits_on_update - AFTER UPDATE - ON deposits - FOR EACH ROW EXECUTE FUNCTION deposits_update_trigger(); - CREATE TRIGGER deposits_on_delete - AFTER DELETE - ON deposits - FOR EACH ROW EXECUTE FUNCTION deposits_delete_trigger(); -END $$; - - -INSERT INTO exchange_tables - (name - ,version - ,action - ,partitioned - ,by_range) - VALUES - ('deposits' - ,'exchange-0002' - ,'create' - ,TRUE - ,FALSE), - ('deposits' - ,'exchange-0002' - ,'constrain' - ,TRUE - ,FALSE), - ('deposits' - ,'exchange-0002' - ,'foreign' - ,TRUE - ,FALSE) - ; diff --git a/src/exchangedb/0002-refunds.sql b/src/exchangedb/0002-refunds.sql index e82dad937..6eae5532b 100644 --- a/src/exchangedb/0002-refunds.sql +++ b/src/exchangedb/0002-refunds.sql @@ -27,7 +27,7 @@ BEGIN 'CREATE TABLE %I' '(refund_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' ',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)' - ',deposit_serial_id INT8 NOT NULL' + ',batch_deposit_serial_id INT8 NOT NULL' ',merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)' ',rtransaction_id INT8 NOT NULL' ',amount_with_fee taler_amount NOT NULL' @@ -43,7 +43,7 @@ BEGIN ); PERFORM comment_partitioned_column( 'Identifies ONLY the merchant_pub, h_contract_terms and coin_pub. Multiple deposits may match a refund, this only identifies one of them.' - ,'deposit_serial_id' + ,'batch_deposit_serial_id' ,table_name ,partition_suffix ); @@ -76,7 +76,7 @@ BEGIN 'ALTER TABLE ' || table_name || ' ADD CONSTRAINT ' || table_name || '_refund_serial_id_key' ' UNIQUE (refund_serial_id) ' - ',ADD PRIMARY KEY (deposit_serial_id, rtransaction_id) ' + ',ADD PRIMARY KEY (batch_deposit_serial_id, rtransaction_id) ' ); END $$; @@ -95,8 +95,8 @@ BEGIN ' FOREIGN KEY (coin_pub) ' ' REFERENCES known_coins (coin_pub) ON DELETE CASCADE' ',ADD CONSTRAINT ' || table_name || '_foreign_deposit' - ' FOREIGN KEY (deposit_serial_id) ' - ' REFERENCES deposits (deposit_serial_id) ON DELETE CASCADE' + ' FOREIGN KEY (batch_deposit_serial_id) ' + ' REFERENCES batch_deposits (batch_deposit_serial_id) ON DELETE CASCADE' ); END $$; diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index cbe06ce0b..4ffc574cf 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -74,7 +74,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_compute_shard.h pg_compute_shard.c \ plugin_exchangedb_postgres.c pg_helper.h \ pg_reserves_update.h pg_reserves_update.c \ - pg_insert_aggregation_tracking.h pg_insert_aggregation_tracking.c \ pg_select_aggregation_amounts_for_kyc_check.h pg_select_aggregation_amounts_for_kyc_check.c \ pg_lookup_wire_fee_by_time.h pg_lookup_wire_fee_by_time.c \ pg_select_satisfied_kyc_processes.h pg_select_satisfied_kyc_processes.c \ @@ -144,7 +143,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_find_aggregation_transient.h pg_find_aggregation_transient.c \ pg_update_aggregation_transient.h pg_update_aggregation_transient.c \ pg_get_ready_deposit.h pg_get_ready_deposit.c \ - pg_insert_deposit.h pg_insert_deposit.c \ pg_insert_refund.h pg_insert_refund.c \ pg_select_refunds_by_coin.h pg_select_refunds_by_coin.c \ pg_get_melt.h pg_get_melt.c \ @@ -165,7 +163,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_start_deferred_wire_out.h pg_start_deferred_wire_out.c \ pg_store_wire_transfer_out.h pg_store_wire_transfer_out.c \ pg_gc.h pg_gc.c \ - pg_select_deposits_above_serial_id.h pg_select_deposits_above_serial_id.c \ + pg_select_coin_deposits_above_serial_id.h pg_select_coin_deposits_above_serial_id.c \ pg_select_history_requests_above_serial_id.h pg_select_history_requests_above_serial_id.c \ pg_select_purse_decisions_above_serial_id.h pg_select_purse_decisions_above_serial_id.c \ pg_select_purse_deposits_by_purse.h pg_select_purse_deposits_by_purse.c \ @@ -182,7 +180,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_get_old_coin_by_h_blind.h pg_get_old_coin_by_h_blind.c \ pg_insert_denomination_revocation.h pg_insert_denomination_revocation.c \ pg_get_denomination_revocation.h pg_get_denomination_revocation.c \ - pg_select_deposits_missing_wire.h pg_select_deposits_missing_wire.c \ + pg_select_batch_deposits_missing_wire.h pg_select_batch_deposits_missing_wire.c \ pg_lookup_auditor_timestamp.h pg_lookup_auditor_timestamp.c \ pg_lookup_auditor_status.h pg_lookup_auditor_status.c \ pg_insert_auditor.h pg_insert_auditor.c \ diff --git a/src/exchangedb/exchange-0002.sql.in b/src/exchangedb/exchange-0002.sql.in index 9dd62a3d1..9a810aa2e 100644 --- a/src/exchangedb/exchange-0002.sql.in +++ b/src/exchangedb/exchange-0002.sql.in @@ -58,10 +58,12 @@ COMMENT ON TYPE exchange_do_array_reserve_insert_return_type #include "0002-reserves_open_requests.sql" #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-deposits.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" diff --git a/src/exchangedb/exchange_do_deposit.sql b/src/exchangedb/exchange_do_deposit.sql index 5a5e2c6f6..f2828cb55 100644 --- a/src/exchangedb/exchange_do_deposit.sql +++ b/src/exchangedb/exchange_do_deposit.sql @@ -1,6 +1,6 @@ -- -- This file is part of TALER --- Copyright (C) 2014--2022 Taler Systems SA +-- 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 @@ -14,156 +14,194 @@ -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -- CREATE OR REPLACE FUNCTION exchange_do_deposit( - IN in_amount_with_fee taler_amount, - IN in_h_contract_terms BYTEA, - IN in_wire_salt BYTEA, + -- For batch_deposits + IN in_shard INT8, + IN in_merchant_pub BYTEA, IN in_wallet_timestamp INT8, IN in_exchange_timestamp INT8, IN in_refund_deadline INT8, IN in_wire_deadline INT8, - IN in_merchant_pub BYTEA, - IN in_receiver_wire_account TEXT, - IN in_h_payto BYTEA, - IN in_known_coin_id INT8, - IN in_coin_pub BYTEA, - IN in_coin_sig BYTEA, - IN in_shard INT8, + IN in_h_contract_terms BYTEA, + IN in_wallet_data_hash BYTEA, -- can be NULL + IN in_wire_salt BYTEA, + IN in_wire_target_h_payto BYTEA, + IN in_policy_details_serial_id INT8, -- can be NULL IN in_policy_blocked BOOLEAN, - IN in_policy_details_serial_id INT8, + -- For wire_targets + IN in_receiver_wire_account TEXT, + -- For coin_deposits + IN ina_coin_pub BYTEA[], + IN ina_coin_sig BYTEA[], + IN ina_amount_with_fee taler_amount[], OUT out_exchange_timestamp INT8, - OUT out_balance_ok BOOLEAN, - OUT out_conflict BOOLEAN) + OUT out_insufficient_balance_coin_index INT4, -- index of coin with bad balance, NULL if none + OUT out_conflict BOOL + ) LANGUAGE plpgsql AS $$ DECLARE wtsi INT8; -- wire target serial id + bdsi INT8; -- batch_deposits serial id + curs REFCURSOR; + i INT4; + ini_amount_with_fee taler_amount; + ini_coin_pub BYTEA; + ini_coin_sig BYTEA; BEGIN --- Shards: INSERT policy_details (by policy_details_serial_id) --- INSERT wire_targets (by h_payto), on CONFLICT DO NOTHING; --- INSERT deposits (by coin_pub, shard), ON CONFLICT DO NOTHING; --- UPDATE known_coins (by coin_pub) +-- Shards: +-- INSERT wire_targets (by h_payto), ON CONFLICT DO NOTHING; +-- INSERT batch_deposits (by shard, merchant_pub), ON CONFLICT idempotency check; +-- INSERT[] coin_deposits (by coin_pub), ON CONFLICT idempotency check; +-- UPDATE[] known_coins (by coin_pub) + -INSERT INTO exchange.wire_targets - (wire_target_h_payto - ,payto_uri) +-- First, get or create the 'wtsi' +INSERT INTO wire_targets + (wire_target_h_payto + ,payto_uri) VALUES - (in_h_payto - ,in_receiver_wire_account) -ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto) - RETURNING wire_target_serial_id INTO wtsi; + (in_wire_target_h_payto + ,in_receiver_wire_account) + ON CONFLICT DO NOTHING -- for CONFLICT ON (wire_target_h_payto) + RETURNING + wire_target_serial_id + INTO + wtsi; IF NOT FOUND THEN - SELECT wire_target_serial_id - INTO wtsi - FROM exchange.wire_targets - WHERE wire_target_h_payto=in_h_payto; + SELECT + wire_target_serial_id + INTO + wtsi + FROM wire_targets + WHERE + wire_target_h_payto=in_wire_target_h_payto; END IF; -INSERT INTO exchange.deposits +-- Second, create the batch_deposits entry +INSERT INTO batch_deposits (shard - ,coin_pub - ,known_coin_id - ,amount_with_fee + ,merchant_pub ,wallet_timestamp ,exchange_timestamp ,refund_deadline ,wire_deadline - ,merchant_pub ,h_contract_terms - ,coin_sig + ,wallet_data_hash ,wire_salt ,wire_target_h_payto - ,policy_blocked ,policy_details_serial_id + ,policy_blocked ) VALUES (in_shard - ,in_coin_pub - ,in_known_coin_id - ,in_amount_with_fee + ,in_merchant_pub ,in_wallet_timestamp ,in_exchange_timestamp ,in_refund_deadline ,in_wire_deadline - ,in_merchant_pub ,in_h_contract_terms - ,in_coin_sig + ,in_wallet_data_hash ,in_wire_salt - ,in_h_payto - ,in_policy_blocked - ,in_policy_details_serial_id) - ON CONFLICT DO NOTHING; + ,in_wire_target_h_payto + ,in_policy_details_serial_id + ,in_policy_blocked) + ON CONFLICT DO NOTHING -- for CONFLICT ON (merchant_pub, h_contract_terms) + RETURNING + batch_deposit_serial_id + INTO + bdsi; IF NOT FOUND THEN -- Idempotency check: see if an identical record exists. - -- Note that by checking 'coin_sig', we implicitly check - -- identity over everything that the signature covers. - -- We do select over merchant_pub and wire_target_h_payto - -- primarily here to maximally use the existing index. + -- We do select over merchant_pub, h_contract_terms and wire_target_h_payto + -- first to maximally increase the chance of using the existing index. SELECT - exchange_timestamp + exchange_timestamp + ,batch_deposit_serial_id INTO - out_exchange_timestamp - FROM exchange.deposits + out_exchange_timestamp + ,bdsi + FROM batch_deposits WHERE shard=in_shard AND merchant_pub=in_merchant_pub - AND wire_target_h_payto=in_h_payto - AND coin_pub=in_coin_pub - AND coin_sig=in_coin_sig; - -- AND policy_details_serial_id=in_policy_details_serial_id; -- FIXME: is this required for idempotency? - + AND h_contract_terms=in_h_contract_terms + AND wire_target_h_payto=in_wire_target_h_payto + -- now check the rest, too + AND ( (wallet_data_hash=in_wallet_data_hash) OR + (wallet_data_hash IS NULL AND in_wallet_data_hash IS NULL) ) + AND wire_salt=in_wire_salt + AND wallet_timestamp=in_wallet_timestamp + AND refund_deadline=in_refund_deadline + AND wire_deadline=in_wire_deadline + AND ( (policy_details_serial_id=in_policy_details_serial_id) OR + (policy_details_serial_id IS NULL AND in_policy_details_serial_id IS NULL) ); IF NOT FOUND THEN - -- Deposit exists, but with differences. Not allowed. - out_balance_ok=FALSE; + -- Deposit exists, but with *strange* differences. Not allowed. out_conflict=TRUE; - out_exchange_timestamp=0; RETURN; END IF; +END IF; - -- Idempotent request known, return success. - out_balance_ok=TRUE; - out_conflict=FALSE; +out_conflict=FALSE; - RETURN; -END IF; +-- Deposit each coin +FOR i IN 1..array_length(ina_coin_pub,1) +LOOP + ini_coin_pub = ina_coin_pub[i]; + ini_coin_sig = ina_coin_sig[i]; + ini_amount_with_fee = ina_amount_with_fee[i]; -out_exchange_timestamp=in_exchange_timestamp; + INSERT INTO coin_deposits + (batch_deposit_serial_id + ,coin_pub + ,coin_sig + ,amount_with_fee + ) + VALUES + (bdsi + ,ini_coin_pub + ,ini_coin_sig + ,ini_amount_with_fee + ) + ON CONFLICT DO NOTHING; --- Check and update balance of the coin. -UPDATE known_coins kc - SET - remaining.frac=(kc.remaining).frac-in_amount_with_fee.frac - + CASE - WHEN (kc.remaining).frac < in_amount_with_fee.frac - THEN 100000000 - ELSE 0 - END, - remaining.val=(kc.remaining).val-in_amount_with_fee.val - - CASE - WHEN (kc.remaining).frac < in_amount_with_fee.frac - THEN 1 - ELSE 0 - END - WHERE coin_pub=in_coin_pub - AND ( ((kc.remaining).val > in_amount_with_fee.val) OR - ( ((kc.remaining).frac >= in_amount_with_fee.frac) AND - ((kc.remaining).val >= in_amount_with_fee.val) ) ); + IF FOUND + THEN + -- Insert did happen, update balance in known_coins! -IF NOT FOUND -THEN - -- Insufficient balance. - out_balance_ok=FALSE; - out_conflict=FALSE; - RETURN; -END IF; + UPDATE known_coins kc + SET + remaining.frac=(kc.remaining).frac-ini_amount_with_fee.frac + + CASE + WHEN (kc.remaining).frac < ini_amount_with_fee.frac + THEN 100000000 + ELSE 0 + END, + remaining.val=(kc.remaining).val-ini_amount_with_fee.val + - CASE + WHEN (kc.remaining).frac < ini_amount_with_fee.frac + THEN 1 + ELSE 0 + END + WHERE coin_pub=ini_coin_pub + AND ( ((kc.remaining).val > ini_amount_with_fee.val) OR + ( ((kc.remaining).frac >= ini_amount_with_fee.frac) AND + ((kc.remaining).val >= ini_amount_with_fee.val) ) ); --- Everything fine, return success! -out_balance_ok=TRUE; -out_conflict=FALSE; + IF NOT FOUND + THEN + -- Insufficient balance. + -- Note: C arrays are 0 indexed, but i started at 1 + out_insufficient_balance_coin_index=i-i; + RETURN; + END IF; + END IF; +END LOOP; -- end FOR all coins END $$; diff --git a/src/exchangedb/exchange_do_gc.sql b/src/exchangedb/exchange_do_gc.sql index 75dd120d7..5758cb2d2 100644 --- a/src/exchangedb/exchange_do_gc.sql +++ b/src/exchangedb/exchange_do_gc.sql @@ -23,7 +23,7 @@ DECLARE reserve_uuid_min INT8; -- minimum reserve UUID still alive melt_min INT8; -- minimum melt still alive coin_min INT8; -- minimum known_coin still alive - deposit_min INT8; -- minimum deposit still alive + batch_deposit_min INT8; -- minimum deposit still alive reserve_out_min INT8; -- minimum reserve_out still alive denom_min INT8; -- minimum denomination still alive BEGIN @@ -104,22 +104,25 @@ SELECT ORDER BY known_coin_id ASC LIMIT 1; -DELETE FROM exchange.deposits - WHERE known_coin_id < coin_min; +DELETE FROM exchange.batch_deposits + WHERE wire_deadline < in_ancient_date; SELECT - deposit_serial_id + batch_deposit_serial_id INTO - deposit_min - FROM exchange.deposits - ORDER BY deposit_serial_id ASC + batch_deposit_min + FROM exchange.coin_deposits + ORDER BY batch_deposit_serial_id ASC LIMIT 1; DELETE FROM exchange.refunds - WHERE deposit_serial_id < deposit_min; - + WHERE batch_deposit_serial_id < batch_deposit_min; DELETE FROM exchange.aggregation_tracking - WHERE deposit_serial_id < deposit_min; + WHERE batch_deposit_serial_id < batch_deposit_min; +DELETE FROM exchange.coin_deposits + WHERE batch_deposit_serial_id < batch_deposit_min; + + SELECT denominations_serial @@ -133,6 +136,3 @@ DELETE FROM exchange.cs_nonce_locks WHERE max_denomination_serial <= denom_min; END $$; - - - diff --git a/src/exchangedb/exchange_do_get_ready_deposit.sql b/src/exchangedb/exchange_do_get_ready_deposit.sql deleted file mode 100644 index 7b10c2055..000000000 --- a/src/exchangedb/exchange_do_get_ready_deposit.sql +++ /dev/null @@ -1,69 +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 OR REPLACE FUNCTION exchange_do_get_ready_deposit( - IN in_now INT8, - IN in_start_shard_now INT8, - IN in_end_shard_now INT8, - OUT out_payto_uri TEXT, - OUT out_merchant_pub BYTEA -) -LANGUAGE plpgsql -AS $$ -DECLARE - var_wire_target_h_payto BYTEA; -DECLARE - var_coin_pub BYTEA; -DECLARE - var_deposit_serial_id INT8; -DECLARE - curs CURSOR - FOR - SELECT - coin_pub - ,deposit_serial_id - ,wire_deadline - ,shard - FROM deposits_by_ready - WHERE wire_deadline <= in_now - AND shard >=in_start_shard_now - AND shard <=in_end_shard_now - LIMIT 1; -DECLARE - i RECORD; -BEGIN -OPEN curs; -FETCH FROM curs INTO i; -IF NOT FOUND -THEN - RETURN; -END IF; -SELECT - payto_uri - ,merchant_pub - INTO - out_payto_uri - ,out_merchant_pub - FROM deposits dep - JOIN wire_targets wt - ON (wt.wire_target_h_payto=dep.wire_target_h_payto) - WHERE dep.coin_pub=i.coin_pub - AND dep.deposit_serial_id=i.deposit_serial_id - ORDER BY - i.wire_deadline ASC - ,i.shard ASC; - -RETURN; -END $$; diff --git a/src/exchangedb/exchange_do_refund.sql b/src/exchangedb/exchange_do_refund.sql index 7cb50e9fd..a95746127 100644 --- a/src/exchangedb/exchange_do_refund.sql +++ b/src/exchangedb/exchange_do_refund.sql @@ -1,6 +1,6 @@ -- -- This file is part of TALER --- Copyright (C) 2014--2022 Taler Systems SA +-- 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 @@ -32,7 +32,7 @@ CREATE OR REPLACE FUNCTION exchange_do_refund( LANGUAGE plpgsql AS $$ DECLARE - dsi INT8; -- ID of deposit being refunded + bdsi INT8; -- ID of deposit being refunded DECLARE tmp_val INT8; -- total amount refunded DECLARE @@ -48,17 +48,19 @@ BEGIN -- UPDATE known_coins (by coin_pub) SELECT - deposit_serial_id - ,(dep.amount_with_fee).val - ,(dep.amount_with_fee).frac - ,done -INTO - dsi + bdep.batch_deposit_serial_id + ,(cdep.amount_with_fee).val + ,(cdep.amount_with_fee).frac + ,bdep.done + INTO + bdsi ,deposit.val ,deposit.frac ,out_gone -FROM exchange.deposits dep - WHERE coin_pub=in_coin_pub + FROM batch_deposits bdep + JOIN coin_deposits cdep + USING (batch_deposit_serial_id) + WHERE cdep.coin_pub=in_coin_pub AND shard=in_deposit_shard AND merchant_pub=in_merchant_pub AND h_contract_terms=in_h_contract_terms; @@ -73,15 +75,15 @@ THEN RETURN; END IF; -INSERT INTO exchange.refunds - (deposit_serial_id +INSERT INTO refunds + (batch_deposit_serial_id ,coin_pub ,merchant_sig ,rtransaction_id ,amount_with_fee ) VALUES - (dsi + (bdsi ,in_coin_pub ,in_merchant_sig ,in_rtransaction_id @@ -99,7 +101,7 @@ THEN PERFORM FROM exchange.refunds WHERE coin_pub=in_coin_pub - AND deposit_serial_id=dsi + AND batch_deposit_serial_id=bdsi AND rtransaction_id=in_rtransaction_id AND amount_with_fee=in_amount_with_fee; @@ -131,14 +133,14 @@ END IF; -- Check refund balance invariant. SELECT - SUM((refunds.amount_with_fee).val) -- overflow here is not plausible - ,SUM(CAST((refunds.amount_with_fee).frac AS INT8)) -- compute using 64 bits + SUM((refs.amount_with_fee).val) -- overflow here is not plausible + ,SUM(CAST((refs.amount_with_fee).frac AS INT8)) -- compute using 64 bits INTO tmp_val ,tmp_frac - FROM exchange.refunds refunds + FROM refunds refs WHERE coin_pub=in_coin_pub - AND deposit_serial_id=dsi; + AND batch_deposit_serial_id=bdsi; IF tmp_val IS NULL THEN RAISE NOTICE 'failed to sum up existing refunds'; @@ -199,5 +201,5 @@ out_not_found=FALSE; END $$; --- COMMENT ON FUNCTION exchange_do_refund(taler_amount, BYTEA, BOOLEAN, BOOLEAN) --- IS 'Executes a refund operation, checking that the corresponding deposit was sufficient to cover the refunded amount'; +COMMENT ON FUNCTION exchange_do_refund(taler_amount, taler_amount, taler_amount, BYTEA, INT8, INT8, INT8, BYTEA, BYTEA, BYTEA) + IS 'Executes a refund operation, checking that the corresponding deposit was sufficient to cover the refunded amount'; diff --git a/src/exchangedb/exchange_do_refund_by_coin.sql b/src/exchangedb/exchange_do_refund_by_coin.sql deleted file mode 100644 index d5f99e6a7..000000000 --- a/src/exchangedb/exchange_do_refund_by_coin.sql +++ /dev/null @@ -1,86 +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 OR REPLACE FUNCTION exchange_do_refund_by_coin( - IN in_coin_pub BYTEA, - IN in_merchant_pub BYTEA, - IN in_h_contract BYTEA -) -RETURNS SETOF record -LANGUAGE plpgsql -AS $$ -DECLARE - curs CURSOR - FOR - SELECT - amount_with_fee - ,deposit_serial_id - FROM refunds - WHERE coin_pub=in_coin_pub; -DECLARE - i RECORD; -BEGIN -OPEN curs; -LOOP - FETCH NEXT FROM curs INTO i; - EXIT WHEN NOT FOUND; - RETURN QUERY - SELECT - i.amount_with_fee - FROM deposits - WHERE - coin_pub=in_coin_pub - AND merchant_pub=in_merchant_pub - AND h_contract_terms=in_h_contract - AND i.deposit_serial_id = deposit_serial_id; -END LOOP; -CLOSE curs; -END $$; - -/*RETURNS TABLE(amount_with_fee taler_amount) -LANGUAGE plpgsql -AS $$ -DECLARE - curs CURSOR - FOR - SELECT - r.amount_with_fee - ,r.deposit_serial_id - FROM refunds r - WHERE r.coin_pub=in_coin_pub; -DECLARE - i RECORD; -BEGIN -OPEN curs; -LOOP - FETCH NEXT FROM curs INTO i; - IF FOUND - THEN - RETURN QUERY - SELECT - i.amount_with_fee - FROM deposits - WHERE - merchant_pub=in_merchant_pub - AND h_contract_terms=in_h_contract - AND i.deposit_serial_id = deposit_serial_id; - END IF; - EXIT WHEN NOT FOUND; -END LOOP; -CLOSE curs; - -END $$; -*/ diff --git a/src/exchangedb/exchange_get_ready_deposit.sql b/src/exchangedb/exchange_get_ready_deposit.sql deleted file mode 100644 index 2186f561a..000000000 --- a/src/exchangedb/exchange_get_ready_deposit.sql +++ /dev/null @@ -1,60 +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 OR REPLACE FUNCTION exchange_do_get_ready_deposit( - IN in_now INT8, - IN in_start_shard_now INT8, - IN in_end_shard_now INT8, - OUT out_payto_uri TEXT, - OUT out_merchant_pub BYTEA -) -LANGUAGE plpgsql -AS $$ -DECLARE - curs CURSOR - FOR - SELECT - coin_pub - ,deposit_serial_id - ,wire_deadline - ,shard - FROM deposits_by_ready dbr - WHERE wire_deadline <= in_now - AND shard >= in_start_shard_now - AND shard <=in_end_shard_now - ORDER BY - wire_deadline ASC - ,shard ASC - LIMIT 1; -DECLARE - i RECORD; -BEGIN -OPEN curs; -FETCH FROM curs INTO i; -SELECT - payto_uri - ,merchant_pub - INTO - out_payto_uri - ,out_merchant_pub - FROM deposits - JOIN wire_targets wt - USING (wire_target_h_payto) - WHERE - i.coin_pub = coin_pub - AND i.deposit_serial_id=deposit_serial_id; -CLOSE curs; -RETURN; -END $$; diff --git a/src/exchangedb/perf_deposits_get_ready.c b/src/exchangedb/perf_deposits_get_ready.c index 89699da24..2effee732 100644 --- 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 - * @author Joseph Xu +git * @author Joseph Xu */ #include "platform.h" #include "taler_exchangedb_lib.h" @@ -196,8 +196,9 @@ run (void *cls) struct TALER_DenominationPublicKey *new_denom_pubs = NULL; struct GNUNET_TIME_Relative times = GNUNET_TIME_UNIT_ZERO; unsigned long long sqrs = 0; - struct TALER_EXCHANGEDB_Deposit *depos = NULL; - struct TALER_EXCHANGEDB_Refund *ref = NULL; + struct TALER_EXCHANGEDB_CoinDepositInformation *depos; + struct TALER_EXCHANGEDB_BatchDeposit bd; + struct TALER_EXCHANGEDB_Refund *ref; unsigned int *perm; unsigned long long duration_sq; struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; @@ -208,7 +209,7 @@ run (void *cls) ref = GNUNET_new_array (ROUNDS + 1, struct TALER_EXCHANGEDB_Refund); depos = GNUNET_new_array (ROUNDS + 1, - struct TALER_EXCHANGEDB_Deposit); + struct TALER_EXCHANGEDB_CoinDepositInformation); if (NULL == (plugin = TALER_EXCHANGEDB_plugin_load (cfg))) @@ -321,9 +322,9 @@ run (void *cls) struct TALER_CoinPubHashP c_hash; unsigned int k = (unsigned int) rand () % 5; unsigned int i = perm[j]; + if (i >= ROUNDS) i = ROUNDS; /* throw-away slot, do not keep around */ - depos[i].deposit_fee = fees.deposit; RND_BLK (&coin_pub); RND_BLK (&c_hash); RND_BLK (&reserve_pub); @@ -331,7 +332,7 @@ run (void *cls) TALER_denom_pub_hash (&new_dkp[k]->pub, &cbc.denom_pub_hash); deadline = GNUNET_TIME_timestamp_get (); - RND_BLK (&depos[i].coin.coin_pub); + depos[i].coin.coin_pub = coin_pub; TALER_denom_pub_hash (&new_dkp[k]->pub, &depos[i].coin.denom_pub_hash); GNUNET_assert (GNUNET_OK == @@ -341,19 +342,21 @@ run (void *cls) &c_hash, &alg_values, &new_dkp[k]->pub)); - RND_BLK (&depos[i].merchant_pub); + RND_BLK (&bd.merchant_pub); RND_BLK (&depos[i].csig); - RND_BLK (&depos[i].h_contract_terms); - RND_BLK (&depos[i].wire_salt); + RND_BLK (&bd.h_contract_terms); + RND_BLK (&bd.wire_salt); depos[i].amount_with_fee = value; - depos[i].refund_deadline = deadline; - depos[i].wire_deadline = deadline; - depos[i].receiver_wire_account = + bd.refund_deadline = deadline; + bd.wire_deadline = deadline; + bd.receiver_wire_account = "payto://iban/DE67830654080004822650?receiver-name=Test"; TALER_merchant_wire_signature_hash ( - "payto://iban/DE67830654080004822650?receiver-name=Test", - &depos[i].wire_salt, + bd.receiver_wire_account, + &bd.wire_salt, &h_wire_wt); + bd.num_cdis = 1; + bd.cdis = &depos[i]; cbc.reserve_pub = reserve_pub; cbc.amount_with_fee = value; GNUNET_assert (GNUNET_OK == @@ -401,12 +404,18 @@ run (void *cls) } { struct GNUNET_TIME_Timestamp now; + bool balance_ok; + uint32_t bad_idx; + bool ctr_conflict; now = GNUNET_TIME_timestamp_get (); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_deposit (plugin->cls, - now, - &depos[i])); + plugin->do_deposit (plugin->cls, + &bd, + &now, + &balance_ok, + &bad_idx, + &ctr_conflict)); } if (ROUNDS == i) TALER_denom_sig_free (&depos[i].coin.denom_sig); diff --git a/src/exchangedb/perf_select_refunds_by_coin.c b/src/exchangedb/perf_select_refunds_by_coin.c index 85c92f4b9..fdb44d2c9 100644 --- a/src/exchangedb/perf_select_refunds_by_coin.c +++ b/src/exchangedb/perf_select_refunds_by_coin.c @@ -211,11 +211,10 @@ run (void *cls) struct GNUNET_CONFIGURATION_Handle *cfg = cls; const uint32_t num_partitions = 10; struct GNUNET_TIME_Timestamp ts; - struct TALER_EXCHANGEDB_Deposit *depos = NULL; + struct TALER_EXCHANGEDB_CoinDepositInformation *depos = NULL; struct GNUNET_TIME_Timestamp deadline; struct TALER_Amount value; union TALER_DenominationBlindingKeyP bks; - struct TALER_CoinPubHashP c_hash; struct TALER_EXCHANGEDB_CollectableBlindcoin cbc; struct TALER_ExchangeWithdrawValues alg_values = { .cipher = TALER_DENOMINATION_RSA @@ -225,7 +224,6 @@ run (void *cls) struct TALER_EXCHANGEDB_Refund *ref = NULL; unsigned int *perm; unsigned long long duration_sq; - struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; struct TALER_DenominationPublicKey *new_denom_pubs = NULL; unsigned int count = 0; @@ -233,7 +231,7 @@ run (void *cls) ref = GNUNET_new_array (ROUNDS + 1, struct TALER_EXCHANGEDB_Refund); depos = GNUNET_new_array (ROUNDS + 1, - struct TALER_EXCHANGEDB_Deposit); + struct TALER_EXCHANGEDB_CoinDepositInformation); ZR_BLK (&cbc); if (NULL == @@ -344,55 +342,68 @@ run (void *cls) { unsigned int i = perm[j]; unsigned int k = (unsigned int) rand () % 5; + struct TALER_CoinPubHashP c_hash; + uint64_t known_coin_id; + struct TALER_EXCHANGEDB_CoinDepositInformation *cdi + = &depos[i]; + struct TALER_EXCHANGEDB_BatchDeposit bd = { + .cdis = cdi, + .num_cdis = 1, + .wallet_timestamp = ts, + .refund_deadline = deadline, + .wire_deadline = deadline, + .receiver_wire_account + = "payto://iban/DE67830654080004822650?receiver-name=Test" + }; + if (i >= ROUNDS) i = ROUNDS; /* throw-away slot, do not keep around */ - RND_BLK (&coin_pub); + RND_BLK (&bd.merchant_pub); + RND_BLK (&bd.h_contract_terms); + RND_BLK (&bd.wire_salt); + TALER_merchant_wire_signature_hash ( + bd.receiver_wire_account, + &bd.wire_salt, + &h_wire_wt); + RND_BLK (&cdi->coin.coin_pub); + RND_BLK (&cdi->csig); RND_BLK (&c_hash); - depos[i].deposit_fee = fees.deposit; - RND_BLK (&depos[i].coin.coin_pub); TALER_denom_pub_hash (&new_dkp[k]->pub, - &depos[i].coin.denom_pub_hash); + &cdi->coin.denom_pub_hash); GNUNET_assert (GNUNET_OK == - TALER_denom_sig_unblind (&depos[i].coin.denom_sig, + TALER_denom_sig_unblind (&cdi->coin.denom_sig, &cbc.sig, &bks, &c_hash, &alg_values, &new_dkp[k]->pub)); - RND_BLK (&depos[i].merchant_pub); - RND_BLK (&depos[i].csig); - RND_BLK (&depos[i].h_contract_terms); - RND_BLK (&depos[i].wire_salt); - depos[i].amount_with_fee = value; - depos[i].refund_deadline = deadline; - depos[i].wire_deadline = deadline; - depos[i].receiver_wire_account = - "payto://iban/DE67830654080004822650?receiver-name=Test"; - TALER_merchant_wire_signature_hash ( - "payto://iban/DE67830654080004822650?receiver-name=Test", - &depos[i].wire_salt, - &h_wire_wt); - depos[i].timestamp = ts; - uint64_t known_coin_id; + cdi->amount_with_fee = value; + { struct TALER_DenominationHashP dph; struct TALER_AgeCommitmentHash agh; FAILIF (TALER_EXCHANGEDB_CKS_ADDED != plugin->ensure_coin_known (plugin->cls, - &depos[i].coin, + &cdi->coin, &known_coin_id, &dph, &agh)); } { struct GNUNET_TIME_Timestamp now; + bool balance_ok; + uint32_t bad_idx; + bool in_conflict; now = GNUNET_TIME_timestamp_get (); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_deposit (plugin->cls, - now, - &depos[i])); + plugin->do_deposit (plugin->cls, + &bd, + &now, + &balance_ok, + &bad_idx, + &in_conflict)); } { bool not_found; @@ -404,9 +415,9 @@ run (void *cls) { case 2: // 100% refund ref[i].coin = depos[i].coin; - ref[i].details.merchant_pub = depos[i].merchant_pub; + ref[i].details.merchant_pub = bd.merchant_pub; RND_BLK (&ref[i].details.merchant_sig); - ref[i].details.h_contract_terms = depos[i].h_contract_terms; + ref[i].details.h_contract_terms = bd.h_contract_terms; ref[i].coin.coin_pub = depos[i].coin.coin_pub; ref[i].details.rtransaction_id = i; ref[i].details.refund_amount = value; @@ -425,9 +436,9 @@ run (void *cls) if (count < (NUM_ROWS / 10)) { ref[i].coin = depos[i].coin; - ref[i].details.merchant_pub = depos[i].merchant_pub; + ref[i].details.merchant_pub = bd.merchant_pub; RND_BLK (&ref[i].details.merchant_sig); - ref[i].details.h_contract_terms = depos[i].h_contract_terms; + ref[i].details.h_contract_terms = bd.h_contract_terms; ref[i].coin.coin_pub = depos[i].coin.coin_pub; ref[i].details.rtransaction_id = i; ref[i].details.refund_amount = value; diff --git a/src/exchangedb/pg_aggregate.c b/src/exchangedb/pg_aggregate.c index 82f731927..6f143e940 100644 --- a/src/exchangedb/pg_aggregate.c +++ b/src/exchangedb/pg_aggregate.c @@ -52,54 +52,61 @@ TEH_PG_aggregate ( pg->aggregator_shift); PREPARE (pg, "aggregate", - "WITH dep AS (" /* restrict to our merchant and account and mark as done */ - " UPDATE deposits" + "WITH bdep AS (" /* restrict to our merchant and account and mark as done */ + " UPDATE batch_deposits" " SET done=TRUE" " WHERE NOT (done OR policy_blocked)" /* only actually executable deposits */ - " AND refund_deadline<$1" /* filter by shard */ + " AND refund_deadline<$1" + /* FIXME: maybe more efficient to add shard here, too? */ " AND merchant_pub=$2" /* filter by target merchant */ " AND wire_target_h_payto=$3" /* merchant could have a 2nd bank account */ " RETURNING" - " deposit_serial_id" + " batch_deposit_serial_id)" + " ,cdep AS (" + " SELECT" + " coin_deposit_serial_id" + " ,batch_deposit_serial_id" " ,coin_pub" - " ,amount_with_fee AS amount)" + " ,amount_with_fee AS amount" + " FROM coin_deposits" + " WHERE batch_deposit_serial_id IN (SELECT batch_deposit_serial_id FROM bdep))" " ,ref AS (" /* find applicable refunds -- NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */ " SELECT" " amount_with_fee AS refund" " ,coin_pub" - " ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */ + " ,batch_deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */ " FROM refunds" - " WHERE coin_pub IN (SELECT coin_pub FROM dep)" - " AND deposit_serial_id IN (SELECT deposit_serial_id FROM dep))" + " WHERE coin_pub IN (SELECT coin_pub FROM cdep)" + " AND batch_deposit_serial_id IN (SELECT batch_deposit_serial_id FROM bdep))" " ,ref_by_coin AS (" /* total up refunds by coin */ " SELECT" " SUM((ref.refund).val) AS sum_refund_val" " ,SUM((ref.refund).frac) AS sum_refund_frac" " ,coin_pub" - " ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */ + " ,batch_deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */ " FROM ref" - " GROUP BY coin_pub, deposit_serial_id)" + " GROUP BY coin_pub, batch_deposit_serial_id)" " ,norm_ref_by_coin AS (" /* normalize */ " SELECT" " sum_refund_val + sum_refund_frac / 100000000 AS norm_refund_val" " ,sum_refund_frac % 100000000 AS norm_refund_frac" " ,coin_pub" - " ,deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */ + " ,batch_deposit_serial_id" /* theoretically, coin could be in multiple refunded transactions */ " FROM ref_by_coin)" " ,fully_refunded_coins AS (" /* find applicable refunds -- NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */ " SELECT" - " dep.coin_pub" + " cdep.coin_pub" " FROM norm_ref_by_coin norm" - " JOIN dep" - " ON (norm.coin_pub = dep.coin_pub" - " AND norm.deposit_serial_id = dep.deposit_serial_id" - " AND norm.norm_refund_val = (dep.amount).val" - " AND norm.norm_refund_frac = (dep.amount).frac))" + " JOIN cdep" + " ON (norm.coin_pub = cdep.coin_pub" + " AND norm.batch_deposit_serial_id = cdep.batch_deposit_serial_id" + " AND norm.norm_refund_val = (cdep.amount).val" + " AND norm.norm_refund_frac = (cdep.amount).frac))" " ,fees AS (" /* find deposit fees for not fully refunded deposits */ " SELECT" " denom.fee_deposit AS fee" - " ,cs.deposit_serial_id" /* ensures we get the fee for each coin, not once per denomination */ - " FROM dep cs" + " ,cs.batch_deposit_serial_id" /* ensures we get the fee for each coin, not once per denomination */ + " FROM cdep cs" " JOIN known_coins kc" /* NOTE: may do a full join on the master, maybe find a left-join way to integrate with query above to push it to the shards? */ " USING (coin_pub)" " JOIN denominations denom" @@ -107,18 +114,18 @@ TEH_PG_aggregate ( " WHERE coin_pub NOT IN (SELECT coin_pub FROM fully_refunded_coins))" " ,dummy AS (" /* add deposits to aggregation_tracking */ " INSERT INTO aggregation_tracking" - " (deposit_serial_id" + " (batch_deposit_serial_id" " ,wtid_raw)" - " SELECT deposit_serial_id,$4" - " FROM dep)" + " SELECT batch_deposit_serial_id,$4" + " FROM bdep)" "SELECT" /* calculate totals (deposits, refunds and fees) */ - " CAST(COALESCE(SUM((dep.amount).val),0) AS INT8) AS sum_deposit_value" /* cast needed, otherwise we get NUMBER */ - " ,COALESCE(SUM((dep.amount).frac),0) AS sum_deposit_fraction" /* SUM over INT returns INT8 */ + " CAST(COALESCE(SUM((cdep.amount).val),0) AS INT8) AS sum_deposit_value" /* cast needed, otherwise we get NUMBER */ + " ,COALESCE(SUM((cdep.amount).frac),0) AS sum_deposit_fraction" /* SUM over INT returns INT8 */ " ,CAST(COALESCE(SUM((ref.refund).val),0) AS INT8) AS sum_refund_value" " ,COALESCE(SUM((ref.refund).frac),0) AS sum_refund_fraction" " ,CAST(COALESCE(SUM((fees.fee).val),0) AS INT8) AS sum_fee_value" " ,COALESCE(SUM((fees.fee).frac),0) AS sum_fee_fraction" - " FROM dep " + " FROM cdep " " FULL OUTER JOIN ref ON (FALSE)" /* We just want all sums */ " FULL OUTER JOIN fees ON (FALSE);"); diff --git a/src/exchangedb/pg_do_deposit.c b/src/exchangedb/pg_do_deposit.c index ce09fb72c..11720d7d9 100644 --- a/src/exchangedb/pg_do_deposit.c +++ b/src/exchangedb/pg_do_deposit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -30,53 +30,78 @@ enum GNUNET_DB_QueryStatus TEH_PG_do_deposit ( void *cls, - const struct TALER_EXCHANGEDB_Deposit *deposit, - uint64_t known_coin_id, - const struct TALER_PaytoHashP *h_payto, - uint64_t *policy_details_serial_id, + const struct TALER_EXCHANGEDB_BatchDeposit *bd, struct GNUNET_TIME_Timestamp *exchange_timestamp, bool *balance_ok, - bool *in_conflict) + uint32_t *bad_balance_index, + bool *ctr_conflict) { struct PostgresClosure *pg = cls; - uint64_t deposit_shard = TEH_PG_compute_shard (&deposit->merchant_pub); + uint64_t deposit_shard = TEH_PG_compute_shard (&bd->merchant_pub); + const struct TALER_CoinSpendPublicKeyP *coin_pubs[GNUNET_NZL (bd->num_cdis)]; + const struct TALER_CoinSpendSignatureP *coin_sigs[GNUNET_NZL (bd->num_cdis)]; + struct TALER_Amount amounts_with_fee[GNUNET_NZL (bd->num_cdis)]; struct GNUNET_PQ_QueryParam params[] = { - TALER_PQ_query_param_amount (pg->conn, - &deposit->amount_with_fee), - GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms), - GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt), - GNUNET_PQ_query_param_timestamp (&deposit->timestamp), - GNUNET_PQ_query_param_timestamp (exchange_timestamp), - GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline), - GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline), - GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub), - GNUNET_PQ_query_param_string (deposit->receiver_wire_account), - GNUNET_PQ_query_param_auto_from_type (h_payto), - GNUNET_PQ_query_param_uint64 (&known_coin_id), - GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub), - GNUNET_PQ_query_param_auto_from_type (&deposit->csig), + /* data for batch_deposits */ GNUNET_PQ_query_param_uint64 (&deposit_shard), - GNUNET_PQ_query_param_bool (deposit->has_policy), - (NULL == policy_details_serial_id) + GNUNET_PQ_query_param_auto_from_type (&bd->merchant_pub), + GNUNET_PQ_query_param_timestamp (&bd->wallet_timestamp), + GNUNET_PQ_query_param_timestamp (exchange_timestamp), + GNUNET_PQ_query_param_timestamp (&bd->refund_deadline), + GNUNET_PQ_query_param_timestamp (&bd->wire_deadline), + GNUNET_PQ_query_param_auto_from_type (&bd->h_contract_terms), + (0 == bd->has_wallet_data_hash) ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_uint64 (policy_details_serial_id), + : GNUNET_PQ_query_param_auto_from_type (&bd->wallet_data_hash), + GNUNET_PQ_query_param_auto_from_type (&bd->wire_salt), + GNUNET_PQ_query_param_auto_from_type (&bd->wire_target_h_payto), + (0 == bd->policy_details_serial_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_uint64 (&bd->policy_details_serial_id), + GNUNET_PQ_query_param_bool (bd->policy_blocked), + /* to create entry in wire_targets */ + GNUNET_PQ_query_param_string (bd->receiver_wire_account), + /* arrays for coin_deposits */ + GNUNET_PQ_query_param_array_ptrs_auto_from_type (bd->num_cdis, + coin_pubs, + pg->conn), + GNUNET_PQ_query_param_array_ptrs_auto_from_type (bd->num_cdis, + coin_sigs, + pg->conn), + TALER_PQ_query_param_array_amount (bd->num_cdis, + amounts_with_fee, + pg->conn), GNUNET_PQ_query_param_end }; + bool no_time; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_bool ("balance_ok", - balance_ok), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_timestamp ("exchange_timestamp", + exchange_timestamp), + &no_time), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint32 ("insufficient_balance_coin_index", + bad_balance_index), + balance_ok), GNUNET_PQ_result_spec_bool ("conflicted", - in_conflict), - GNUNET_PQ_result_spec_timestamp ("exchange_timestamp", - exchange_timestamp), + ctr_conflict), GNUNET_PQ_result_spec_end }; + for (unsigned int i = 0; i < bd->num_cdis; i++) + { + const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi + = &bd->cdis[i]; + + amounts_with_fee[i] = cdi->amount_with_fee; + coin_pubs[i] = &cdi->coin.coin_pub; + coin_sigs[i] = &cdi->csig; + } PREPARE (pg, "call_deposit", "SELECT " " out_exchange_timestamp AS exchange_timestamp" - ",out_balance_ok AS balance_ok" + ",out_insufficient_balance_coin_index AS insufficient_balance_coin_index" ",out_conflict AS conflicted" " FROM exchange_do_deposit" " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);"); diff --git a/src/exchangedb/pg_do_deposit.h b/src/exchangedb/pg_do_deposit.h index e71cf0e4b..449ec04be 100644 --- a/src/exchangedb/pg_do_deposit.h +++ b/src/exchangedb/pg_do_deposit.h @@ -24,29 +24,28 @@ #include "taler_util.h" #include "taler_json_lib.h" #include "taler_exchangedb_plugin.h" + + /** * Perform deposit operation, checking for sufficient balance - * of the coin and possibly persisting the deposit details. + * of the coins and possibly persisting the deposit details. * * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param deposit deposit operation details - * @param known_coin_id row of the coin in the known_coins table - * @param h_payto hash of the merchant's bank account details - * @param policy_details_serial_id pointer to the ID of the entry in policy_details, maybe NULL + * @param bd batch deposit operation details * @param[in,out] exchange_timestamp time to use for the deposit (possibly updated) * @param[out] balance_ok set to true if the balance was sufficient + * @param[out] bad_balance_index set to the first index of a coin for which the balance was insufficient, + * only used if @a balance_ok is set to false. * @param[out] in_conflict set to true if the deposit conflicted * @return query execution status */ enum GNUNET_DB_QueryStatus TEH_PG_do_deposit ( void *cls, - const struct TALER_EXCHANGEDB_Deposit *deposit, - uint64_t known_coin_id, - const struct TALER_PaytoHashP *h_payto, - uint64_t *policy_details_serial_id, + const struct TALER_EXCHANGEDB_BatchDeposit *bd, struct GNUNET_TIME_Timestamp *exchange_timestamp, bool *balance_ok, + uint32_t *bad_balance_index, bool *in_conflict); #endif diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c index 807c4d8fc..c2f0f8222 100644 --- a/src/exchangedb/pg_get_coin_transactions.c +++ b/src/exchangedb/pg_get_coin_transactions.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -116,7 +116,7 @@ add_coin_deposit (void *cls, &deposit->receiver_wire_account), GNUNET_PQ_result_spec_auto_from_type ("coin_sig", &deposit->csig), - GNUNET_PQ_result_spec_uint64 ("deposit_serial_id", + GNUNET_PQ_result_spec_uint64 ("coin_deposit_serial_id", &serial_id), GNUNET_PQ_result_spec_auto_from_type ("done", &deposit->done), @@ -726,28 +726,30 @@ TEH_PG_get_coin_transactions ( PREPARE (pg, "get_deposit_with_coin_pub", "SELECT" - " dep.amount_with_fee" + " cdep.amount_with_fee" ",denoms.fee_deposit" ",denoms.denom_pub_hash" ",kc.age_commitment_hash" - ",dep.wallet_timestamp" - ",dep.refund_deadline" - ",dep.wire_deadline" - ",dep.merchant_pub" - ",dep.h_contract_terms" - ",dep.wire_salt" + ",bdep.wallet_timestamp" + ",bdep.refund_deadline" + ",bdep.wire_deadline" + ",bdep.merchant_pub" + ",bdep.h_contract_terms" + ",bdep.wire_salt" ",wt.payto_uri" - ",dep.coin_sig" - ",dep.deposit_serial_id" - ",dep.done" - " FROM deposits dep" + ",cdep.coin_sig" + ",cdep.coin_deposit_serial_id" + ",bdep.done" + " FROM coin_deposits cdep" + " JOIN batch_deposits bdep" + " USING (batch_deposit_serial_id)" " JOIN wire_targets wt" " USING (wire_target_h_payto)" " JOIN known_coins kc" - " ON (kc.coin_pub = dep.coin_pub)" + " ON (kc.coin_pub = cdep.coin_pub)" " JOIN denominations denoms" " USING (denominations_serial)" - " WHERE dep.coin_pub=$1;"); + " WHERE cdep.coin_pub=$1;"); PREPARE (pg, "get_refresh_session_by_coin", "SELECT" @@ -792,16 +794,18 @@ TEH_PG_get_coin_transactions ( PREPARE (pg, "get_refunds_by_coin", "SELECT" - " dep.merchant_pub" + " bdep.merchant_pub" ",ref.merchant_sig" - ",dep.h_contract_terms" + ",bdep.h_contract_terms" ",ref.rtransaction_id" ",ref.amount_with_fee" ",denom.fee_refund" ",ref.refund_serial_id" " FROM refunds ref" - " JOIN deposits dep" - " ON (ref.coin_pub = dep.coin_pub AND ref.deposit_serial_id = dep.deposit_serial_id)" + " JOIN coin_deposits cdep" + " ON (ref.coin_pub = cdep.coin_pub AND ref.batch_deposit_serial_id = cdep.batch_deposit_serial_id)" + " JOIN batch_deposits bdep" + " ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)" " JOIN known_coins kc" " ON (ref.coin_pub = kc.coin_pub)" " JOIN denominations denom" diff --git a/src/exchangedb/pg_get_ready_deposit.c b/src/exchangedb/pg_get_ready_deposit.c index 91151c617..d8344faf1 100644 --- a/src/exchangedb/pg_get_ready_deposit.c +++ b/src/exchangedb/pg_get_ready_deposit.c @@ -33,7 +33,6 @@ TEH_PG_get_ready_deposit (void *cls, struct TALER_MerchantPublicKeyP *merchant_pub, char **payto_uri) { - static int choose_mode = -2; struct PostgresClosure *pg = cls; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); @@ -50,76 +49,24 @@ TEH_PG_get_ready_deposit (void *cls, payto_uri), GNUNET_PQ_result_spec_end }; - const char *query; - - if (-2 == choose_mode) - { - const char *mode = getenv ("TALER_POSTGRES_GET_READY_LOGIC"); - char dummy; - - if ( (NULL==mode) || - (1 != sscanf (mode, - "%d%c", - &choose_mode, - &dummy)) ) - { - if (NULL != mode) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Bad mode `%s' specified\n", - mode); - choose_mode = 0; - } - } - switch (choose_mode) - { - case 0: - query = "deposits_get_ready-v5"; - PREPARE (pg, - query, - "SELECT" - " payto_uri" - ",merchant_pub" - " FROM deposits dep" - " JOIN wire_targets wt" - " USING (wire_target_h_payto)" - " WHERE NOT (done OR policy_blocked)" - " AND dep.wire_deadline<=$1" - " AND dep.shard >= $2" - " AND dep.shard <= $3" - " ORDER BY " - " dep.wire_deadline ASC" - " ,dep.shard ASC" - " LIMIT 1;"); - break; - case 1: - query = "deposits_get_ready-v6"; - PREPARE (pg, - query, - "WITH rc AS MATERIALIZED (" - " SELECT" - " merchant_pub" - ",wire_target_h_payto" - " FROM deposits" - " WHERE NOT (done OR policy_blocked)" - " AND wire_deadline<=$1" - " AND shard >= $2" - " AND shard <= $3" - " ORDER BY wire_deadline ASC" - " ,shard ASC" - " LIMIT 1" - ")" - "SELECT" - " wt.payto_uri" - ",rc.merchant_pub" - " FROM wire_targets wt" - " JOIN rc" - " USING (wire_target_h_payto);"); - break; - default: - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } + const char *query = "deposits_get_ready"; + PREPARE (pg, + query, + "SELECT" + " wts.payto_uri" + ",bdep.merchant_pub" + " FROM batch_deposits bdep" + " JOIN wire_targets wts" + " USING (wire_target_h_payto)" + " WHERE NOT (bdep.done OR bdep.policy_blocked)" + " AND bdep.wire_deadline<=$1" + " AND bdep.shard >= $2" + " AND bdep.shard <= $3" + " ORDER BY " + " bdep.wire_deadline ASC" + " ,bdep.shard ASC" + " LIMIT 1;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, query, params, diff --git a/src/exchangedb/pg_have_deposit2.c b/src/exchangedb/pg_have_deposit2.c index ccb4f5c91..e00ad7490 100644 --- a/src/exchangedb/pg_have_deposit2.c +++ b/src/exchangedb/pg_have_deposit2.c @@ -73,23 +73,25 @@ TEH_PG_have_deposit2 ( PREPARE (pg, "get_deposit", "SELECT" - " dep.amount_with_fee" + " cdep.amount_with_fee" ",denominations.fee_deposit" - ",dep.wallet_timestamp" - ",dep.exchange_timestamp" - ",dep.refund_deadline" - ",dep.wire_deadline" - ",dep.h_contract_terms" - ",dep.wire_salt" + ",bdep.wallet_timestamp" + ",bdep.exchange_timestamp" + ",bdep.refund_deadline" + ",bdep.wire_deadline" + ",bdep.h_contract_terms" + ",bdep.wire_salt" ",wt.payto_uri AS receiver_wire_account" - " FROM deposits dep" - " JOIN known_coins kc ON (kc.coin_pub = dep.coin_pub)" + " FROM coin_deposits cdep" + " JOIN batch_deposits bdep USING (batch_deposit_serial_id)" + " JOIN known_coins kc ON (kc.coin_pub = cdep.coin_pub)" " JOIN denominations USING (denominations_serial)" " JOIN wire_targets wt USING (wire_target_h_payto)" - " WHERE dep.coin_pub=$1" - " AND dep.merchant_pub=$3" - " AND dep.h_contract_terms=$2;"); - + " WHERE cdep.coin_pub=$1" + " AND bdep.merchant_pub=$3" + " AND bdep.h_contract_terms=$2;"); + /* Note: query might be made more efficient if we computed the 'shard' + from merchant_pub and included that as a constraint on bdep! */ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "get_deposit", params, diff --git a/src/exchangedb/pg_insert_aggregation_tracking.c b/src/exchangedb/pg_insert_aggregation_tracking.c deleted file mode 100644 index fe61b841d..000000000 --- a/src/exchangedb/pg_insert_aggregation_tracking.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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/> - */ -/** - * @file exchangedb/pg_insert_aggregation_tracking.c - * @brief Implementation of the insert_aggregation_tracking function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_error_codes.h" -#include "taler_dbevents.h" -#include "taler_pq_lib.h" -#include "pg_insert_aggregation_tracking.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TEH_PG_insert_aggregation_tracking ( - void *cls, - const struct TALER_WireTransferIdentifierRawP *wtid, - unsigned long long deposit_serial_id) -{ - struct PostgresClosure *pg = cls; - uint64_t rid = deposit_serial_id; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&rid), - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "insert_aggregation_tracking", - "INSERT INTO aggregation_tracking " - "(deposit_serial_id" - ",wtid_raw" - ") VALUES " - "($1, $2);"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_aggregation_tracking", - params); -} diff --git a/src/exchangedb/pg_insert_aggregation_tracking.h b/src/exchangedb/pg_insert_aggregation_tracking.h deleted file mode 100644 index e67c0e8e7..000000000 --- a/src/exchangedb/pg_insert_aggregation_tracking.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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/> - */ -/** - * @file exchangedb/pg_insert_aggregation_tracking.h - * @brief implementation of the insert_aggregation_tracking function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_INSERT_AGGREGATION_TRACKING_H -#define PG_INSERT_AGGREGATION_TRACKING_H - -#include "taler_util.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" - -/** - * Function called to insert aggregation information into the DB. - * - * @param cls closure - * @param wtid the raw wire transfer identifier we used - * @param deposit_serial_id row in the deposits table for which this is aggregation data - * @return transaction status code - */ -enum GNUNET_DB_QueryStatus -TEH_PG_insert_aggregation_tracking ( - void *cls, - const struct TALER_WireTransferIdentifierRawP *wtid, - unsigned long long deposit_serial_id); - -#endif diff --git a/src/exchangedb/pg_insert_deposit.c b/src/exchangedb/pg_insert_deposit.c deleted file mode 100644 index a55355259..000000000 --- a/src/exchangedb/pg_insert_deposit.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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/> - */ -/** - * @file exchangedb/pg_insert_deposit.c - * @brief Implementation of the insert_deposit function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_error_codes.h" -#include "taler_dbevents.h" -#include "taler_pq_lib.h" -#include "pg_insert_deposit.h" -#include "pg_helper.h" -#include "pg_setup_wire_target.h" -#include "pg_compute_shard.h" - - -enum GNUNET_DB_QueryStatus -TEH_PG_insert_deposit (void *cls, - struct GNUNET_TIME_Timestamp exchange_timestamp, - const struct TALER_EXCHANGEDB_Deposit *deposit) -{ - struct PostgresClosure *pg = cls; - struct TALER_PaytoHashP h_payto; - enum GNUNET_DB_QueryStatus qs; - - qs = TEH_PG_setup_wire_target (pg, - deposit->receiver_wire_account, - &h_payto); - if (qs < 0) - return qs; - if (GNUNET_TIME_timestamp_cmp (deposit->wire_deadline, - <, - deposit->refund_deadline)) - { - GNUNET_break (0); - } - { - uint64_t shard = TEH_PG_compute_shard (&deposit->merchant_pub); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub), - TALER_PQ_query_param_amount (pg->conn, - &deposit->amount_with_fee), - GNUNET_PQ_query_param_timestamp (&deposit->timestamp), - GNUNET_PQ_query_param_timestamp (&deposit->refund_deadline), - GNUNET_PQ_query_param_timestamp (&deposit->wire_deadline), - GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub), - GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms), - GNUNET_PQ_query_param_auto_from_type (&deposit->wire_salt), - GNUNET_PQ_query_param_auto_from_type (&h_payto), - GNUNET_PQ_query_param_auto_from_type (&deposit->csig), - GNUNET_PQ_query_param_timestamp (&exchange_timestamp), - GNUNET_PQ_query_param_uint64 (&shard), - GNUNET_PQ_query_param_end - }; - - GNUNET_assert (shard <= INT32_MAX); - GNUNET_log ( - GNUNET_ERROR_TYPE_INFO, - "Inserting deposit to be executed at %s (%llu/%llu)\n", - GNUNET_TIME_timestamp2s (deposit->wire_deadline), - (unsigned long long) deposit->wire_deadline.abs_time.abs_value_us, - (unsigned long long) deposit->refund_deadline.abs_time.abs_value_us); - - PREPARE (pg, - "insert_deposit", - "INSERT INTO deposits " - "(known_coin_id" - ",coin_pub" - ",amount_with_fee" - ",wallet_timestamp" - ",refund_deadline" - ",wire_deadline" - ",merchant_pub" - ",h_contract_terms" - ",wire_salt" - ",wire_target_h_payto" - ",coin_sig" - ",exchange_timestamp" - ",shard" - ") SELECT known_coin_id, $1, $2, $3, $4, $5, $6, " - " $7, $8, $9, $10, $11, $12" - " FROM known_coins" - " WHERE coin_pub=$1" - " ON CONFLICT DO NOTHING;"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_deposit", - params); - } -} diff --git a/src/exchangedb/pg_insert_deposit.h b/src/exchangedb/pg_insert_deposit.h deleted file mode 100644 index 82cbcd542..000000000 --- a/src/exchangedb/pg_insert_deposit.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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/> - */ -/** - * @file exchangedb/pg_insert_deposit.h - * @brief implementation of the insert_deposit function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_INSERT_DEPOSIT_H -#define PG_INSERT_DEPOSIT_H - -#include "taler_util.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" -/** - * Insert information about deposited coin into the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param exchange_timestamp time the exchange received the deposit request - * @param deposit deposit information to store - * @return query result status - */ -enum GNUNET_DB_QueryStatus -TEH_PG_insert_deposit (void *cls, - struct GNUNET_TIME_Timestamp exchange_timestamp, - const struct TALER_EXCHANGEDB_Deposit *deposit); - -#endif diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c index f7994e53c..6ecec5bcf 100644 --- a/src/exchangedb/pg_insert_records_by_table.c +++ b/src/exchangedb/pg_insert_records_by_table.c @@ -855,68 +855,106 @@ irbt_cb_table_refresh_transfer_keys ( /** - * Function called with deposits records to insert into table. + * Function called with batch deposits records to insert into table. * * @param pg plugin context * @param td record to insert */ static enum GNUNET_DB_QueryStatus -irbt_cb_table_deposits (struct PostgresClosure *pg, - const struct TALER_EXCHANGEDB_TableData *td) +irbt_cb_table_batch_deposits (struct PostgresClosure *pg, + const struct TALER_EXCHANGEDB_TableData *td) { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&td->serial), - GNUNET_PQ_query_param_uint64 (&td->details.deposits.shard), - GNUNET_PQ_query_param_uint64 (&td->details.deposits.known_coin_id), + GNUNET_PQ_query_param_uint64 (&td->details.batch_deposits.shard), GNUNET_PQ_query_param_auto_from_type ( - &td->details.deposits.coin_pub), - TALER_PQ_query_param_amount ( - pg->conn, - &td->details.deposits.amount_with_fee), - GNUNET_PQ_query_param_timestamp (&td->details.deposits.wallet_timestamp), + &td->details.batch_deposits.merchant_pub), + GNUNET_PQ_query_param_timestamp ( + &td->details.batch_deposits.wallet_timestamp), + GNUNET_PQ_query_param_timestamp ( + &td->details.batch_deposits.exchange_timestamp), GNUNET_PQ_query_param_timestamp ( - &td->details.deposits.exchange_timestamp), - GNUNET_PQ_query_param_timestamp (&td->details.deposits.refund_deadline), - GNUNET_PQ_query_param_timestamp (&td->details.deposits.wire_deadline), - GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.merchant_pub), - GNUNET_PQ_query_param_auto_from_type ( - &td->details.deposits.h_contract_terms), - GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.coin_sig), - GNUNET_PQ_query_param_auto_from_type (&td->details.deposits.wire_salt), - GNUNET_PQ_query_param_auto_from_type ( - &td->details.deposits.wire_target_h_payto), - GNUNET_PQ_query_param_bool (td->details.deposits.policy_blocked), - 0 == td->details.deposits.policy_details_serial_id + &td->details.batch_deposits.refund_deadline), + GNUNET_PQ_query_param_timestamp (&td->details.batch_deposits.wire_deadline), + GNUNET_PQ_query_param_auto_from_type ( + &td->details.batch_deposits.h_contract_terms), + td->details.batch_deposits.no_wallet_data_hash + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_auto_from_type ( + &td->details.batch_deposits.wallet_data_hash), + GNUNET_PQ_query_param_auto_from_type ( + &td->details.batch_deposits.wire_salt), + GNUNET_PQ_query_param_auto_from_type ( + &td->details.batch_deposits.wire_target_h_payto), + GNUNET_PQ_query_param_bool (td->details.batch_deposits.policy_blocked), + td->details.batch_deposits.no_policy_details ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_uint64 ( - &td->details.deposits.policy_details_serial_id), + &td->details.batch_deposits.policy_details_serial_id), GNUNET_PQ_query_param_end }; PREPARE (pg, - "insert_into_table_deposits", - "INSERT INTO deposits" - "(deposit_serial_id" + "insert_into_table_batch_deposits", + "INSERT INTO batch_deposits" + "(batch_deposit_serial_id" ",shard" - ",known_coin_id" - ",coin_pub" - ",amount_with_fee" + ",merchant_pub" ",wallet_timestamp" ",exchange_timestamp" ",refund_deadline" ",wire_deadline" - ",merchant_pub" ",h_contract_terms" - ",coin_sig" + ",wallet_data_hash" ",wire_salt" ",wire_target_h_payto" - ",policy_blocked" ",policy_details_serial_id" + ",policy_blocked" ") VALUES " "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10," - " $11, $12, $13, $14, $15, $16);"); + " $11, $12, $13);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_into_table_deposits", + "insert_into_table_batch_deposits", + params); +} + + +/** + * Function called with deposits records to insert into table. + * + * @param pg plugin context + * @param td record to insert + */ +static enum GNUNET_DB_QueryStatus +irbt_cb_table_coin_deposits (struct PostgresClosure *pg, + const struct TALER_EXCHANGEDB_TableData *td) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&td->serial), + GNUNET_PQ_query_param_uint64 ( + &td->details.coin_deposits.batch_deposit_serial_id), + GNUNET_PQ_query_param_auto_from_type ( + &td->details.coin_deposits.coin_pub), + GNUNET_PQ_query_param_auto_from_type ( + &td->details.coin_deposits.coin_sig), + TALER_PQ_query_param_amount ( + pg->conn, + &td->details.coin_deposits.amount_with_fee), + GNUNET_PQ_query_param_end + }; + + PREPARE (pg, + "insert_into_table_coin_deposits", + "INSERT INTO coin_deposits" + "(coin_deposit_serial_id" + ",batch_deposit_serial_id" + ",coin_pub" + ",coin_sig" + ",amount_with_fee" + ") VALUES " + "($1, $2, $3, $4, $5);"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_into_table_coin_deposits", params); } @@ -939,7 +977,8 @@ irbt_cb_table_refunds (struct PostgresClosure *pg, TALER_PQ_query_param_amount ( pg->conn, &td->details.refunds.amount_with_fee), - GNUNET_PQ_query_param_uint64 (&td->details.refunds.deposit_serial_id), + GNUNET_PQ_query_param_uint64 ( + &td->details.refunds.batch_deposit_serial_id), GNUNET_PQ_query_param_end }; @@ -951,7 +990,7 @@ irbt_cb_table_refunds (struct PostgresClosure *pg, ",merchant_sig" ",rtransaction_id" ",amount_with_fee" - ",deposit_serial_id" + ",batch_deposit_serial_id" ") VALUES " "($1, $2, $3, $4, $5, $6);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, @@ -1014,7 +1053,7 @@ irbt_cb_table_aggregation_tracking (struct PostgresClosure *pg, struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&td->serial), GNUNET_PQ_query_param_uint64 ( - &td->details.aggregation_tracking.deposit_serial_id), + &td->details.aggregation_tracking.batch_deposit_serial_id), GNUNET_PQ_query_param_auto_from_type ( &td->details.aggregation_tracking.wtid_raw), GNUNET_PQ_query_param_end @@ -1024,7 +1063,7 @@ irbt_cb_table_aggregation_tracking (struct PostgresClosure *pg, "insert_into_table_aggregation_tracking", "INSERT INTO aggregation_tracking" "(aggregation_serial_id" - ",deposit_serial_id" + ",batch_deposit_serial_id" ",wtid_raw" ") VALUES " "($1, $2, $3);"); @@ -2194,8 +2233,11 @@ TEH_PG_insert_records_by_table (void *cls, case TALER_EXCHANGEDB_RT_REFRESH_TRANSFER_KEYS: rh = &irbt_cb_table_refresh_transfer_keys; break; - case TALER_EXCHANGEDB_RT_DEPOSITS: - rh = &irbt_cb_table_deposits; + case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS: + rh = &irbt_cb_table_batch_deposits; + break; + case TALER_EXCHANGEDB_RT_COIN_DEPOSITS: + rh = &irbt_cb_table_coin_deposits; break; case TALER_EXCHANGEDB_RT_REFUNDS: rh = &irbt_cb_table_refunds; diff --git a/src/exchangedb/pg_insert_refund.c b/src/exchangedb/pg_insert_refund.c index 05f8a25b7..e989c91bc 100644 --- a/src/exchangedb/pg_insert_refund.c +++ b/src/exchangedb/pg_insert_refund.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -49,12 +49,13 @@ TEH_PG_insert_refund (void *cls, "insert_refund", "INSERT INTO refunds " "(coin_pub" - ",deposit_serial_id" + ",batch_deposit_serial_id" ",merchant_sig" ",rtransaction_id" ",amount_with_fee" - ") SELECT $1, deposit_serial_id, $3, $5, $6" - " FROM deposits" + ") SELECT $1, cdep.batch_deposit_serial_id, $3, $5, $6" + " FROM coin_deposits cdep" + " JOIN batch_deposits bdep USING (batch_deposit_serial_id)" " WHERE coin_pub=$1" " AND h_contract_terms=$4" " AND merchant_pub=$2"); diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c index 92378c3f2..b1656aaf1 100644 --- a/src/exchangedb/pg_lookup_records_by_table.c +++ b/src/exchangedb/pg_lookup_records_by_table.c @@ -1135,77 +1135,125 @@ lrbt_cb_table_refresh_transfer_keys (void *cls, /** - * Function called with deposits table entries. + * Function called with batch deposits table entries. * * @param cls closure * @param result the postgres result * @param num_results the number of results in @a result */ static void -lrbt_cb_table_deposits (void *cls, - PGresult *result, - unsigned int num_results) +lrbt_cb_table_batch_deposits (void *cls, + PGresult *result, + unsigned int num_results) { struct LookupRecordsByTableContext *ctx = cls; struct PostgresClosure *pg = ctx->pg; struct TALER_EXCHANGEDB_TableData td = { - .table = TALER_EXCHANGEDB_RT_DEPOSITS + .table = TALER_EXCHANGEDB_RT_BATCH_DEPOSITS }; for (unsigned int i = 0; i<num_results; i++) { - bool no_policy; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ( "serial", &td.serial), GNUNET_PQ_result_spec_uint64 ( "shard", - &td.details.deposits.shard), - GNUNET_PQ_result_spec_uint64 ( - "known_coin_id", - &td.details.deposits.known_coin_id), + &td.details.batch_deposits.shard), GNUNET_PQ_result_spec_auto_from_type ( - "coin_pub", - &td.details.deposits.coin_pub), - TALER_PQ_RESULT_SPEC_AMOUNT ( - "amount_with_fee", - &td.details.deposits.amount_with_fee), + "merchant_pub", + &td.details.batch_deposits.merchant_pub), GNUNET_PQ_result_spec_timestamp ( "wallet_timestamp", - &td.details.deposits.wallet_timestamp), + &td.details.batch_deposits.wallet_timestamp), GNUNET_PQ_result_spec_timestamp ( "exchange_timestamp", - &td.details.deposits.exchange_timestamp), + &td.details.batch_deposits.exchange_timestamp), GNUNET_PQ_result_spec_timestamp ( "refund_deadline", - &td.details.deposits.refund_deadline), + &td.details.batch_deposits.refund_deadline), GNUNET_PQ_result_spec_timestamp ( "wire_deadline", - &td.details.deposits.wire_deadline), - GNUNET_PQ_result_spec_auto_from_type ( - "merchant_pub", - &td.details.deposits.merchant_pub), + &td.details.batch_deposits.wire_deadline), GNUNET_PQ_result_spec_auto_from_type ( "h_contract_terms", - &td.details.deposits.h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ( - "coin_sig", - &td.details.deposits.coin_sig), + &td.details.batch_deposits.h_contract_terms), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_auto_from_type ( + "wallet_data_hash", + &td.details.batch_deposits.wallet_data_hash), + &td.details.batch_deposits.no_wallet_data_hash), GNUNET_PQ_result_spec_auto_from_type ( "wire_salt", - &td.details.deposits.wire_salt), + &td.details.batch_deposits.wire_salt), GNUNET_PQ_result_spec_auto_from_type ( "wire_target_h_payto", - &td.details.deposits.wire_target_h_payto), + &td.details.batch_deposits.wire_target_h_payto), GNUNET_PQ_result_spec_auto_from_type ( "policy_blocked", - &td.details.deposits.policy_blocked), + &td.details.batch_deposits.policy_blocked), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_uint64 ( "policy_details_serial_id", - &td.details.deposits.policy_details_serial_id), - &no_policy), + &td.details.batch_deposits.policy_details_serial_id), + &td.details.batch_deposits.no_policy_details), + GNUNET_PQ_result_spec_end + }; + + td.details.batch_deposits.policy_details_serial_id = 0; + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ctx->error = true; + return; + } + ctx->cb (ctx->cb_cls, + &td); + GNUNET_PQ_cleanup_result (rs); + } +} + + +/** + * Function called with coin deposits table entries. + * + * @param cls closure + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +lrbt_cb_table_coin_deposits (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct LookupRecordsByTableContext *ctx = cls; + struct PostgresClosure *pg = ctx->pg; + struct TALER_EXCHANGEDB_TableData td = { + .table = TALER_EXCHANGEDB_RT_COIN_DEPOSITS + }; + + for (unsigned int i = 0; i<num_results; i++) + { + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ( + "serial", + &td.serial), + GNUNET_PQ_result_spec_uint64 ( + "batch_deposit_serial_id", + &td.details.coin_deposits.batch_deposit_serial_id), + GNUNET_PQ_result_spec_auto_from_type ( + "coin_pub", + &td.details.coin_deposits.coin_pub), + GNUNET_PQ_result_spec_auto_from_type ( + "coin_sig", + &td.details.coin_deposits.coin_sig), + TALER_PQ_RESULT_SPEC_AMOUNT ( + "amount_with_fee", + &td.details.coin_deposits.amount_with_fee), GNUNET_PQ_result_spec_end }; @@ -1262,8 +1310,8 @@ lrbt_cb_table_refunds (void *cls, "amount_with_fee", &td.details.refunds.amount_with_fee), GNUNET_PQ_result_spec_uint64 ( - "deposit_serial_id", - &td.details.refunds.deposit_serial_id), + "batch_deposit_serial_id", + &td.details.refunds.batch_deposit_serial_id), GNUNET_PQ_result_spec_end }; @@ -1364,8 +1412,8 @@ lrbt_cb_table_aggregation_tracking (void *cls, "serial", &td.serial), GNUNET_PQ_result_spec_uint64 ( - "deposit_serial_id", - &td.details.aggregation_tracking.deposit_serial_id), + "batch_deposit_serial_id", + &td.details.aggregation_tracking.batch_deposit_serial_id), GNUNET_PQ_result_spec_auto_from_type ( "wtid_raw", &td.details.aggregation_tracking.wtid_raw), @@ -3124,30 +3172,40 @@ TEH_PG_lookup_records_by_table (void *cls, " ORDER BY rtc_serial ASC;"); rh = &lrbt_cb_table_refresh_transfer_keys; break; - case TALER_EXCHANGEDB_RT_DEPOSITS: - XPREPARE ("select_above_serial_by_table_deposits", + case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS: + XPREPARE ("select_above_serial_by_table_batch_deposits", "SELECT" - " deposit_serial_id AS serial" + " batch_deposit_serial_id AS serial" ",shard" - ",coin_pub" - ",known_coin_id" - ",amount_with_fee" + ",merchant_pub" ",wallet_timestamp" ",exchange_timestamp" ",refund_deadline" ",wire_deadline" - ",merchant_pub" ",h_contract_terms" - ",coin_sig" + ",wallet_data_hash" ",wire_salt" ",wire_target_h_payto" ",done" ",policy_blocked" ",policy_details_serial_id" - " FROM deposits" - " WHERE deposit_serial_id > $1" - " ORDER BY deposit_serial_id ASC;"); - rh = &lrbt_cb_table_deposits; + " FROM batch_deposits" + " WHERE batch_deposit_serial_id > $1" + " ORDER BY batch_deposit_serial_id ASC;"); + rh = &lrbt_cb_table_batch_deposits; + break; + case TALER_EXCHANGEDB_RT_COIN_DEPOSITS: + XPREPARE ("select_above_serial_by_table_coin_deposits", + "SELECT" + " coin_deposit_serial_id AS serial" + ",batch_deposit_serial_id" + ",coin_pub" + ",coin_sig" + ",amount_with_fee" + " FROM coin_deposits" + " WHERE coin_deposit_serial_id > $1" + " ORDER BY coin_deposit_serial_id ASC;"); + rh = &lrbt_cb_table_coin_deposits; break; case TALER_EXCHANGEDB_RT_REFUNDS: XPREPARE ("select_above_serial_by_table_refunds", @@ -3157,7 +3215,7 @@ TEH_PG_lookup_records_by_table (void *cls, ",merchant_sig" ",rtransaction_id" ",amount_with_fee" - ",deposit_serial_id" + ",batch_deposit_serial_id" " FROM refunds" " WHERE refund_serial_id > $1" " ORDER BY refund_serial_id ASC;"); @@ -3181,7 +3239,7 @@ TEH_PG_lookup_records_by_table (void *cls, XPREPARE ("select_above_serial_by_table_aggregation_tracking", "SELECT" " aggregation_serial_id AS serial" - ",deposit_serial_id" + ",batch_deposit_serial_id" ",wtid_raw" " FROM aggregation_tracking" " WHERE aggregation_serial_id > $1" diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c index 0bf0b9718..9fda7ddf8 100644 --- a/src/exchangedb/pg_lookup_serial_by_table.c +++ b/src/exchangedb/pg_lookup_serial_by_table.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -205,12 +205,20 @@ TEH_PG_lookup_serial_by_table (void *cls, " ORDER BY rtc_serial DESC" " LIMIT 1;"); break; - case TALER_EXCHANGEDB_RT_DEPOSITS: - XPREPARE ("select_serial_by_table_deposits", + case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS: + XPREPARE ("select_serial_by_table_batch_deposits", "SELECT" - " deposit_serial_id AS serial" - " FROM deposits" - " ORDER BY deposit_serial_id DESC" + " batch_deposit_serial_id AS serial" + " FROM batch_deposits" + " ORDER BY batch_deposit_serial_id DESC" + " LIMIT 1;"); + break; + case TALER_EXCHANGEDB_RT_COIN_DEPOSITS: + XPREPARE ("select_serial_by_table_coin_deposits", + "SELECT" + " coin_deposit_serial_id AS serial" + " FROM coin_deposits" + " ORDER BY coin_deposit_serial_id DESC" " LIMIT 1;"); break; case TALER_EXCHANGEDB_RT_REFUNDS: diff --git a/src/exchangedb/pg_lookup_transfer_by_deposit.c b/src/exchangedb/pg_lookup_transfer_by_deposit.c index 5a3043585..425a11d3c 100644 --- a/src/exchangedb/pg_lookup_transfer_by_deposit.c +++ b/src/exchangedb/pg_lookup_transfer_by_deposit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -74,27 +74,30 @@ TEH_PG_lookup_transfer_by_deposit ( PREPARE (pg, "lookup_deposit_wtid", "SELECT" - " aggregation_tracking.wtid_raw" + " atr.wtid_raw" ",wire_out.execution_date" - ",dep.amount_with_fee" - ",dep.wire_salt" + ",cdep.amount_with_fee" + ",bdep.wire_salt" ",wt.payto_uri" ",denom.fee_deposit" - " FROM deposits dep" + " FROM coin_deposits cdep" + " JOIN batch_deposits bdep" + " USING (batch_deposit_serial_id)" " JOIN wire_targets wt" " USING (wire_target_h_payto)" - " JOIN aggregation_tracking" - " USING (deposit_serial_id)" + " JOIN aggregation_tracking atr" + " ON (cdep.batch_deposit_serial_id = atr.batch_deposit_serial_id)" " JOIN known_coins kc" - " ON (kc.coin_pub = dep.coin_pub)" + " ON (kc.coin_pub = cdep.coin_pub)" " JOIN denominations denom" " USING (denominations_serial)" " JOIN wire_out" " USING (wtid_raw)" - " WHERE dep.coin_pub=$1" - " AND dep.merchant_pub=$3" - " AND dep.h_contract_terms=$2"); - + " WHERE cdep.coin_pub=$1" + " AND bdep.merchant_pub=$3" + " AND bdep.h_contract_terms=$2"); + /* NOTE: above query might be more efficient if we computed the shard + from the merchant_pub and included that in the query */ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_deposit_wtid", params, @@ -163,29 +166,33 @@ TEH_PG_lookup_transfer_by_deposit ( "get_deposit_without_wtid", "SELECT" " agt.legitimization_requirement_serial_id" - ",dep.wire_salt" + ",bdep.wire_salt" ",wt.payto_uri" - ",dep.amount_with_fee" + ",cdep.amount_with_fee" ",denom.fee_deposit" - ",dep.wire_deadline" + ",bdep.wire_deadline" ",aml.status" ",aml.kyc_requirement" - " FROM deposits dep" + " FROM coin_deposits cdep" + " JOIN batch_deposits bdep" + " USING (batch_deposit_serial_id)" " JOIN wire_targets wt" " USING (wire_target_h_payto)" " JOIN known_coins kc" - " ON (kc.coin_pub = dep.coin_pub)" + " ON (kc.coin_pub = cdep.coin_pub)" " JOIN denominations denom" " USING (denominations_serial)" " LEFT JOIN aggregation_transient agt " - " ON ( (dep.wire_target_h_payto = agt.wire_target_h_payto) AND" - " (dep.merchant_pub = agt.merchant_pub) )" + " ON ( (bdep.wire_target_h_payto = agt.wire_target_h_payto) AND" + " (bdep.merchant_pub = agt.merchant_pub) )" " LEFT JOIN aml_status aml" " ON (wt.wire_target_h_payto = aml.h_payto)" - " WHERE dep.coin_pub=$1" - " AND dep.merchant_pub=$3" - " AND dep.h_contract_terms=$2" + " WHERE cdep.coin_pub=$1" + " AND bdep.merchant_pub=$3" + " AND bdep.h_contract_terms=$2" " LIMIT 1;"); + /* NOTE: above query might be more efficient if we computed the shard + from the merchant_pub and included that in the query */ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "get_deposit_without_wtid", params, diff --git a/src/exchangedb/pg_lookup_wire_transfer.c b/src/exchangedb/pg_lookup_wire_transfer.c index 500ec2890..7ab023fe7 100644 --- a/src/exchangedb/pg_lookup_wire_transfer.c +++ b/src/exchangedb/pg_lookup_wire_transfer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -82,7 +82,8 @@ handle_wt_result (void *cls, struct TALER_DenominationPublicKey denom_pub; char *payto_uri; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid), + GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", + &rowid), GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract_terms), GNUNET_PQ_result_spec_string ("payto_uri", @@ -153,18 +154,20 @@ TEH_PG_lookup_wire_transfer ( "lookup_transactions", "SELECT" " aggregation_serial_id" - ",deposits.h_contract_terms" + ",bdep.h_contract_terms" ",payto_uri" ",wire_targets.wire_target_h_payto" ",kc.coin_pub" - ",deposits.merchant_pub" + ",bdep.merchant_pub" ",wire_out.execution_date" - ",deposits.amount_with_fee" + ",cdep.amount_with_fee" ",denom.fee_deposit" ",denom.denom_pub" " FROM aggregation_tracking" - " JOIN deposits" - " USING (deposit_serial_id)" + " JOIN batch_deposits bdep" + " USING (batch_deposit_serial_id)" + " JOIN coin_deposits cdep" + " USING (batch_deposit_serial_id)" " JOIN wire_targets" " USING (wire_target_h_payto)" " JOIN known_coins kc" diff --git a/src/exchangedb/pg_persist_policy_details.h b/src/exchangedb/pg_persist_policy_details.h index ed9fd95d8..4fe709d92 100644 --- a/src/exchangedb/pg_persist_policy_details.h +++ b/src/exchangedb/pg_persist_policy_details.h @@ -24,6 +24,8 @@ #include "taler_util.h" #include "taler_json_lib.h" #include "taler_exchangedb_plugin.h" + + /* Persist the details to a policy in the policy_details table. If there * already exists a policy, update the fields accordingly. * diff --git a/src/exchangedb/pg_select_deposits_missing_wire.c b/src/exchangedb/pg_select_batch_deposits_missing_wire.c index a18a7abf8..7118135a1 100644 --- a/src/exchangedb/pg_select_deposits_missing_wire.c +++ b/src/exchangedb/pg_select_batch_deposits_missing_wire.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -14,15 +14,15 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file exchangedb/pg_select_deposits_missing_wire.c - * @brief Implementation of the select_deposits_missing_wire function for Postgres + * @file exchangedb/pg_select_batch_deposits_missing_wire.c + * @brief Implementation of the select_batch_deposits_missing_wire function for Postgres * @author Christian Grothoff */ #include "platform.h" #include "taler_error_codes.h" #include "taler_dbevents.h" #include "taler_pq_lib.h" -#include "pg_select_deposits_missing_wire.h" +#include "pg_select_batch_deposits_missing_wire.h" #include "pg_helper.h" /** @@ -76,7 +76,7 @@ missing_wire_cb (void *cls, struct GNUNET_TIME_Timestamp deadline; bool done; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("deposit_serial_id", + GNUNET_PQ_result_spec_uint64 ("batch_deposit_serial_id", &rowid), GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub), @@ -113,11 +113,12 @@ missing_wire_cb (void *cls, enum GNUNET_DB_QueryStatus -TEH_PG_select_deposits_missing_wire (void *cls, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp end_date, - TALER_EXCHANGEDB_WireMissingCallback cb, - void *cb_cls) +TEH_PG_select_batch_deposits_missing_wire ( + void *cls, + struct GNUNET_TIME_Timestamp start_date, + struct GNUNET_TIME_Timestamp end_date, + TALER_EXCHANGEDB_WireMissingCallback cb, + void *cb_cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -133,36 +134,39 @@ TEH_PG_select_deposits_missing_wire (void *cls, }; enum GNUNET_DB_QueryStatus qs; - /* Used in #postgres_select_deposits_missing_wire */ // FIXME: used by the auditor; can probably be done // smarter by checking if 'done' or 'blocked' // are set correctly when going over deposits, instead // of JOINing with refunds. + // Also unclear why we return by coin_pub here; + // Also fails to check overdue in case of PARTIAL refunds. + PREPARE (pg, "deposits_get_overdue", "SELECT" - " deposit_serial_id" - ",coin_pub" - ",amount_with_fee" - ",payto_uri" - ",wire_deadline" - ",done" - " FROM deposits d" + " bdep.batch_deposit_serial_id" + ",cdep.coin_pub" + ",cdep.amount_with_fee" + ",wt.payto_uri" + ",bdep.wire_deadline" + ",bdep.done" + " FROM batch_deposits bdep" + " JOIN coin_deposits cdep" + " USING (batch_deposit_serial_id)" " JOIN known_coins" " USING (coin_pub)" - " JOIN wire_targets" + " JOIN wire_targets wt" " USING (wire_target_h_payto)" - " WHERE wire_deadline >= $1" - " AND wire_deadline < $2" + " WHERE bdep.wire_deadline >= $1" + " AND bdep.wire_deadline < $2" " AND NOT (EXISTS (SELECT 1" " FROM refunds r" - " WHERE (r.coin_pub = d.coin_pub) AND (r.deposit_serial_id = d.deposit_serial_id))" + " WHERE (r.coin_pub = cdep.coin_pub)" + " AND (r.batch_deposit_serial_id = bdep.batch_deposit_serial_id))" " OR EXISTS (SELECT 1" - " FROM aggregation_tracking" - " WHERE (aggregation_tracking.deposit_serial_id = d.deposit_serial_id)))" - " ORDER BY wire_deadline ASC"); - - + " FROM aggregation_tracking atr" + " WHERE (atr.batch_deposit_serial_id = bdep.batch_deposit_serial_id)))" + " ORDER BY bdep.wire_deadline ASC"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "deposits_get_overdue", params, diff --git a/src/exchangedb/pg_select_deposits_missing_wire.h b/src/exchangedb/pg_select_batch_deposits_missing_wire.h index 40c592cee..697baa837 100644 --- a/src/exchangedb/pg_select_deposits_missing_wire.h +++ b/src/exchangedb/pg_select_batch_deposits_missing_wire.h @@ -14,8 +14,8 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file exchangedb/pg_select_deposits_missing_wire.h - * @brief implementation of the select_deposits_missing_wire function for Postgres + * @file exchangedb/pg_select_batch_deposits_missing_wire.h + * @brief implementation of the select_batch_deposits_missing_wire function for Postgres * @author Christian Grothoff */ #ifndef PG_SELECT_DEPOSITS_MISSING_WIRE_H @@ -37,10 +37,13 @@ * @return transaction status code */ enum GNUNET_DB_QueryStatus -TEH_PG_select_deposits_missing_wire (void *cls, - struct GNUNET_TIME_Timestamp start_date, - struct GNUNET_TIME_Timestamp end_date, - TALER_EXCHANGEDB_WireMissingCallback cb, - void *cb_cls); +TEH_PG_select_batch_deposits_missing_wire (void *cls, + struct GNUNET_TIME_Timestamp + start_date, + struct GNUNET_TIME_Timestamp + end_date, + TALER_EXCHANGEDB_WireMissingCallback + cb, + void *cb_cls); #endif diff --git a/src/exchangedb/pg_select_deposits_above_serial_id.c b/src/exchangedb/pg_select_coin_deposits_above_serial_id.c index 102604f24..49065c175 100644 --- a/src/exchangedb/pg_select_deposits_above_serial_id.c +++ b/src/exchangedb/pg_select_coin_deposits_above_serial_id.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -14,21 +14,21 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file exchangedb/pg_select_deposits_above_serial_id.c - * @brief Implementation of the select_deposits_above_serial_id function for Postgres + * @file exchangedb/pg_select_coin_deposits_above_serial_id.c + * @brief Implementation of the select_coin_deposits_above_serial_id function for Postgres * @author Christian Grothoff */ #include "platform.h" #include "taler_error_codes.h" #include "taler_dbevents.h" #include "taler_pq_lib.h" -#include "pg_select_deposits_above_serial_id.h" +#include "pg_select_coin_deposits_above_serial_id.h" #include "pg_helper.h" /** * Closure for #deposit_serial_helper_cb(). */ -struct DepositSerialContext +struct CoinDepositSerialContext { /** @@ -57,16 +57,16 @@ struct DepositSerialContext * Helper function to be called with the results of a SELECT statement * that has returned @a num_results results. * - * @param cls closure of type `struct DepositSerialContext` + * @param cls closure of type `struct CoinDepositSerialContext` * @param result the postgres result * @param num_results the number of results in @a result */ static void -deposit_serial_helper_cb (void *cls, - PGresult *result, - unsigned int num_results) +coin_deposit_serial_helper_cb (void *cls, + PGresult *result, + unsigned int num_results) { - struct DepositSerialContext *dsc = cls; + struct CoinDepositSerialContext *dsc = cls; struct PostgresClosure *pg = dsc->pg; for (unsigned int i = 0; i<num_results; i++) @@ -107,7 +107,7 @@ deposit_serial_helper_cb (void *cls, &deposit.receiver_wire_account), GNUNET_PQ_result_spec_bool ("done", &done), - GNUNET_PQ_result_spec_uint64 ("deposit_serial_id", + GNUNET_PQ_result_spec_uint64 ("coin_deposit_serial_id", &rowid), GNUNET_PQ_result_spec_end }; @@ -139,7 +139,7 @@ deposit_serial_helper_cb (void *cls, enum GNUNET_DB_QueryStatus -TEH_PG_select_deposits_above_serial_id ( +TEH_PG_select_coin_deposits_above_serial_id ( void *cls, uint64_t serial_id, TALER_EXCHANGEDB_DepositCallback cb, @@ -150,7 +150,7 @@ TEH_PG_select_deposits_above_serial_id ( GNUNET_PQ_query_param_uint64 (&serial_id), GNUNET_PQ_query_param_end }; - struct DepositSerialContext dsc = { + struct CoinDepositSerialContext dsc = { .cb = cb, .cb_cls = cb_cls, .pg = pg, @@ -160,36 +160,38 @@ TEH_PG_select_deposits_above_serial_id ( /* Fetch deposits with rowid '\geq' the given parameter */ PREPARE (pg, - "audit_get_deposits_incr", + "audit_get_coin_deposits_incr", "SELECT" - " amount_with_fee" - ",wallet_timestamp" - ",exchange_timestamp" - ",merchant_pub" + " cdep.amount_with_fee" + ",bdep.wallet_timestamp" + ",bdep.exchange_timestamp" + ",bdep.merchant_pub" ",denom.denom_pub" ",kc.coin_pub" ",kc.age_commitment_hash" - ",coin_sig" - ",refund_deadline" - ",wire_deadline" - ",h_contract_terms" - ",wire_salt" - ",payto_uri AS receiver_wire_account" - ",done" - ",deposit_serial_id" - " FROM deposits" - " JOIN wire_targets USING (wire_target_h_payto)" - " JOIN known_coins kc USING (coin_pub)" - " JOIN denominations denom USING (denominations_serial)" - " WHERE (" - " (deposit_serial_id>=$1)" - " )" - " ORDER BY deposit_serial_id ASC;"); - + ",cdep.coin_sig" + ",bdep.refund_deadline" + ",bdep.wire_deadline" + ",bdep.h_contract_terms" + ",bdep.wire_salt" + ",wt.payto_uri AS receiver_wire_account" + ",bdep.done" + ",cdep.coin_deposit_serial_id" + " FROM coin_deposits cdep" + " JOIN batch_deposits bdep" + " USING (batch_deposit_serial_id)" + " JOIN wire_targets wt" + " USING (wire_target_h_payto)" + " JOIN known_coins kc" + " USING (coin_pub)" + " JOIN denominations denom" + " USING (denominations_serial)" + " WHERE (coin_deposit_serial_id>=$1)" + " ORDER BY coin_deposit_serial_id ASC;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "audit_get_deposits_incr", + "audit_get_coin_deposits_incr", params, - &deposit_serial_helper_cb, + &coin_deposit_serial_helper_cb, &dsc); if (GNUNET_OK != dsc.status) return GNUNET_DB_STATUS_HARD_ERROR; diff --git a/src/exchangedb/pg_select_deposits_above_serial_id.h b/src/exchangedb/pg_select_coin_deposits_above_serial_id.h index e29937e08..5202336a4 100644 --- a/src/exchangedb/pg_select_deposits_above_serial_id.h +++ b/src/exchangedb/pg_select_coin_deposits_above_serial_id.h @@ -14,8 +14,8 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file exchangedb/pg_select_deposits_above_serial_id.h - * @brief implementation of the select_deposits_above_serial_id function for Postgres + * @file exchangedb/pg_select_coin_deposits_above_serial_id.h + * @brief implementation of the select_coin_deposits_above_serial_id function for Postgres * @author Christian Grothoff */ #ifndef PG_SELECT_DEPOSITS_ABOVE_SERIAL_ID_H @@ -35,7 +35,7 @@ * @return transaction status code */ enum GNUNET_DB_QueryStatus -TEH_PG_select_deposits_above_serial_id ( +TEH_PG_select_coin_deposits_above_serial_id ( void *cls, uint64_t serial_id, TALER_EXCHANGEDB_DepositCallback cb, diff --git a/src/exchangedb/pg_select_refunds_above_serial_id.c b/src/exchangedb/pg_select_refunds_above_serial_id.c index 34e6ba8b4..396e8d3d5 100644 --- a/src/exchangedb/pg_select_refunds_above_serial_id.c +++ b/src/exchangedb/pg_select_refunds_above_serial_id.c @@ -182,19 +182,21 @@ TEH_PG_select_refunds_above_serial_id ( PREPARE (pg, "audit_get_refunds_incr", "SELECT" - " dep.merchant_pub" + " bdep.merchant_pub" ",ref.merchant_sig" - ",dep.h_contract_terms" + ",bdep.h_contract_terms" ",ref.rtransaction_id" ",denom.denom_pub" ",kc.coin_pub" ",ref.amount_with_fee" ",ref.refund_serial_id" " FROM refunds ref" - " JOIN deposits dep" - " ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)" + " JOIN batch_deposits bdep" + " ON (ref.batch_deposit_serial_id=bdep.batch_deposit_serial_id)" + " JOIN coin_deposits cdep" + " ON (ref.coin_pub=cdep.coin_pub AND ref.batch_deposit_serial_id=cdep.batch_deposit_serial_id)" " JOIN known_coins kc" - " ON (dep.coin_pub=kc.coin_pub)" + " ON (cdep.coin_pub=kc.coin_pub)" " JOIN denominations denom" " ON (kc.denominations_serial=denom.denominations_serial)" " WHERE ref.refund_serial_id>=$1" @@ -204,12 +206,12 @@ TEH_PG_select_refunds_above_serial_id ( "SELECT" " CAST(SUM(CAST((ref.amount_with_fee).frac AS INT8)) AS INT8) AS s_f" ",CAST(SUM((ref.amount_with_fee).val) AS INT8) AS s_v" - ",dep.amount_with_fee" + ",cdep.amount_with_fee" " FROM refunds ref" - " JOIN deposits dep" - " ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)" + " JOIN coin_deposits cdep" + " ON (ref.coin_pub=cdep.coin_pub AND ref.batch_deposit_serial_id=cdep.batch_deposit_serial_id)" " WHERE ref.refund_serial_id=$1" - " GROUP BY (dep.amount_with_fee);"); + " GROUP BY (cdep.amount_with_fee);"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "audit_get_refunds_incr", params, diff --git a/src/exchangedb/pg_select_refunds_by_coin.c b/src/exchangedb/pg_select_refunds_by_coin.c index 44a505791..d9cd6dd3c 100644 --- a/src/exchangedb/pg_select_refunds_by_coin.c +++ b/src/exchangedb/pg_select_refunds_by_coin.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -49,7 +49,7 @@ struct SelectRefundContext /** * Set to #GNUNET_SYSERR on error. */ - int status; + enum GNUNET_GenericReturnValue status; }; @@ -112,205 +112,29 @@ TEH_PG_select_refunds_by_coin ( GNUNET_PQ_query_param_auto_from_type (h_contract), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_QueryParam params5[] = { - GNUNET_PQ_query_param_auto_from_type (coin_pub), - GNUNET_PQ_query_param_end - }; - struct SelectRefundContext srctx = { .cb = cb, .cb_cls = cb_cls, .pg = pg, .status = GNUNET_OK }; - static int percent_refund = -2; - const char *query; - struct GNUNET_PQ_QueryParam *xparams = params; - - if (-2 == percent_refund) - { - const char *mode = getenv ("TALER_POSTGRES_SELECT_REFUNDS_BY_COIN_LOGIC"); - char dummy; - - if ( (NULL==mode) || - (1 != sscanf (mode, - "%d%c", - &percent_refund, - &dummy)) ) - { - if (NULL != mode) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Bad mode `%s' specified\n", - mode); - percent_refund = 0; - } - } - - switch (percent_refund) - { - case 0: - query = "get_refunds_by_coin_and_contract-v0"; - PREPARE (pg, - query, - "SELECT" - " ref.amount_with_fee" - " FROM refunds ref" - " JOIN deposits dep" - " USING (coin_pub,deposit_serial_id)" - " WHERE ref.coin_pub=$1" - " AND dep.merchant_pub=$2" - " AND dep.h_contract_terms=$3;"); - break; - case 1: - query = "get_refunds_by_coin_and_contract-v1"; - PREPARE (pg, - query, - "SELECT" - " ref.amount_with_fee" - " FROM refunds ref" - " LEFT JOIN deposits dep" - " ON dep.coin_pub = ref.coin_pub" - " AND ref.deposit_serial_id = dep.deposit_serial_id" - " WHERE ref.coin_pub=$1" - " AND dep.merchant_pub=$2" - " AND dep.h_contract_terms=$3;"); - break; - case 2: - query = "get_refunds_by_coin_and_contract-v2"; - PREPARE (pg, - query, - "WITH rc AS MATERIALIZED(" - "SELECT" - " amount_with_fee" - ",coin_pub" - ",deposit_serial_id" - " FROM refunds ref" - " WHERE ref.coin_pub=$1)" - "SELECT" - " rc.amount_with_fee" - " FROM deposits dep" - " JOIN rc" - " ON rc.deposit_serial_id = dep.deposit_serial_id" - " WHERE" - " dep.coin_pub = $1" - " AND dep.merchant_pub = $2" - " AND dep.h_contract_terms = $3"); - break; - case 3: - query = "get_refunds_by_coin_and_contract-v3"; - PREPARE (pg, - query, - "WITH rc AS MATERIALIZED(" - "SELECT" - " amount_with_fee" - ",deposit_serial_id" - " FROM refunds" - " WHERE coin_pub=$1)" - "SELECT" - " rc.amount_with_fee" - " FROM (" - "SELECT" - " amount_with_fee" - " FROM deposits depos" - " WHERE" - " depos.coin_pub = $1" - " AND depos.merchant_pub = $2" - " AND depos.h_contract_terms = $3) dep, rc;"); - break; - case 4: - query = "get_refunds_by_coin_and_contract-v4"; - PREPARE (pg, - query, - "WITH rc AS MATERIALIZED(" - "SELECT" - " amount_with_fee" - ",coin_pub" - ",deposit_serial_id" - " FROM refunds ref" - " WHERE ref.coin_pub=$1)" - "SELECT" - " rc.amount_with_fee" - " ,deposit_serial_id" - " FROM (" - "SELECT" - " amount_with_fee" - " FROM deposits depos" - " WHERE" - " depos.merchant_pub = $2" - " AND depos.h_contract_terms = $3) dep JOIN rc " - "USING(deposit_serial_id, coin_pub);"); - break; - case 5: - query = "get_refunds_by_coin_and_contract-v-broken"; - xparams = params5; - PREPARE (pg, - query, - "SELECT" - " amount_with_fee" - ",coin_pub" - ",deposit_serial_id" - " FROM refunds" - " WHERE coin_pub=$1;"); - break; - case 8: - query = "get_refunds_by_coin_and_contract-v8"; - PREPARE (pg, - query, - "WITH" - " rc AS MATERIALIZED(" - " SELECT" - " amount_with_fee" - " ,coin_pub" - " ,deposit_serial_id" - " FROM refunds" - " WHERE coin_pub=$1)," - " dep AS MATERIALIZED(" - " SELECT" - " deposit_serial_id" - " FROM deposits" - " WHERE coin_pub = $1" - " AND merchant_pub = $2" - " AND h_contract_terms = $3" - ")" - "SELECT" - " rc.amount_with_fee" - " FROM " - " rc JOIN dep USING (deposit_serial_id);"); - break; - case 9: - query = "get_refunds_by_coin_and_contract-v9-broken"; - PREPARE (pg, - query, - "SELECT" - " ref.amount_with_fee" - " FROM deposits dep" - " JOIN refunds ref USING(deposit_serial_id)" - " WHERE dep.coin_pub IN (" - " SELECT coin_pub" - " FROM refunds" - " WHERE coin_pub=$1)" - " AND merchant_pub = $2" - " AND h_contract_terms = $3;"); - break; - case 10: - query = "get_refunds_by_coin_and_contract-v10-broken"; - PREPARE (pg, - query, - "SELECT" - " *" - " FROM" - " exchange_do_refund_by_coin" - " ($1, $2, $3) " - " AS (amount_with_fee taler_amount);"); - break; - default: - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - + const char *query = "get_refunds_by_coin_and_contract"; + + PREPARE (pg, + query, + "SELECT" + " ref.amount_with_fee" + " FROM refunds ref" + " JOIN coin_deposits cdep" + " USING (coin_pub,batch_deposit_serial_id)" + " JOIN batch_deposits bdep" + " ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)" + " WHERE ref.coin_pub=$1" + " AND bdep.merchant_pub=$2" + " AND bdep.h_contract_terms=$3;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, query, - xparams, + params, &get_refunds_cb, &srctx); if (GNUNET_SYSERR == srctx.status) diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 503a699d3..067e859b8 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -65,7 +65,6 @@ #include "pg_iterate_active_signkeys.h" #include "pg_preflight.h" #include "pg_commit.h" -#include "pg_insert_aggregation_tracking.h" #include "pg_drop_tables.h" #include "pg_select_satisfied_kyc_processes.h" #include "pg_select_aggregation_amounts_for_kyc_check.h" @@ -140,7 +139,6 @@ #include "pg_find_aggregation_transient.h" #include "pg_update_aggregation_transient.h" #include "pg_get_ready_deposit.h" -#include "pg_insert_deposit.h" #include "pg_insert_refund.h" #include "pg_select_refunds_by_coin.h" #include "pg_get_melt.h" @@ -161,7 +159,7 @@ #include "pg_start_deferred_wire_out.h" #include "pg_store_wire_transfer_out.h" #include "pg_gc.h" -#include "pg_select_deposits_above_serial_id.h" +#include "pg_select_coin_deposits_above_serial_id.h" #include "pg_select_history_requests_above_serial_id.h" #include "pg_select_purse_decisions_above_serial_id.h" #include "pg_select_purse_deposits_by_purse.h" @@ -178,7 +176,7 @@ #include "pg_get_old_coin_by_h_blind.h" #include "pg_insert_denomination_revocation.h" #include "pg_get_denomination_revocation.h" -#include "pg_select_deposits_missing_wire.h" +#include "pg_select_batch_deposits_missing_wire.h" #include "pg_lookup_auditor_timestamp.h" #include "pg_lookup_auditor_status.h" #include "pg_insert_auditor.h" @@ -479,8 +477,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_commit; plugin->preflight = &TEH_PG_preflight; - plugin->insert_aggregation_tracking - = &TEH_PG_insert_aggregation_tracking; plugin->select_aggregation_amounts_for_kyc_check = &TEH_PG_select_aggregation_amounts_for_kyc_check; plugin->select_satisfied_kyc_processes @@ -627,8 +623,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_update_aggregation_transient; plugin->get_ready_deposit = &TEH_PG_get_ready_deposit; - plugin->insert_deposit - = &TEH_PG_insert_deposit; plugin->insert_refund = &TEH_PG_insert_refund; plugin->select_refunds_by_coin @@ -669,8 +663,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_store_wire_transfer_out; plugin->gc = &TEH_PG_gc; - plugin->select_deposits_above_serial_id - = &TEH_PG_select_deposits_above_serial_id; + plugin->select_coin_deposits_above_serial_id + = &TEH_PG_select_coin_deposits_above_serial_id; plugin->select_history_requests_above_serial_id = &TEH_PG_select_history_requests_above_serial_id; plugin->select_purse_decisions_above_serial_id @@ -703,8 +697,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_insert_denomination_revocation; plugin->get_denomination_revocation = &TEH_PG_get_denomination_revocation; - plugin->select_deposits_missing_wire - = &TEH_PG_select_deposits_missing_wire; + plugin->select_batch_deposits_missing_wire + = &TEH_PG_select_batch_deposits_missing_wire; plugin->lookup_auditor_timestamp = &TEH_PG_lookup_auditor_timestamp; plugin->lookup_auditor_status diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in index 7fff2fd41..cc67249fd 100644 --- a/src/exchangedb/procedures.sql.in +++ b/src/exchangedb/procedures.sql.in @@ -44,8 +44,6 @@ SET search_path TO exchange; #include "exchange_do_insert_kyc_attributes.sql" #include "exchange_do_reserves_in_insert.sql" #include "exchange_do_batch_reserves_update.sql" -#include "exchange_do_refund_by_coin.sql" -#include "exchange_do_get_ready_deposit.sql" #include "exchange_do_get_link_data.sql" #include "exchange_do_batch_coin_known.sql" diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 659f82694..7aba4f12c 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -603,7 +603,7 @@ static struct TALER_PaytoHashP wire_target_h_payto; /** - * Callback for #select_deposits_above_serial_id () + * Callback for #select_coin_deposits_above_serial_id () * * @param cls closure * @param rowid unique serial ID for the deposit in our DB @@ -930,14 +930,17 @@ audit_wire_cb (void *cls, /** * Test API relating to wire_out handling. * + * @param bd batch deposit to test * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit) +test_wire_out (const struct TALER_EXCHANGEDB_BatchDeposit *bd) { + const struct TALER_EXCHANGEDB_CoinDepositInformation *deposit = &bd->cdis[0]; struct TALER_PaytoHashP h_payto; - TALER_payto_hash (deposit->receiver_wire_account, + GNUNET_assert (0 < bd->num_cdis); + TALER_payto_hash (bd->receiver_wire_account, &h_payto); auditor_row_cnt = 0; memset (&wire_out_wtid, @@ -955,8 +958,8 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit) plugin->start_deferred_wire_out (plugin->cls)); /* setup values for wire transfer aggregation data */ - merchant_pub_wt = deposit->merchant_pub; - h_contract_terms_wt = deposit->h_contract_terms; + merchant_pub_wt = bd->merchant_pub; + h_contract_terms_wt = bd->h_contract_terms; coin_pub_wt = deposit->coin.coin_pub; coin_value_wt = deposit->amount_with_fee; @@ -1202,8 +1205,10 @@ run (void *cls) struct TALER_EXCHANGEDB_ReserveHistory *rh_head; struct TALER_EXCHANGEDB_BankTransfer *bt; struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw; - struct TALER_EXCHANGEDB_Deposit deposit; - struct TALER_EXCHANGEDB_Deposit deposit2; + struct TALER_EXCHANGEDB_CoinDepositInformation deposit; + struct TALER_EXCHANGEDB_BatchDeposit bd; + struct TALER_CoinSpendPublicKeyP cpub2; + struct TALER_MerchantPublicKeyP mpub2; struct TALER_EXCHANGEDB_Refund refund; struct TALER_EXCHANGEDB_TransactionList *tl; struct TALER_EXCHANGEDB_TransactionList *tlp; @@ -1233,7 +1238,12 @@ run (void *cls) memset (&deposit, 0, sizeof (deposit)); - deposit.receiver_wire_account = (char *) rcvr; + memset (&bd, + 0, + sizeof (bd)); + bd.receiver_wire_account = (char *) rcvr; + bd.cdis = &deposit; + bd.num_cdis = 1; memset (&salt, 45, sizeof (salt)); @@ -1282,7 +1292,6 @@ run (void *cls) GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.000010", &fees.deposit)); - deposit.deposit_fee = fees.deposit; GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.000010", &fees.refresh)); @@ -1516,23 +1525,22 @@ run (void *cls) struct GNUNET_TIME_Timestamp deposit_timestamp = GNUNET_TIME_timestamp_get (); bool balance_ok; + uint32_t bad_balance_idx; bool in_conflict; struct TALER_PaytoHashP h_payto; RND_BLK (&h_payto); - deposit.refund_deadline + bd.refund_deadline = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MONTHS); - deposit.wire_deadline + bd.wire_deadline = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_MONTHS); deposit.amount_with_fee = value; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->do_deposit (plugin->cls, - &deposit, - known_coin_id, - &h_payto, - false, + &bd, &deposit_timestamp, &balance_ok, + &bad_balance_idx, &in_conflict)); FAILIF (! balance_ok); FAILIF (in_conflict); @@ -1545,9 +1553,9 @@ run (void *cls) bool conflict; refund.coin = deposit.coin; - refund.details.merchant_pub = deposit.merchant_pub; + refund.details.merchant_pub = bd.merchant_pub; RND_BLK (&refund.details.merchant_sig); - refund.details.h_contract_terms = deposit.h_contract_terms; + refund.details.h_contract_terms = bd.h_contract_terms; refund.details.rtransaction_id = 1; refund.details.refund_amount = value; refund.details.refund_fee = fees.refund; @@ -2002,26 +2010,24 @@ run (void *cls) &deposit.csig)); FAILIF (0 != GNUNET_memcmp (&have->merchant_pub, - &deposit.merchant_pub)); + &bd.merchant_pub)); FAILIF (0 != GNUNET_memcmp (&have->h_contract_terms, - &deposit.h_contract_terms)); + &bd.h_contract_terms)); FAILIF (0 != GNUNET_memcmp (&have->wire_salt, - &deposit.wire_salt)); + &bd.wire_salt)); FAILIF (GNUNET_TIME_timestamp_cmp (have->timestamp, !=, - deposit.timestamp)); + bd.wallet_timestamp)); FAILIF (GNUNET_TIME_timestamp_cmp (have->refund_deadline, !=, - deposit.refund_deadline)); + bd.refund_deadline)); FAILIF (GNUNET_TIME_timestamp_cmp (have->wire_deadline, !=, - deposit.wire_deadline)); + bd.wire_deadline)); FAILIF (0 != TALER_amount_cmp (&have->amount_with_fee, &deposit.amount_with_fee)); - FAILIF (0 != TALER_amount_cmp (&have->deposit_fee, - &deposit.deposit_fee)); matched |= 1; break; } @@ -2092,7 +2098,6 @@ run (void *cls) memset (&deposit, 0, sizeof (deposit)); - deposit.deposit_fee = fees.deposit; RND_BLK (&deposit.coin.coin_pub); TALER_denom_pub_hash (&dkp->pub, &deposit.coin.denom_pub_hash); @@ -2104,20 +2109,18 @@ run (void *cls) &alg_values, &dkp->pub)); RND_BLK (&deposit.csig); - RND_BLK (&deposit.merchant_pub); - RND_BLK (&deposit.h_contract_terms); - RND_BLK (&deposit.wire_salt); - deposit.receiver_wire_account = + RND_BLK (&bd.merchant_pub); + RND_BLK (&bd.h_contract_terms); + RND_BLK (&bd.wire_salt); + bd.receiver_wire_account = "payto://iban/DE67830654080004822650?receiver-name=Test"; TALER_merchant_wire_signature_hash ( "payto://iban/DE67830654080004822650?receiver-name=Test", - &deposit.wire_salt, + &bd.wire_salt, &h_wire_wt); deposit.amount_with_fee = value; - deposit.deposit_fee = fees.deposit; - - deposit.refund_deadline = deadline; - deposit.wire_deadline = deadline; + bd.refund_deadline = deadline; + bd.wire_deadline = deadline; result = 8; { uint64_t known_coin_id; @@ -2136,22 +2139,30 @@ run (void *cls) struct GNUNET_TIME_Timestamp r; struct TALER_Amount deposit_fee; struct TALER_MerchantWireHashP h_wire; + bool balance_ok; + uint32_t bad_idx; + bool ctr_conflict; now = GNUNET_TIME_timestamp_get (); + TALER_payto_hash (bd.receiver_wire_account, + &bd.wire_target_h_payto); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_deposit (plugin->cls, - now, - &deposit)); - TALER_merchant_wire_signature_hash (deposit.receiver_wire_account, - &deposit.wire_salt, + plugin->do_deposit (plugin->cls, + &bd, + &now, + &balance_ok, + &bad_idx, + &ctr_conflict)); + TALER_merchant_wire_signature_hash (bd.receiver_wire_account, + &bd.wire_salt, &h_wire); FAILIF (1 != plugin->have_deposit2 (plugin->cls, - &deposit.h_contract_terms, + &bd.h_contract_terms, &h_wire, &deposit.coin.coin_pub, - &deposit.merchant_pub, - deposit.refund_deadline, + &bd.merchant_pub, + bd.refund_deadline, &deposit_fee, &r)); FAILIF (GNUNET_TIME_timestamp_cmp (now, @@ -2169,19 +2180,19 @@ run (void *cls) GNUNET_TIME_absolute_add (deadline.abs_time, GNUNET_TIME_UNIT_SECONDS)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->select_deposits_missing_wire (plugin->cls, - start_range, - end_range, - &wire_missing_cb, - &deposit)); + plugin->select_batch_deposits_missing_wire (plugin->cls, + start_range, + end_range, + &wire_missing_cb, + &deposit)); FAILIF (8 != result); } auditor_row_cnt = 0; FAILIF (0 >= - plugin->select_deposits_above_serial_id (plugin->cls, - 0, - &audit_deposit_cb, - NULL)); + plugin->select_coin_deposits_above_serial_id (plugin->cls, + 0, + &audit_deposit_cb, + NULL)); FAILIF (0 == auditor_row_cnt); result = 8; sleep (2); /* give deposit time to be ready */ @@ -2196,9 +2207,9 @@ run (void *cls) &merchant_pub2, &payto_uri2)); FAILIF (0 != GNUNET_memcmp (&merchant_pub2, - &deposit.merchant_pub)); + &bd.merchant_pub)); FAILIF (0 != strcmp (payto_uri2, - deposit.receiver_wire_account)); + bd.receiver_wire_account)); TALER_payto_hash (payto_uri2, &wire_target_h_payto); GNUNET_free (payto_uri2); @@ -2214,7 +2225,7 @@ run (void *cls) FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->aggregate (plugin->cls, &wire_target_h_payto, - &deposit.merchant_pub, + &bd.merchant_pub, &wtid, &total)); } @@ -2238,7 +2249,7 @@ run (void *cls) FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->select_aggregation_transient (plugin->cls, &wire_target_h_payto, - &deposit.merchant_pub, + &bd.merchant_pub, "x-bank", &wtid2, &total2)); @@ -2246,14 +2257,14 @@ run (void *cls) plugin->create_aggregation_transient (plugin->cls, &wire_target_h_payto, "x-bank", - &deposit.merchant_pub, + &bd.merchant_pub, &wtid, 0, &total)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->select_aggregation_transient (plugin->cls, &wire_target_h_payto, - &deposit.merchant_pub, + &bd.merchant_pub, "x-bank", &wtid2, &total2)); @@ -2275,7 +2286,7 @@ run (void *cls) FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->select_aggregation_transient (plugin->cls, &wire_target_h_payto, - &deposit.merchant_pub, + &bd.merchant_pub, "x-bank", &wtid2, &total2)); @@ -2292,7 +2303,7 @@ run (void *cls) FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->select_aggregation_transient (plugin->cls, &wire_target_h_payto, - &deposit.merchant_pub, + &bd.merchant_pub, "x-bank", &wtid2, &total2)); @@ -2301,37 +2312,35 @@ run (void *cls) plugin->commit (plugin->cls)); result = 10; - deposit2 = deposit; FAILIF (GNUNET_OK != plugin->start (plugin->cls, "test-2")); - RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */ + RND_BLK (&mpub2); /* should fail if merchant is different */ { struct TALER_MerchantWireHashP h_wire; struct GNUNET_TIME_Timestamp r; struct TALER_Amount deposit_fee; - TALER_merchant_wire_signature_hash (deposit2.receiver_wire_account, - &deposit2.wire_salt, + TALER_merchant_wire_signature_hash (bd.receiver_wire_account, + &bd.wire_salt, &h_wire); FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->have_deposit2 (plugin->cls, - &deposit2.h_contract_terms, + &bd.h_contract_terms, &h_wire, - &deposit2.coin.coin_pub, - &deposit2.merchant_pub, - deposit2.refund_deadline, + &deposit.coin.coin_pub, + &mpub2, + bd.refund_deadline, &deposit_fee, &r)); - deposit2.merchant_pub = deposit.merchant_pub; - RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */ + RND_BLK (&cpub2); /* should fail if coin is different */ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->have_deposit2 (plugin->cls, - &deposit2.h_contract_terms, + &bd.h_contract_terms, &h_wire, - &deposit2.coin.coin_pub, - &deposit2.merchant_pub, - deposit2.refund_deadline, + &cpub2, + &bd.merchant_pub, + bd.refund_deadline, &deposit_fee, &r)); } @@ -2381,7 +2390,7 @@ run (void *cls) FAILIF (GNUNET_OK != test_wire_prepare ()); FAILIF (GNUNET_OK != - test_wire_out (&deposit)); + test_wire_out (&bd)); FAILIF (GNUNET_OK != test_gc ()); FAILIF (GNUNET_OK != |