summaryrefslogtreecommitdiff
path: root/src/exchangedb
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-10-30 17:36:57 +0100
committerChristian Grothoff <christian@grothoff.org>2022-10-30 17:36:57 +0100
commit2d55647f2aab1feb37d5439049f6824d11cda56c (patch)
treeaef525c04a030b318a97b9d8ae77573ca73a8237 /src/exchangedb
parent38a078d543f53ca4925ea364919306395e7e4597 (diff)
downloadexchange-2d55647f2aab1feb37d5439049f6824d11cda56c.tar.gz
exchange-2d55647f2aab1feb37d5439049f6824d11cda56c.tar.bz2
exchange-2d55647f2aab1feb37d5439049f6824d11cda56c.zip
add support for reserve open/close operations to auditor, begin to split off purse auditing logic
Diffstat (limited to 'src/exchangedb')
-rw-r--r--src/exchangedb/Makefile.am4
-rw-r--r--src/exchangedb/bench-db-postgres.conf4
-rw-r--r--src/exchangedb/common-0001.sql351
-rw-r--r--src/exchangedb/exchange-0001-part.sql63
-rw-r--r--src/exchangedb/exchangedb-postgres.conf3
-rw-r--r--src/exchangedb/pg_get_coin_transactions.c36
-rw-r--r--src/exchangedb/pg_get_expired_reserves.c3
-rw-r--r--src/exchangedb/pg_get_reserve_history.c10
-rw-r--r--src/exchangedb/pg_get_unfinished_close_requests.c7
-rw-r--r--src/exchangedb/pg_insert_records_by_table.c22
-rw-r--r--src/exchangedb/pg_lookup_records_by_table.c34
-rw-r--r--src/exchangedb/pg_lookup_serial_by_table.c10
-rw-r--r--src/exchangedb/pg_select_reserve_closed_above_serial_id.c180
-rw-r--r--src/exchangedb/pg_select_reserve_closed_above_serial_id.h47
-rw-r--r--src/exchangedb/pg_select_reserve_open_above_serial_id.c169
-rw-r--r--src/exchangedb/pg_select_reserve_open_above_serial_id.h47
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c250
-rw-r--r--src/exchangedb/procedures.sql103
-rw-r--r--src/exchangedb/shard-0001-part.sql4
-rw-r--r--src/exchangedb/test-exchange-db-postgres.conf5
-rw-r--r--src/exchangedb/test_exchangedb.c6
21 files changed, 873 insertions, 485 deletions
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 54f51e9a3..f4ea5df9c 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -79,7 +79,9 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_iterate_reserve_close_info.c pg_iterate_reserve_close_info.h \
pg_lookup_records_by_table.c pg_lookup_records_by_table.h \
pg_lookup_serial_by_table.c pg_lookup_serial_by_table.h \
- pg_select_reserve_close_info.c pg_select_reserve_close_info.h
+ pg_select_reserve_close_info.c pg_select_reserve_close_info.h \
+ pg_select_reserve_closed_above_serial_id.c pg_select_reserve_closed_above_serial_id.h \
+ pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h
libtaler_plugin_exchangedb_postgres_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_exchangedb_postgres_la_LDFLAGS = \
diff --git a/src/exchangedb/bench-db-postgres.conf b/src/exchangedb/bench-db-postgres.conf
index 837ae41e2..d51cf9175 100644
--- a/src/exchangedb/bench-db-postgres.conf
+++ b/src/exchangedb/bench-db-postgres.conf
@@ -8,3 +8,7 @@ CONFIG = postgres:///talercheck
# Where are the SQL files to setup our tables?
# Important: this MUST end with a "/"!
SQL_DIR = $DATADIR/sql/exchange/
+
+[exchangedb]
+# Number of purses per account by default.
+DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file
diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql
index 9f32ede74..ab4f8ea91 100644
--- a/src/exchangedb/common-0001.sql
+++ b/src/exchangedb/common-0001.sql
@@ -346,6 +346,7 @@ BEGIN
',amount_frac INT4 NOT NULL'
',closing_fee_val INT8 NOT NULL'
',closing_fee_frac INT4 NOT NULL'
+ ',close_request_row INT8 NOT NULL DEFAULT(0)'
') %s ;'
,table_name
,'PARTITION BY HASH (reserve_pub)'
@@ -383,6 +384,79 @@ END
$$;
+--------------------------- close_requests ---------------------------
+
+CREATE OR REPLACE FUNCTION create_table_close_requests(
+ IN shard_suffix VARCHAR DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name VARCHAR DEFAULT 'close_requests';
+BEGIN
+
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I '
+ '(close_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE'
+ ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)' -- REFERENCES reserves(reserve_pub) ON DELETE CASCADE
+ ',close_timestamp INT8 NOT NULL'
+ ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
+ ',close_val INT8 NOT NULL'
+ ',close_frac INT4 NOT NULL'
+ ',close_fee_val INT8 NOT NULL'
+ ',close_fee_frac INT4 NOT NULL'
+ ',payto_uri VARCHAR NOT NULL'
+ ',done BOOL NOT NULL DEFAULT(FALSE)'
+ ',PRIMARY KEY (reserve_pub,close_timestamp)'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (reserve_pub)'
+ ,shard_suffix
+ );
+END
+$$;
+
+
+CREATE OR REPLACE FUNCTION add_constraints_to_close_requests(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name VARCHAR DEFAULT 'close_requests';
+BEGIN
+
+ EXECUTE FORMAT (
+ 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_uuid_index '
+ 'ON ' || table_name || ' '
+ '(close_request_serial_id);'
+ );
+ EXECUTE FORMAT (
+ 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_done_index '
+ 'ON ' || table_name || ' '
+ '(done);'
+ );
+END
+$$;
+
+CREATE OR REPLACE FUNCTION add_constraints_to_close_requests_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE close_requests_' || partition_suffix || ' '
+ 'ADD CONSTRAINT close_requests_' || partition_suffix || '_close_request_uuid_pkey '
+ 'UNIQUE (close_request_serial_id)'
+ );
+END
+$$;
+
+
--------------------------- reserves_open_requests -------------------------------
CREATE OR REPLACE FUNCTION create_table_reserves_open_requests(
@@ -1391,8 +1465,6 @@ BEGIN
',h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)'
',age_limit INT4 NOT NULL'
',flags INT4 NOT NULL'
- ',refunded BOOLEAN NOT NULL DEFAULT(FALSE)'
- ',finished BOOLEAN NOT NULL DEFAULT(FALSE)'
',in_reserve_quota BOOLEAN NOT NULL DEFAULT(FALSE)'
',amount_with_fee_val INT8 NOT NULL'
',amount_with_fee_frac INT4 NOT NULL'
@@ -1444,53 +1516,6 @@ END
$$;
-------------------------------- purse_refunds ----------------------------------------
-
-CREATE OR REPLACE FUNCTION create_table_purse_refunds(
- IN shard_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'purse_refunds';
-BEGIN
-
- PERFORM create_partitioned_table(
- 'CREATE TABLE IF NOT EXISTS %I '
- '(purse_refunds_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE
- ',purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)'
- ',PRIMARY KEY (purse_pub)'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (purse_pub)'
- ,shard_suffix
- );
-
- table_name = concat_ws('_', table_name, shard_suffix);
-
-END
-$$;
-
-CREATE OR REPLACE FUNCTION add_constraints_to_purse_refunds_partition(
- IN partition_suffix VARCHAR
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE purse_refunds_' || partition_suffix || ' '
- 'ADD CONSTRAINT purse_refunds_' || partition_suffix || '_purse_refunds_serial_id_key '
- 'UNIQUE (purse_refunds_serial_id) '
- );
-END
-$$;
-
-
-
-
-
---------------------------- purse_merges -----------------------------
CREATE OR REPLACE FUNCTION create_table_purse_merges(
@@ -1602,6 +1627,53 @@ BEGIN
END
$$;
+
+------------------------------- purse_decision ----------------------------------------
+
+CREATE OR REPLACE FUNCTION create_table_purse_decision(
+ IN shard_suffix VARCHAR DEFAULT NULL
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name VARCHAR DEFAULT 'purse_decision';
+BEGIN
+
+ PERFORM create_partitioned_table(
+ 'CREATE TABLE IF NOT EXISTS %I '
+ '(purse_decision_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE
+ ',purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)'
+ ',action_timestamp INT8 NOT NULL'
+ ',refunded BOOL NOT NULL'
+ ',PRIMARY KEY (purse_pub)'
+ ') %s ;'
+ ,table_name
+ ,'PARTITION BY HASH (purse_pub)'
+ ,shard_suffix
+ );
+
+ table_name = concat_ws('_', table_name, shard_suffix);
+
+END
+$$;
+
+CREATE OR REPLACE FUNCTION add_constraints_to_purse_decision_partition(
+ IN partition_suffix VARCHAR
+)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+BEGIN
+ EXECUTE FORMAT (
+ 'ALTER TABLE purse_decision_' || partition_suffix || ' '
+ 'ADD CONSTRAINT purse_decision_' || partition_suffix || '_purse_action_serial_id_key '
+ 'UNIQUE (purse_decision_serial_id) '
+ );
+END
+$$;
+
+
------------------------- contracts -------------------------------
CREATE OR REPLACE FUNCTION create_table_contracts(
@@ -1678,80 +1750,6 @@ BEGIN
END
$$;
---------------------------- close_requests ---------------------------
-
-CREATE OR REPLACE FUNCTION create_table_close_requests(
- IN shard_suffix VARCHAR DEFAULT NULL
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'close_requests';
-BEGIN
-
- PERFORM create_partitioned_table(
- 'CREATE TABLE IF NOT EXISTS %I '
- '(close_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE'
- ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)' -- REFERENCES reserves(reserve_pub) ON DELETE CASCADE
- ',close_timestamp INT8 NOT NULL'
- ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
- ',close_val INT8 NOT NULL'
- ',close_frac INT4 NOT NULL'
- ',close_fee_val INT8 NOT NULL'
- ',close_fee_frac INT4 NOT NULL'
- ',payto_uri VARCHAR NOT NULL'
- ',done BOOL NOT NULL DEFAULT(FALSE)'
- ',PRIMARY KEY (reserve_pub,close_timestamp)'
- ') %s ;'
- ,table_name
- ,'PARTITION BY HASH (reserve_pub)'
- ,shard_suffix
- );
-END
-$$;
-
-
-CREATE OR REPLACE FUNCTION add_constraints_to_close_requests(
- IN partition_suffix VARCHAR
-)
-RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- table_name VARCHAR DEFAULT 'close_requests';
-BEGIN
-
- EXECUTE FORMAT (
- 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_uuid_index '
- 'ON ' || table_name || ' '
- '(close_request_serial_id);'
- );
- EXECUTE FORMAT (
- 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_done_index '
- 'ON ' || table_name || ' '
- '(done);'
- );
-END
-$$;
-
-CREATE OR REPLACE FUNCTION add_constraints_to_close_requests_partition(
- IN partition_suffix VARCHAR
-)
-RETURNS void
-LANGUAGE plpgsql
-AS $$
-BEGIN
- EXECUTE FORMAT (
- 'ALTER TABLE close_requests_' || partition_suffix || ' '
- 'ADD CONSTRAINT close_requests_' || partition_suffix || '_close_request_uuid_pkey '
- 'UNIQUE (close_request_serial_id)'
- );
-END
-$$;
-
-
-
------------------------------- purse_deposits -------------------------------
CREATE OR REPLACE FUNCTION create_table_purse_deposits(
@@ -2087,6 +2085,15 @@ BEGIN
ALTER TABLE IF EXISTS reserves_close
DETACH PARTITION reserves_close_default;
+ ALTER TABLE IF EXISTS history_requests
+ DETACH partition history_requests_default;
+
+ ALTER TABLE IF EXISTS close_requests
+ DETACH partition close_requests_default;
+
+ ALTER TABLE IF EXISTS reserves_open_requests
+ DETACH partition reserves_open_requests_default;
+
ALTER TABLE IF EXISTS reserves_out
DETACH PARTITION reserves_out_default;
@@ -2145,8 +2152,8 @@ BEGIN
ALTER TABLE IF EXISTS purse_requests
DETACH partition purse_requests_default;
- ALTER TABLE IF EXISTS purse_refunds
- DETACH partition purse_refunds_default;
+ ALTER TABLE IF EXISTS purse_decision
+ DETACH partition purse_decision_default;
ALTER TABLE IF EXISTS purse_merges
DETACH partition purse_merges_default;
@@ -2157,12 +2164,6 @@ BEGIN
ALTER TABLE IF EXISTS contracts
DETACH partition contracts_default;
- ALTER TABLE IF EXISTS history_requests
- DETACH partition history_requests_default;
-
- ALTER TABLE IF EXISTS close_requests
- DETACH partition close_requests_default;
-
ALTER TABLE IF EXISTS purse_deposits
DETACH partition purse_deposits_default;
@@ -2194,6 +2195,10 @@ BEGIN
DROP TABLE IF EXISTS reserves_default;
DROP TABLE IF EXISTS reserves_in_default;
DROP TABLE IF EXISTS reserves_close_default;
+ DROP TABLE IF EXISTS reserves_open_requests_default;
+ DROP TABLE IF EXISTS history_requests_default;
+ DROP TABLE IF EXISTS close_requests_default;
+
DROP TABLE IF EXISTS reserves_out_default;
DROP TABLE IF EXISTS reserves_out_by_reserve_default;
DROP TABLE IF EXISTS known_coins_default;
@@ -2214,13 +2219,12 @@ BEGIN
DROP TABLE IF EXISTS cs_nonce_locks_default;
DROP TABLE IF EXISTS purse_requests_default;
- DROP TABLE IF EXISTS purse_refunds_default;
+ DROP TABLE IF EXISTS purse_decision_default;
DROP TABLE IF EXISTS purse_merges_default;
DROP TABLE IF EXISTS account_merges_default;
- DROP TABLE IF EXISTS contracts_default;
- DROP TABLE IF EXISTS history_requests_default;
- DROP TABLE IF EXISTS close_requests_default;
DROP TABLE IF EXISTS purse_deposits_default;
+ DROP TABLE IF EXISTS contracts_default;
+
DROP TABLE IF EXISTS wad_out_entries_default;
DROP TABLE IF EXISTS wads_in_default;
DROP TABLE IF EXISTS wad_in_entries_default;
@@ -2417,54 +2421,63 @@ BEGIN
);
PERFORM add_constraints_to_cs_nonce_locks_partition(num_partitions::varchar);
- ---------------- P2P ----------------------
PERFORM create_hash_partition(
- 'purse_requests'
+ 'close_requests'
,modulus
,num_partitions
);
- PERFORM add_constraints_to_purse_requests_partition(num_partitions::varchar);
PERFORM create_hash_partition(
- 'purse_refunds'
+ 'reserves_open_requests'
,modulus
,num_partitions
);
- PERFORM add_constraints_to_purse_refunds_partition(num_partitions::varchar);
+ PERFORM add_constraints_to_reserves_open_request_partition(num_partitions::varchar);
PERFORM create_hash_partition(
- 'purse_merges'
+ 'history_requests'
,modulus
,num_partitions
);
- PERFORM add_constraints_to_purse_merges_partition(num_partitions::varchar);
+
+
+ ---------------- P2P ----------------------
PERFORM create_hash_partition(
- 'account_merges'
+ 'purse_requests'
,modulus
,num_partitions
);
- PERFORM add_constraints_to_account_merges_partition(num_partitions::varchar);
+ PERFORM add_constraints_to_purse_requests_partition(num_partitions::varchar);
PERFORM create_hash_partition(
- 'contracts'
+ 'purse_decision'
,modulus
,num_partitions
);
- PERFORM add_constraints_to_contracts_partition(num_partitions::varchar);
+ PERFORM add_constraints_to_purse_decision_partition(num_partitions::varchar);
PERFORM create_hash_partition(
- 'history_requests'
+ 'purse_merges'
,modulus
,num_partitions
);
+ PERFORM add_constraints_to_purse_merges_partition(num_partitions::varchar);
PERFORM create_hash_partition(
- 'close_requests'
+ 'account_merges'
,modulus
,num_partitions
);
+ PERFORM add_constraints_to_account_merges_partition(num_partitions::varchar);
+
+ PERFORM create_hash_partition(
+ 'contracts'
+ ,modulus
+ ,num_partitions
+ );
+ PERFORM add_constraints_to_contracts_partition(num_partitions::varchar);
PERFORM create_hash_partition(
'purse_deposits'
@@ -2644,8 +2657,8 @@ BEGIN
DROP CONSTRAINT IF EXISTS purse_requests_pkey CASCADE
;
- ALTER TABLE IF EXISTS purse_refunds
- DROP CONSTRAINT IF EXISTS purse_refunds_pkey CASCADE
+ ALTER TABLE IF EXISTS purse_decision
+ DROP CONSTRAINT IF EXISTS purse_decision_pkey CASCADE
;
ALTER TABLE IF EXISTS purse_merges
@@ -2782,6 +2795,27 @@ BEGIN
,local_user
);
PERFORM create_foreign_hash_partition(
+ 'history_requests'
+ ,total_num_shards
+ ,shard_suffix
+ ,current_shard_num
+ ,local_user
+ );
+ PERFORM create_foreign_hash_partition(
+ 'close_requests'
+ ,total_num_shards
+ ,shard_suffix
+ ,current_shard_num
+ ,local_user
+ );
+ PERFORM create_foreign_hash_partition(
+ 'open_requests'
+ ,total_num_shards
+ ,shard_suffix
+ ,current_shard_num
+ ,local_user
+ );
+ PERFORM create_foreign_hash_partition(
'known_coins'
,total_num_shards
,shard_suffix
@@ -2904,7 +2938,7 @@ BEGIN
,local_user
);
PERFORM create_foreign_hash_partition(
- 'purse_refunds'
+ 'purse_decision'
,total_num_shards
,shard_suffix
,current_shard_num
@@ -2931,20 +2965,7 @@ BEGIN
,current_shard_num
,local_user
);
- PERFORM create_foreign_hash_partition(
- 'history_requests'
- ,total_num_shards
- ,shard_suffix
- ,current_shard_num
- ,local_user
- );
- PERFORM create_foreign_hash_partition(
- 'close_requests'
- ,total_num_shards
- ,shard_suffix
- ,current_shard_num
- ,local_user
- );
+
PERFORM create_foreign_hash_partition(
'purse_deposits'
,total_num_shards
diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql
index 760acd98b..4903b8879 100644
--- a/src/exchangedb/exchange-0001-part.sql
+++ b/src/exchangedb/exchange-0001-part.sql
@@ -1135,10 +1135,6 @@ COMMENT ON COLUMN purse_requests.h_contract_terms
IS 'Hash of the contract the parties are to agree to';
COMMENT ON COLUMN purse_requests.flags
IS 'see the enum TALER_WalletAccountMergeFlags';
-COMMENT ON COLUMN purse_requests.finished
- IS 'set to TRUE once the purse has been merged (into reserve or wad) or the coins were refunded (transfer aborted)';
-COMMENT ON COLUMN purse_requests.refunded
- IS 'set to TRUE if the purse could not be merged and thus all deposited coins were refunded';
COMMENT ON COLUMN purse_requests.in_reserve_quota
IS 'set to TRUE if this purse currently counts against the number of free purses in the respective reserve';
COMMENT ON COLUMN purse_requests.amount_with_fee_val
@@ -1157,20 +1153,20 @@ CREATE TABLE IF NOT EXISTS purse_requests_default
SELECT add_constraints_to_purse_requests_partition('default');
--- ------------------------------ purse_refunds ----------------------------------------
+-- ------------------------------ purse_decisions ----------------------------------------
-SELECT create_table_purse_refunds();
+SELECT create_table_purse_decision();
-COMMENT ON TABLE purse_refunds
- IS 'Purses that were refunded due to expiration';
-COMMENT ON COLUMN purse_refunds.purse_pub
+COMMENT ON TABLE purse_decision
+ IS 'Purses that were decided upon (refund or merge)';
+COMMENT ON COLUMN purse_decision.purse_pub
IS 'Public key of the purse';
-CREATE TABLE IF NOT EXISTS purse_refunds_default
- PARTITION OF purse_refunds
+CREATE TABLE IF NOT EXISTS purse_decision_default
+ PARTITION OF purse_decision
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
-SELECT add_constraints_to_purse_refunds_partition('default');
+SELECT add_constraints_to_purse_decision_partition('default');
-- ------------------------------ purse_merges ----------------------------------------
@@ -1462,7 +1458,6 @@ CREATE OR REPLACE FUNCTION purse_requests_insert_trigger()
LANGUAGE plpgsql
AS $$
BEGIN
- ASSERT NOT NEW.finished,'Internal invariant violated';
INSERT INTO
purse_actions
(purse_pub
@@ -1482,45 +1477,3 @@ CREATE TRIGGER purse_requests_on_insert
COMMENT ON TRIGGER purse_requests_on_insert
ON purse_requests
IS 'Here we install an entry for the purse expiration.';
-
-
-CREATE OR REPLACE FUNCTION purse_requests_on_update_trigger()
- RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- IF (NEW.finished AND NOT OLD.finished)
- THEN
- -- If this purse counted against the reserve's
- -- quota of purses, decrement the reserve accounting.
- IF (NEW.in_reserve_quota)
- THEN
- UPDATE reserves
- SET purses_active=purses_active-1
- WHERE reserve_pub IN
- (SELECT reserve_pub
- FROM exchange.purse_merges
- WHERE purse_pub=NEW.purse_pub
- LIMIT 1);
- NEW.in_reserve_quota=FALSE;
- END IF;
- -- Delete from the purse_actions table, we are done
- -- with this purse for good.
- DELETE FROM exchange.purse_actions
- WHERE purse_pub=NEW.purse_pub;
- RETURN NEW;
- END IF;
-
- RETURN NEW;
-END $$;
-
-COMMENT ON FUNCTION purse_requests_on_update_trigger()
- IS 'Trigger the router if the purse is ready. Also removes the entry from the router watchlist once the purse is finished.';
-
-CREATE TRIGGER purse_requests_on_update
- BEFORE UPDATE
- ON purse_requests
- FOR EACH ROW EXECUTE FUNCTION purse_requests_on_update_trigger();
-COMMENT ON TRIGGER purse_requests_on_update
- ON purse_requests
- IS 'This covers the case where a deposit is made into a purse, which inherently then changes the purse balance via an UPDATE. If the merge is already present and the balance matches the total, we trigger the router. Once the router sets the purse to finished, the trigger will remove the purse from the watchlist of the router.';
diff --git a/src/exchangedb/exchangedb-postgres.conf b/src/exchangedb/exchangedb-postgres.conf
index 7d600586f..e481940ce 100644
--- a/src/exchangedb/exchangedb-postgres.conf
+++ b/src/exchangedb/exchangedb-postgres.conf
@@ -4,3 +4,6 @@ CONFIG = "postgres:///taler"
# Where are the SQL files to setup our tables?
# Important: this MUST end with a "/"!
SQL_DIR = $DATADIR/sql/exchange/
+
+# Number of purses per account by default.
+DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file
diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c
index 27bd513fd..f24c9be4a 100644
--- a/src/exchangedb/pg_get_coin_transactions.c
+++ b/src/exchangedb/pg_get_coin_transactions.c
@@ -169,6 +169,7 @@ add_coin_purse_deposit (void *cls,
chc->have_deposit_or_melt = true;
deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
{
+ bool not_finished;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&deposit->amount),
@@ -186,8 +187,10 @@ add_coin_purse_deposit (void *cls,
&deposit->coin_sig),
GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
&deposit->h_age_commitment),
- GNUNET_PQ_result_spec_bool ("refunded",
- &deposit->refunded),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_bool ("refunded",
+ &deposit->refunded),
+ &not_finished),
GNUNET_PQ_result_spec_end
};
@@ -201,6 +204,8 @@ add_coin_purse_deposit (void *cls,
chc->failed = true;
return;
}
+ if (not_finished)
+ deposit->refunded = false;
deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment);
}
tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
@@ -352,9 +357,9 @@ add_coin_refund (void *cls,
* @param num_results the number of results in @a result
*/
static void
-add_coin_purse_refund (void *cls,
- PGresult *result,
- unsigned int num_results)
+add_coin_purse_decision (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
struct CoinHistoryContext *chc = cls;
struct PostgresClosure *pg = chc->pg;
@@ -374,7 +379,7 @@ add_coin_purse_refund (void *cls,
&prefund->refund_amount),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
&prefund->refund_fee),
- GNUNET_PQ_result_spec_uint64 ("purse_refunds_serial_id",
+ GNUNET_PQ_result_spec_uint64 ("purse_decision_serial_id",
&serial_id),
GNUNET_PQ_result_spec_end
};
@@ -687,8 +692,8 @@ TEH_PG_get_coin_transactions (
{ "get_purse_deposit_by_coin_pub",
&add_coin_purse_deposit },
/** #TALER_EXCHANGEDB_TT_PURSE_REFUND */
- { "get_purse_refund_by_coin_pub",
- &add_coin_purse_refund },
+ { "get_purse_decision_by_coin_pub",
+ &add_coin_purse_decision },
/** #TALER_EXCHANGEDB_TT_REFUND */
{ "get_refunds_by_coin",
&add_coin_refund },
@@ -775,12 +780,14 @@ TEH_PG_get_coin_transactions (
",kc.age_commitment_hash"
",pd.coin_sig"
",pd.purse_deposit_serial_id"
- ",pr.refunded"
+ ",pdes.refunded"
" FROM purse_deposits pd"
" LEFT JOIN partners"
" USING (partner_serial_id)"
" JOIN purse_requests pr"
" USING (purse_pub)"
+ " LEFT JOIN purse_decision pdes"
+ " USING (purse_pub)"
" JOIN known_coins kc"
" ON (pd.coin_pub = kc.coin_pub)"
" JOIN denominations denoms"
@@ -809,22 +816,23 @@ TEH_PG_get_coin_transactions (
" USING (denominations_serial)"
" WHERE ref.coin_pub=$1;");
PREPARE (pg,
- "get_purse_refund_by_coin_pub",
+ "get_purse_decision_by_coin_pub",
"SELECT"
- " pr.purse_pub"
+ " pdes.purse_pub"
",pd.amount_with_fee_val"
",pd.amount_with_fee_frac"
",denom.fee_refund_val "
",denom.fee_refund_frac "
- ",pr.purse_refunds_serial_id"
+ ",pdes.purse_decision_serial_id"
" FROM purse_deposits pd"
- " JOIN purse_refunds pr"
+ " JOIN purse_decision pdes"
" USING (purse_pub)"
" JOIN known_coins kc"
" ON (pd.coin_pub = kc.coin_pub)"
" JOIN denominations denom"
" USING (denominations_serial)"
- " WHERE pd.coin_pub=$1;");
+ " WHERE pd.coin_pub=$1"
+ " AND pdes.refunded;");
PREPARE (pg,
"recoup_by_old_coin",
"SELECT"
diff --git a/src/exchangedb/pg_get_expired_reserves.c b/src/exchangedb/pg_get_expired_reserves.c
index 07a739115..c7162dc6b 100644
--- a/src/exchangedb/pg_get_expired_reserves.c
+++ b/src/exchangedb/pg_get_expired_reserves.c
@@ -102,7 +102,8 @@ reserve_expired_cb (void *cls,
&reserve_pub,
&remaining_balance,
account_details,
- exp_date);
+ exp_date,
+ 0);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c
index 893ba7db4..8b8e280a6 100644
--- a/src/exchangedb/pg_get_reserve_history.c
+++ b/src/exchangedb/pg_get_reserve_history.c
@@ -797,13 +797,14 @@ TEH_PG_get_reserve_history (void *cls,
" FROM purse_merges pm"
" JOIN purse_requests pr"
" USING (purse_pub)"
+ " JOIN purse_decision pdes"
+ " USING (purse_pub)"
" JOIN account_merges am"
" ON (am.purse_pub = pm.purse_pub AND"
" am.reserve_pub = pm.reserve_pub)"
" WHERE pm.reserve_pub=$1"
" AND pm.partner_serial_id=0" /* must be local! */
- " AND pr.finished"
- " AND NOT pr.refunded;");
+ " AND NOT pdes.refunded;");
PREPARE (pg,
"history_by_reserve",
"SELECT"
@@ -1089,14 +1090,15 @@ TEH_PG_get_reserve_status (void *cls,
" FROM purse_merges pm"
" JOIN purse_requests pr"
" USING (purse_pub)"
+ " JOIN purse_decision pdes"
+ " USING (purse_pub)"
" JOIN account_merges am"
" ON (am.purse_pub = pm.purse_pub AND"
" am.reserve_pub = pm.reserve_pub)"
" WHERE pm.reserve_pub=$1"
" AND pm.merge_timestamp >= $2"
" AND pm.partner_serial_id=0" /* must be local! */
- " AND pr.finished"
- " AND NOT pr.refunded;");
+ " AND NOT pdes.refunded;");
PREPARE (pg,
"history_by_reserve_truncated",
"SELECT"
diff --git a/src/exchangedb/pg_get_unfinished_close_requests.c b/src/exchangedb/pg_get_unfinished_close_requests.c
index d9da6a7c0..fa8abdf8b 100644
--- a/src/exchangedb/pg_get_unfinished_close_requests.c
+++ b/src/exchangedb/pg_get_unfinished_close_requests.c
@@ -77,6 +77,7 @@ reserve_cb (void *cls,
char *account_details;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount remaining_balance;
+ uint64_t close_request_row;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_timestamp ("expiration_date",
&exp_date),
@@ -86,6 +87,8 @@ reserve_cb (void *cls,
&reserve_pub),
TALER_PQ_RESULT_SPEC_AMOUNT ("close",
&remaining_balance),
+ GNUNET_PQ_result_spec_uint64 ("close_request_serial_id",
+ &close_request_row),
GNUNET_PQ_result_spec_end
};
@@ -102,7 +105,8 @@ reserve_cb (void *cls,
&reserve_pub,
&remaining_balance,
account_details,
- exp_date);
+ exp_date,
+ close_request_row);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
@@ -136,6 +140,7 @@ TEH_PG_get_unfinished_close_requests (
" WHERE done=FALSE"
" RETURNING"
" reserve_pub"
+ " ,close_request_serial_id"
" ,close_timestamp AS expiration_date"
" ,close_val"
" ,close_frac"
diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c
index 5613166cd..99173cc60 100644
--- a/src/exchangedb/pg_insert_records_by_table.c
+++ b/src/exchangedb/pg_insert_records_by_table.c
@@ -1336,19 +1336,23 @@ irbt_cb_table_purse_requests (struct PostgresClosure *pg,
/**
- * Function called with purse_refunds records to insert into table.
+ * Function called with purse_decision records to insert into table.
*
* @param pg plugin context
* @param td record to insert
*/
static enum GNUNET_DB_QueryStatus
-irbt_cb_table_purse_refunds (struct PostgresClosure *pg,
- const struct TALER_EXCHANGEDB_TableData *td)
+irbt_cb_table_purse_decision (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_auto_from_type (
- &td->details.purse_refunds.purse_pub),
+ &td->details.purse_decision.purse_pub),
+ GNUNET_PQ_query_param_timestamp (
+ &td->details.purse_decision.action_timestamp),
+ GNUNET_PQ_query_param_bool (
+ &td->details.purse_decision.refunded),
GNUNET_PQ_query_param_end
};
@@ -1357,10 +1361,12 @@ irbt_cb_table_purse_refunds (struct PostgresClosure *pg,
"INSERT INTO purse_refunds"
"(purse_refunds_serial_id"
",purse_pub"
+ ",action_timestamp"
+ ",refunded"
") VALUES "
- "($1, $2);");
+ "($1, $2, $3, $4);");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_into_table_purse_refunds",
+ "insert_into_table_purse_decision",
params);
}
@@ -1910,8 +1916,8 @@ TEH_PG_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
rh = &irbt_cb_table_purse_requests;
break;
- case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
- rh = &irbt_cb_table_purse_refunds;
+ case TALER_EXCHANGEDB_RT_PURSE_DECISION:
+ rh = &irbt_cb_table_purse_decision;
break;
case TALER_EXCHANGEDB_RT_PURSE_MERGES:
rh = &irbt_cb_table_purse_merges;
diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c
index dc1f17caa..b7435bc2b 100644
--- a/src/exchangedb/pg_lookup_records_by_table.c
+++ b/src/exchangedb/pg_lookup_records_by_table.c
@@ -1575,20 +1575,20 @@ lrbt_cb_table_purse_requests (void *cls,
/**
- * Function called with purse_refunds table entries.
+ * Function called with purse_decision 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_purse_refunds (void *cls,
- PGresult *result,
- unsigned int num_results)
+lrbt_cb_table_purse_decision (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
struct LookupRecordsByTableContext *ctx = cls;
struct TALER_EXCHANGEDB_TableData td = {
- .table = TALER_EXCHANGEDB_RT_PURSE_REFUNDS
+ .table = TALER_EXCHANGEDB_RT_PURSE_DECISION
};
for (unsigned int i = 0; i<num_results; i++)
@@ -1599,7 +1599,13 @@ lrbt_cb_table_purse_refunds (void *cls,
&td.serial),
GNUNET_PQ_result_spec_auto_from_type (
"purse_pub",
- &td.details.purse_refunds.purse_pub),
+ &td.details.purse_decision.purse_pub),
+ GNUNET_PQ_result_spec_timestamp (
+ "action_timestamp",
+ &td.details.purse_decision.action_timestamp),
+ GNUNET_PQ_result_spec_bool (
+ "refunded",
+ &td.details.purse_decision.refunded),
GNUNET_PQ_result_spec_end
};
@@ -2639,15 +2645,17 @@ TEH_PG_lookup_records_by_table (void *cls,
" ORDER BY purse_requests_serial_id ASC;");
rh = &lrbt_cb_table_purse_requests;
break;
- case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
- XPREPARE ("select_above_serial_by_table_purse_refunds",
+ case TALER_EXCHANGEDB_RT_PURSE_DECISION:
+ XPREPARE ("select_above_serial_by_table_purse_decision",
"SELECT"
- " purse_refunds_serial_id"
+ " purse_decision_serial_id"
+ ",action_timestamp"
+ ",refunded"
",purse_pub"
- " FROM purse_refunds"
- " WHERE purse_refunds_serial_id > $1"
- " ORDER BY purse_refunds_serial_id ASC;");
- rh = &lrbt_cb_table_purse_refunds;
+ " FROM purse_decision"
+ " WHERE purse_decision_serial_id > $1"
+ " ORDER BY purse_decision_serial_id ASC;");
+ rh = &lrbt_cb_table_purse_decision;
break;
case TALER_EXCHANGEDB_RT_PURSE_MERGES:
XPREPARE ("select_above_serial_by_table_purse_merges",
diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c
index b8d254791..202be30f8 100644
--- a/src/exchangedb/pg_lookup_serial_by_table.c
+++ b/src/exchangedb/pg_lookup_serial_by_table.c
@@ -293,12 +293,12 @@ TEH_PG_lookup_serial_by_table (void *cls,
" ORDER BY purse_requests_serial_id DESC"
" LIMIT 1;")
break;
- case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
- XPREPARE ("select_serial_by_table_purse_refunds",
+ case TALER_EXCHANGEDB_RT_PURSE_DECISION:
+ XPREPARE ("select_serial_by_table_purse_decision",
"SELECT"
- " purse_refunds_serial_id AS serial"
- " FROM purse_refunds"
- " ORDER BY purse_refunds_serial_id DESC"
+ " purse_decision_serial_id AS serial"
+ " FROM purse_decision"
+ " ORDER BY purse_decision_serial_id DESC"
" LIMIT 1;");
break;
case TALER_EXCHANGEDB_RT_PURSE_MERGES:
diff --git a/src/exchangedb/pg_select_reserve_closed_above_serial_id.c b/src/exchangedb/pg_select_reserve_closed_above_serial_id.c
new file mode 100644
index 000000000..985c6792c
--- /dev/null
+++ b/src/exchangedb/pg_select_reserve_closed_above_serial_id.c
@@ -0,0 +1,180 @@
+/*
+ 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 pg_select_reserve_closed_above_serial_id.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_get_reserve_history.h"
+#include "plugin_exchangedb_common.h"
+#include "pg_helper.h"
+
+/**
+ * Closure for #reserve_closed_serial_helper_cb().
+ */
+struct ReserveClosedSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_ReserveClosedCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin's context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * 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 ReserveClosedSerialContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+reserve_closed_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveClosedSerialContext *rcsc = cls;
+ struct PostgresClosure *pg = rcsc->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ uint64_t rowid;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ char *receiver_account;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount closing_fee;
+ struct GNUNET_TIME_Timestamp execution_date;
+ uint64_t close_request_row;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("close_uuid",
+ &rowid),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &reserve_pub),
+ GNUNET_PQ_result_spec_timestamp ("execution_date",
+ &execution_date),
+ GNUNET_PQ_result_spec_auto_from_type ("wtid",
+ &wtid),
+ GNUNET_PQ_result_spec_string ("receiver_account",
+ &receiver_account),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+ &amount_with_fee),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
+ &closing_fee),
+ GNUNET_PQ_result_spec_uint64 ("close_request_row",
+ &close_request_row),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_GenericReturnValue ret;
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ rcsc->status = GNUNET_SYSERR;
+ return;
+ }
+ ret = rcsc->cb (rcsc->cb_cls,
+ rowid,
+ execution_date,
+ &amount_with_fee,
+ &closing_fee,
+ &reserve_pub,
+ receiver_account,
+ &wtid,
+ close_request_row);
+ GNUNET_PQ_cleanup_result (rs);
+ if (GNUNET_OK != ret)
+ break;
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_reserve_closed_above_serial_id (
+ void *cls,
+ uint64_t serial_id,
+ TALER_EXCHANGEDB_ReserveClosedCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&serial_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct ReserveClosedSerialContext rcsc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ /* Used in #postgres_select_reserve_closed_above_serial_id() to
+ obtain information about closed reserves */
+ PREPARE (
+ pg,
+ "reserves_close_get_incr",
+ "SELECT"
+ " close_uuid"
+ ",reserves.reserve_pub"
+ ",execution_date"
+ ",wtid"
+ ",payto_uri AS receiver_account"
+ ",amount_val"
+ ",amount_frac"
+ ",closing_fee_val"
+ ",closing_fee_frac"
+ ",close_request_row"
+ " FROM reserves_close"
+ " JOIN wire_targets"
+ " USING (wire_target_h_payto)"
+ " JOIN reserves"
+ " USING (reserve_pub)"
+ " WHERE close_uuid>=$1"
+ " ORDER BY close_uuid ASC;");
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "reserves_close_get_incr",
+ params,
+ &reserve_closed_serial_helper_cb,
+ &rcsc);
+ if (GNUNET_OK != rcsc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_reserve_closed_above_serial_id.h b/src/exchangedb/pg_select_reserve_closed_above_serial_id.h
new file mode 100644
index 000000000..af3c8631e
--- /dev/null
+++ b/src/exchangedb/pg_select_reserve_closed_above_serial_id.h
@@ -0,0 +1,47 @@
+/*
+ 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 pg_select_reserve_closed_above_serial_id.h
+ * @brief implementation of the select_reserve_closed_above_serial_id function
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_RESERVE_CLOSED_ABOVE_SERIAL_ID_H
+#define PG_SELECT_RESERVE_CLOSED_ABOVE_SERIAL_ID_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Function called to select reserve close operations the aggregator
+ * triggered, ordered by serial ID (monotonically increasing).
+ *
+ * @param cls closure
+ * @param serial_id lowest serial ID to include (select larger or equal)
+ * @param cb function to call for ONE unfinished item
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_reserve_closed_above_serial_id (
+ void *cls,
+ uint64_t serial_id,
+ TALER_EXCHANGEDB_ReserveClosedCallback cb,
+ void *cb_cls);
+
+
+#endif
diff --git a/src/exchangedb/pg_select_reserve_open_above_serial_id.c b/src/exchangedb/pg_select_reserve_open_above_serial_id.c
new file mode 100644
index 000000000..cc33bc48c
--- /dev/null
+++ b/src/exchangedb/pg_select_reserve_open_above_serial_id.c
@@ -0,0 +1,169 @@
+/*
+ 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 pg_select_reserve_open_above_serial_id.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_reserve_open_above_serial_id.h"
+#include "plugin_exchangedb_common.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #reserve_open_serial_helper_cb().
+ */
+struct ReserveOpenSerialContext
+{
+
+ /**
+ * Callback to call.
+ */
+ TALER_EXCHANGEDB_ReserveOpenCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin's context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Status code, set to #GNUNET_SYSERR on hard errors.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * 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 ReserveOpenSerialContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+reserve_open_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct ReserveOpenSerialContext *rcsc = cls;
+ struct PostgresClosure *pg = rcsc->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ uint64_t rowid;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_ReserveSignatureP reserve_sig;
+ uint32_t requested_purse_limit;
+ struct GNUNET_TIME_Timestamp request_timestamp;
+ struct GNUNET_TIME_Timestamp reserve_expiration;
+ struct TALER_Amount reserve_payment;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("open_request_uuid",
+ &rowid),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &reserve_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+ &reserve_sig),
+ GNUNET_PQ_result_spec_timestamp ("request_timestamp",
+ &request_timestamp),
+ GNUNET_PQ_result_spec_timestamp ("expiration_date",
+ &reserve_expiration),
+ GNUNET_PQ_result_spec_uint32 ("requested_purse_limit",
+ &requested_purse_limit),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_payment",
+ &reserve_payment),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_GenericReturnValue ret;
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ rcsc->status = GNUNET_SYSERR;
+ return;
+ }
+ ret = rcsc->cb (rcsc->cb_cls,
+ rowid,
+ &reserve_payment,
+ request_timestamp,
+ reserve_expiration,
+ requested_purse_limit,
+ &reserve_pub,
+ &reserve_sig);
+ GNUNET_PQ_cleanup_result (rs);
+ if (GNUNET_OK != ret)
+ break;
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_reserve_open_above_serial_id (
+ void *cls,
+ uint64_t serial_id,
+ TALER_EXCHANGEDB_ReserveOpenCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&serial_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct ReserveOpenSerialContext rcsc = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (
+ pg,
+ "reserves_open_get_incr",
+ "SELECT"
+ " open_request_uuid"
+ ",reserve_pub"
+ ",request_timestamp"
+ ",expiration_date"
+ ",reserve_sig"
+ ",reserve_payment_val"
+ ",reserve_payment_frac"
+ ",requested_purse_limit"
+ " FROM reserves_open_requests"
+ " WHERE open_request_uuid>=$1"
+ " ORDER BY open_request_uuid ASC;");
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "reserves_open_get_incr",
+ params,
+ &reserve_open_serial_helper_cb,
+ &rcsc);
+ if (GNUNET_OK != rcsc.status)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/exchangedb/pg_select_reserve_open_above_serial_id.h b/src/exchangedb/pg_select_reserve_open_above_serial_id.h
new file mode 100644
index 000000000..4ec5b705a
--- /dev/null
+++ b/src/exchangedb/pg_select_reserve_open_above_serial_id.h
@@ -0,0 +1,47 @@
+/*
+ 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 pg_select_reserve_open_above_serial_id.h
+ * @brief implementation of the select_reserve_open_above_serial_id function
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_RESERVE_OPEN_ABOVE_SERIAL_ID_H
+#define PG_SELECT_RESERVE_OPEN_ABOVE_SERIAL_ID_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Function called to select reserve open operations, ordered by serial ID
+ * (monotonically increasing).
+ *
+ * @param cls closure
+ * @param serial_id lowest serial ID to include (select larger or equal)
+ * @param cb function to call for ONE unfinished item
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_reserve_open_above_serial_id (
+ void *cls,
+ uint64_t serial_id,
+ TALER_EXCHANGEDB_ReserveOpenCallback cb,
+ void *cb_cls);
+
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index af441d95d..c4957c912 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -44,6 +44,8 @@
#include "pg_lookup_records_by_table.h"
#include "pg_lookup_serial_by_table.h"
#include "pg_select_reserve_close_info.h"
+#include "pg_select_reserve_closed_above_serial_id.h"
+#include "pg_select_reserve_open_above_serial_id.h"
#include <poll.h>
#include <pthread.h>
#include <libpq-fe.h>
@@ -574,7 +576,8 @@ prepare_statements (struct PostgresClosure *pg)
",amount_frac"
",closing_fee_val"
",closing_fee_frac"
- ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8);"),
+ ",close_request_row"
+ ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);"),
/* Used in #postgres_insert_drain_profit() */
GNUNET_PQ_make_prepare (
"drain_profit_insert",
@@ -741,7 +744,7 @@ prepare_statements (struct PostgresClosure *pg)
" out_balance_ok AS balance_ok"
",out_conflict AS conflict"
" FROM exchange_do_purse_deposit"
- " ($1,$2,$3,$4,$5,$6,$7,$8,$9);"),
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);"),
/* Used in #postgres_update_aggregation_transient() */
GNUNET_PQ_make_prepare (
"set_purse_balance",
@@ -755,7 +758,7 @@ prepare_statements (struct PostgresClosure *pg)
"SELECT "
" out_found AS found"
" FROM exchange_do_expire_purse"
- " ($1,$2);"),
+ " ($1,$2,$3);"),
/* Used in #postgres_do_melt() to melt a coin. */
GNUNET_PQ_make_prepare (
"call_melt",
@@ -1304,15 +1307,21 @@ prepare_statements (struct PostgresClosure *pg)
" JOIN denominations denom USING (denominations_serial)"
" WHERE purse_pub=$1;"),
GNUNET_PQ_make_prepare (
- "audit_get_purse_refunds_incr",
+ "audit_get_purse_decisions_incr",
"SELECT"
- " purse_pub"
- ",purse_refunds_serial_id"
- " FROM purse_refunds"
+ " pd.purse_pub"
+ ",pm.reserve_pub"
+ ",pd.purse_decision_serial_id"
+ ",pr.amount_with_fee_val"
+ ",pr.amount_with_fee_frac"
+ " FROM purse_decision pd"
+ " JOIN purse_requests pr ON (pd.purse_pub = pr.purse_pub)"
+ " LEFT JOIN purse_merges pm ON (pm.purse_pub = pd.purse_pub)"
" WHERE ("
- " (purse_refunds_serial_id>=$1)"
+ " (purse_decision_serial_id>=$1) AND "
+ " (refunded=$2)"
" )"
- " ORDER BY purse_refunds_serial_id ASC;"),
+ " ORDER BY purse_decision_serial_id ASC;"),
/* Fetch an existing deposit request.
Used in #postgres_lookup_transfer_by_deposit(). */
GNUNET_PQ_make_prepare (
@@ -1845,27 +1854,6 @@ prepare_statements (struct PostgresClosure *pg)
" ON (old_coins.denominations_serial = old_denoms.denominations_serial)"
" WHERE recoup_refresh_uuid>=$1"
" ORDER BY recoup_refresh_uuid ASC;"),
- /* Used in #postgres_select_reserve_closed_above_serial_id() to
- obtain information about closed reserves */
- GNUNET_PQ_make_prepare (
- "reserves_close_get_incr",
- "SELECT"
- " close_uuid"
- ",reserves.reserve_pub"
- ",execution_date"
- ",wtid"
- ",payto_uri AS receiver_account"
- ",amount_val"
- ",amount_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
- " FROM reserves_close"
- " JOIN wire_targets"
- " USING (wire_target_h_payto)"
- " JOIN reserves"
- " USING (reserve_pub)"
- " WHERE close_uuid>=$1"
- " ORDER BY close_uuid ASC;"),
/* Used in #postgres_get_reserve_by_h_blind() */
GNUNET_PQ_make_prepare (
"reserve_by_h_blind",
@@ -2491,9 +2479,10 @@ prepare_statements (struct PostgresClosure *pg)
" FROM account_merges"
" JOIN purse_merges USING (purse_pub)"
" JOIN purse_requests USING (purse_pub)"
+ " JOIN purse_decision USING (purse_pub)"
" WHERE wallet_h_payto=$1"
" AND merge_timestamp >= $2"
- " AND finished"
+ " AND NOT refunded"
" ORDER BY merge_timestamp DESC"),
GNUNET_PQ_PREPARED_STATEMENT_END
@@ -6725,6 +6714,7 @@ postgres_insert_global_fee (void *cls,
* @param wtid wire transfer details
* @param amount_with_fee amount we charged to the reserve
* @param closing_fee how high is the closing fee
+ * @param close_request_row identifies explicit close request, 0 for none
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
@@ -6735,7 +6725,8 @@ postgres_insert_reserve_closed (
const char *receiver_account,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *amount_with_fee,
- const struct TALER_Amount *closing_fee)
+ const struct TALER_Amount *closing_fee,
+ uint64_t close_request_row)
{
struct PostgresClosure *pg = cls;
struct TALER_EXCHANGEDB_Reserve reserve;
@@ -6752,6 +6743,7 @@ postgres_insert_reserve_closed (
GNUNET_PQ_query_param_auto_from_type (&h_payto),
TALER_PQ_query_param_amount (amount_with_fee),
TALER_PQ_query_param_amount (closing_fee),
+ GNUNET_PQ_query_param_uint64 (&close_request_row),
GNUNET_PQ_query_param_end
};
@@ -7879,15 +7871,15 @@ postgres_select_history_requests_above_serial_id (
/**
- * Closure for #purse_refund_serial_helper_cb().
+ * Closure for #purse_decision_serial_helper_cb().
*/
-struct PurseRefundSerialContext
+struct PurseDecisionSerialContext
{
/**
* Callback to call.
*/
- TALER_EXCHANGEDB_PurseRefundCallback cb;
+ TALER_EXCHANGEDB_PurseDecisionCallback cb;
/**
* Closure for @e cb.
@@ -7915,19 +7907,29 @@ struct PurseRefundSerialContext
* @param num_results the number of results in @a result
*/
static void
-purse_refund_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
+purse_decision_serial_helper_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
{
- struct PurseRefundSerialContext *dsc = cls;
+ struct PurseDecisionSerialContext *dsc = cls;
+ struct PostgresClosure *pg = dsc->pg;
for (unsigned int i = 0; i<num_results; i++)
{
struct TALER_PurseContractPublicKeyP purse_pub;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ bool no_reserve = true;
uint64_t rowid;
+ struct TALER_Amount val;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
&purse_pub),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &reserve_pub),
+ &no_reserve),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+ &val),
GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
&rowid),
GNUNET_PQ_result_spec_end
@@ -7945,7 +7947,9 @@ purse_refund_serial_helper_cb (void *cls,
}
ret = dsc->cb (dsc->cb_cls,
rowid,
- &purse_pub);
+ &purse_pub,
+ no_reserve ? NULL : &reserve_pub,
+ &val);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
@@ -7954,28 +7958,31 @@ purse_refund_serial_helper_cb (void *cls,
/**
- * Select purse refunds above @a serial_id in monotonically increasing
+ * Select purse decisions above @a serial_id in monotonically increasing
* order.
*
* @param cls closure
* @param serial_id highest serial ID to exclude (select strictly larger)
+ * @param refunded which refund status to select for
* @param cb function to call on each result
* @param cb_cls closure for @a cb
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
-postgres_select_purse_refunds_above_serial_id (
+postgres_select_purse_decisions_above_serial_id (
void *cls,
uint64_t serial_id,
- TALER_EXCHANGEDB_PurseRefundCallback cb,
+ bool refunded,
+ TALER_EXCHANGEDB_PurseDecisionCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&serial_id),
+ GNUNET_PQ_query_param_bool (refunded),
GNUNET_PQ_query_param_end
};
- struct PurseRefundSerialContext dsc = {
+ struct PurseDecisionSerialContext dsc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg,
@@ -7984,9 +7991,9 @@ postgres_select_purse_refunds_above_serial_id (
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "audit_get_purse_refunds_incr",
+ "audit_get_purse_decisions_incr",
params,
- &purse_refund_serial_helper_cb,
+ &purse_decision_serial_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -9233,143 +9240,6 @@ postgres_select_recoup_refresh_above_serial_id (
/**
- * Closure for #reserve_closed_serial_helper_cb().
- */
-struct ReserveClosedSerialContext
-{
-
- /**
- * Callback to call.
- */
- TALER_EXCHANGEDB_ReserveClosedCallback cb;
-
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
-
- /**
- * Plugin's context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Status code, set to #GNUNET_SYSERR on hard errors.
- */
- enum GNUNET_GenericReturnValue status;
-};
-
-
-/**
- * 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 ReserveClosedSerialContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-reserve_closed_serial_helper_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct ReserveClosedSerialContext *rcsc = cls;
- struct PostgresClosure *pg = rcsc->pg;
-
- for (unsigned int i = 0; i<num_results; i++)
- {
- uint64_t rowid;
- struct TALER_ReservePublicKeyP reserve_pub;
- char *receiver_account;
- struct TALER_WireTransferIdentifierRawP wtid;
- struct TALER_Amount amount_with_fee;
- struct TALER_Amount closing_fee;
- struct GNUNET_TIME_Timestamp execution_date;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("close_uuid",
- &rowid),
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- GNUNET_PQ_result_spec_timestamp ("execution_date",
- &execution_date),
- GNUNET_PQ_result_spec_auto_from_type ("wtid",
- &wtid),
- GNUNET_PQ_result_spec_string ("receiver_account",
- &receiver_account),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
- &amount_with_fee),
- TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
- &closing_fee),
- GNUNET_PQ_result_spec_end
- };
- enum GNUNET_GenericReturnValue ret;
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- rcsc->status = GNUNET_SYSERR;
- return;
- }
- ret = rcsc->cb (rcsc->cb_cls,
- rowid,
- execution_date,
- &amount_with_fee,
- &closing_fee,
- &reserve_pub,
- receiver_account,
- &wtid);
- GNUNET_PQ_cleanup_result (rs);
- if (GNUNET_OK != ret)
- break;
- }
-}
-
-
-/**
- * Function called to select reserve close operations the aggregator
- * triggered, ordered by serial ID (monotonically increasing).
- *
- * @param cls closure
- * @param serial_id lowest serial ID to include (select larger or equal)
- * @param cb function to call for ONE unfinished item
- * @param cb_cls closure for @a cb
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_select_reserve_closed_above_serial_id (
- void *cls,
- uint64_t serial_id,
- TALER_EXCHANGEDB_ReserveClosedCallback cb,
- void *cb_cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&serial_id),
- GNUNET_PQ_query_param_end
- };
- struct ReserveClosedSerialContext rcsc = {
- .cb = cb,
- .cb_cls = cb_cls,
- .pg = pg,
- .status = GNUNET_OK
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "reserves_close_get_incr",
- params,
- &reserve_closed_serial_helper_cb,
- &rcsc);
- if (GNUNET_OK != rcsc.status)
- return GNUNET_DB_STATUS_HARD_ERROR;
- return qs;
-}
-
-
-/**
* Obtain information about which reserve a coin was generated
* from given the hash of the blinded coin.
*
@@ -11639,9 +11509,11 @@ postgres_expire_purse (
struct GNUNET_TIME_Absolute end_time)
{
struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_absolute_time (&start_time),
GNUNET_PQ_query_param_absolute_time (&end_time),
+ GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end
};
bool found = false;
@@ -11806,6 +11678,7 @@ postgres_do_purse_deposit (
bool *conflict)
{
struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
struct GNUNET_TIME_Timestamp reserve_expiration;
uint64_t partner_id = 0; /* FIXME #7271: WAD support... */
struct GNUNET_PQ_QueryParam params[] = {
@@ -11816,6 +11689,7 @@ postgres_do_purse_deposit (
GNUNET_PQ_query_param_auto_from_type (coin_sig),
TALER_PQ_query_param_amount (amount_minus_fee),
GNUNET_PQ_query_param_timestamp (&reserve_expiration),
+ GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
@@ -13166,8 +13040,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_purse_merges_above_serial_id;
plugin->select_history_requests_above_serial_id
= &postgres_select_history_requests_above_serial_id;
- plugin->select_purse_refunds_above_serial_id
- = &postgres_select_purse_refunds_above_serial_id;
+ plugin->select_purse_decisions_above_serial_id
+ = &postgres_select_purse_decisions_above_serial_id;
plugin->select_purse_deposits_by_purse
= &postgres_select_purse_deposits_by_purse;
plugin->select_refreshes_above_serial_id
@@ -13188,8 +13062,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_recoup_above_serial_id;
plugin->select_recoup_refresh_above_serial_id
= &postgres_select_recoup_refresh_above_serial_id;
- plugin->select_reserve_closed_above_serial_id
- = &postgres_select_reserve_closed_above_serial_id;
plugin->get_reserve_by_h_blind
= &postgres_get_reserve_by_h_blind;
plugin->get_old_coin_by_h_blind
@@ -13347,6 +13219,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_lookup_serial_by_table;
plugin->select_reserve_close_info
= &TEH_PG_select_reserve_close_info;
+ plugin->select_reserve_closed_above_serial_id
+ = &TEH_PG_select_reserve_closed_above_serial_id;
+ plugin->select_reserve_open_above_serial_id
+ = &TEH_PG_select_reserve_open_above_serial_id;
return plugin;
}
diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql
index 912a37dbc..e9075775c 100644
--- a/src/exchangedb/procedures.sql
+++ b/src/exchangedb/procedures.sql
@@ -1428,6 +1428,7 @@ CREATE OR REPLACE FUNCTION exchange_do_purse_deposit(
IN in_amount_without_fee_val INT8,
IN in_amount_without_fee_frac INT4,
IN in_reserve_expiration INT8,
+ IN in_now INT8,
OUT out_balance_ok BOOLEAN,
OUT out_conflict BOOLEAN)
LANGUAGE plpgsql
@@ -1443,6 +1444,8 @@ DECLARE
DECLARE
was_paid BOOLEAN;
DECLARE
+ my_in_reserve_quota BOOLEAN;
+DECLARE
my_reserve_pub BYTEA;
BEGIN
@@ -1548,9 +1551,11 @@ END IF;
SELECT
amount_with_fee_val
,amount_with_fee_frac
+ ,in_reserve_quota
INTO
my_amount_val
,my_amount_frac
+ ,my_in_reserve_quota
FROM exchange.purse_requests
WHERE (purse_pub=in_purse_pub)
AND ( ( ( (amount_with_fee_val <= balance_val)
@@ -1561,6 +1566,28 @@ THEN
RETURN;
END IF;
+-- Remember how this purse was finished.
+INSERT INTO purse_decision
+ (purse_pub
+ ,action_timestamp
+ ,refunded)
+VALUES
+ (in_purse_pub
+ ,in_now
+ ,FALSE);
+
+IF (my_in_reserve_quota)
+THEN
+ UPDATE reserves
+ SET purses_active=purses_active-1
+ WHERE reserve_pub IN
+ (SELECT reserve_pub
+ FROM exchange.purse_merges
+ WHERE purse_pub=my_purse_pub
+ LIMIT 1);
+END IF;
+
+
IF (0 != psi)
THEN
-- The taler-exchange-router will take care of this.
@@ -1606,11 +1633,6 @@ ELSE
WHERE reserve_pub=my_reserve_pub;
END IF;
- -- ... and mark purse as finished.
- -- FIXME: combine with UPDATE above?
- UPDATE purse_requests
- SET finished=true
- WHERE purse_pub=in_purse_pub;
END IF;
@@ -1644,7 +1666,7 @@ DECLARE
DECLARE
my_partner_serial_id INT8;
DECLARE
- my_finished BOOLEAN;
+ my_in_reserve_quota BOOLEAN;
BEGIN
IF in_partner_url IS NULL
@@ -1675,12 +1697,12 @@ SELECT amount_with_fee_val
,amount_with_fee_frac
,purse_fee_val
,purse_fee_frac
- ,finished
+ ,in_reserve_quota
INTO my_amount_val
,my_amount_frac
,my_purse_fee_val
,my_purse_fee_frac
- ,my_finished
+ ,my_in_reserve_quota
FROM exchange.purse_requests
WHERE purse_pub=in_purse_pub
AND balance_val >= amount_with_fee_val
@@ -1731,8 +1753,6 @@ THEN
END IF;
out_conflict=FALSE;
-ASSERT NOT my_finished, 'internal invariant failed';
-
-- Initialize reserve, if not yet exists.
INSERT INTO reserves
@@ -1745,8 +1765,26 @@ INSERT INTO reserves
,in_expiration_date)
ON CONFLICT DO NOTHING;
+-- Remember how this purse was finished.
+INSERT INTO purse_decision
+ (purse_pub
+ ,action_timestamp
+ ,refunded)
+VALUES
+ (in_purse_pub
+ ,in_merge_timestamp
+ ,FALSE);
-
+IF (my_in_reserve_quota)
+THEN
+ UPDATE reserves
+ SET purses_active=purses_active-1
+ WHERE reserve_pub IN
+ (SELECT reserve_pub
+ FROM exchange.purse_merges
+ WHERE purse_pub=my_purse_pub
+ LIMIT 1);
+END IF;
-- Store account merge signature.
INSERT INTO exchange.account_merges
@@ -1794,10 +1832,6 @@ ELSE
END
WHERE reserve_pub=in_reserve_pub;
- -- ... and mark purse as finished.
- UPDATE exchange.purse_requests
- SET finished=true
- WHERE purse_pub=in_purse_pub;
END IF;
@@ -1969,6 +2003,7 @@ END $$;
CREATE OR REPLACE FUNCTION exchange_do_expire_purse(
IN in_start_time INT8,
IN in_end_time INT8,
+ IN in_now INT8,
OUT out_found BOOLEAN)
LANGUAGE plpgsql
AS $$
@@ -1976,15 +2011,21 @@ DECLARE
my_purse_pub BYTEA;
DECLARE
my_deposit record;
+DECLARE
+ my_in_reserve_quota BOOLEAN;
BEGIN
+-- FIXME: we should probably do this in a loop
+-- and expire all at once, instead of one per query
SELECT purse_pub
+ ,in_reserve_quota
INTO my_purse_pub
+ ,my_in_reserve_quota
FROM exchange.purse_requests
WHERE (purse_expiration >= in_start_time) AND
(purse_expiration < in_end_time) AND
- (NOT finished) AND
- (NOT refunded)
+ purse_pub NOT IN (SELECT purse_pub
+ FROM purse_decision)
ORDER BY purse_expiration ASC
LIMIT 1;
out_found = FOUND;
@@ -1993,15 +2034,25 @@ THEN
RETURN;
END IF;
-UPDATE exchange.purse_requests
- SET refunded=TRUE,
- finished=TRUE
- WHERE purse_pub=my_purse_pub;
+INSERT INTO purse_decision
+ (purse_pub
+ ,action_timestamp
+ ,refunded)
+VALUES
+ (my_purse_pub
+ ,in_now
+ ,TRUE);
-INSERT INTO exchange.purse_refunds
- (purse_pub)
- VALUES
- (my_purse_pub);
+IF (my_in_reserve_quota)
+THEN
+ UPDATE reserves
+ SET purses_active=purses_active-1
+ WHERE reserve_pub IN
+ (SELECT reserve_pub
+ FROM exchange.purse_merges
+ WHERE purse_pub=my_purse_pub
+ LIMIT 1);
+END IF;
-- restore balance to each coin deposited into the purse
FOR my_deposit IN
@@ -2028,7 +2079,7 @@ LOOP
END LOOP;
END $$;
-COMMENT ON FUNCTION exchange_do_expire_purse(INT8,INT8)
+COMMENT ON FUNCTION exchange_do_expire_purse(INT8,INT8,INT8)
IS 'Finds an expired purse in the given time range and refunds the coins (if any).';
diff --git a/src/exchangedb/shard-0001-part.sql b/src/exchangedb/shard-0001-part.sql
index a54eb8dc8..8b6ec07f9 100644
--- a/src/exchangedb/shard-0001-part.sql
+++ b/src/exchangedb/shard-0001-part.sql
@@ -101,8 +101,8 @@ BEGIN
PERFORM create_table_purse_requests(shard_suffix);
PERFORM add_constraints_to_purse_requests_partition(shard_suffix);
- PERFORM create_table_purse_refunds(shard_suffix);
- PERFORM add_constraints_to_purse_refunds_partition(shard_suffix);
+ PERFORM create_table_purse_decision(shard_suffix);
+ PERFORM add_constraints_to_purse_decision_partition(shard_suffix);
PERFORM create_table_purse_merges(shard_suffix);
PERFORM add_constraints_to_purse_merges_partition(shard_suffix);
diff --git a/src/exchangedb/test-exchange-db-postgres.conf b/src/exchangedb/test-exchange-db-postgres.conf
index ab70bcfce..05104cb7f 100644
--- a/src/exchangedb/test-exchange-db-postgres.conf
+++ b/src/exchangedb/test-exchange-db-postgres.conf
@@ -30,4 +30,7 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
LEGAL_RESERVE_EXPIRATION_TIME = 7 years
# Shift to apply before aggregating.
-AGGREGATOR_SHIFT = 1s \ No newline at end of file
+AGGREGATOR_SHIFT = 1s
+
+# Number of purses per account by default.
+DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index d19f91a44..77f3dab26 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1809,7 +1809,8 @@ run (void *cls)
sndr,
&wire_out_wtid,
&amount_with_fee,
- &fee_closing));
+ &fee_closing,
+ 0));
FAILIF (GNUNET_OK !=
check_reserve (&reserve_pub2,
0,
@@ -1823,7 +1824,8 @@ run (void *cls)
sndr,
&wire_out_wtid,
&value,
- &fee_closing));
+ &fee_closing,
+ 0));
FAILIF (GNUNET_OK !=
check_reserve (&reserve_pub,
0,