exchange

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

commit e2d1d1df45ced27b6ad98b59e15d4d60cfdaf3a2
parent 527a9a6157fb13601365ad3d22676d95f6240140
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  8 Aug 2026 21:56:41 +0200

add missing UNIQUE constraint on auditor_pending_deposits per batch deposit

Diffstat:
Msrc/auditor/taler-helper-auditor-transfer.c | 14++++++++------
Asrc/auditordb/0004-auditor_kycauth_in_inconsistency.sql | 23+++++++++++++++++++++++
Asrc/auditordb/0004-auditor_pending_deposits.sql | 30++++++++++++++++++++++++++++++
Asrc/auditordb/0004-auditor_purse_not_closed_inconsistencies.sql | 29+++++++++++++++++++++++++++++
Asrc/auditordb/0004-auditor_reserve_in_inconsistency.sql | 23+++++++++++++++++++++++
Asrc/auditordb/0004-preamble.sql | 21+++++++++++++++++++++
Msrc/auditordb/insert_purse_not_closed_inconsistencies.c | 16+++++++++-------
Msrc/auditordb/meson.build | 2++
Msrc/auditordb/test_auditordb_regression.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/auditor-database/insert_purse_not_closed_inconsistencies.h | 4+++-
10 files changed, 217 insertions(+), 14 deletions(-)

diff --git a/src/auditor/taler-helper-auditor-transfer.c b/src/auditor/taler-helper-auditor-transfer.c @@ -240,9 +240,10 @@ import_wire_missing_cb ( wc->err = qs; return; } - TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_amount_lag), - &TALER_ARL_USE_AB (total_amount_lag), - total_amount); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_amount_lag), + &TALER_ARL_USE_AB (total_amount_lag), + total_amount); break; case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_early_aggregation), @@ -353,9 +354,10 @@ clear_finished_transfer_cb ( ac->err = qs; return; } - TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_early_aggregation), - &TALER_ARL_USE_AB (total_early_aggregation), - amount); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_early_aggregation), + &TALER_ARL_USE_AB (total_early_aggregation), + amount); break; case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_amount_lag), diff --git a/src/auditordb/0004-auditor_kycauth_in_inconsistency.sql b/src/auditordb/0004-auditor_kycauth_in_inconsistency.sql @@ -0,0 +1,23 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2026 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/> +-- + +-- TALER_AUDITORDB_get_kycauth_in_inconsistency() looks up the most recent +-- finding for a bank row. bank_row_id is not unique, so support the +-- "ORDER BY row_id DESC LIMIT 1" lookup with an index. +CREATE INDEX IF NOT EXISTS auditor_kycauth_in_inconsistency_by_bank_row_id + ON auditor_kycauth_in_inconsistency + (bank_row_id ASC + ,row_id DESC); diff --git a/src/auditordb/0004-auditor_pending_deposits.sql b/src/auditordb/0004-auditor_pending_deposits.sql @@ -0,0 +1,30 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2026 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/> +-- + +-- delete_pending_deposit() deletes by batch_deposit_serial_id and its +-- caller (clear_finished_transfer_cb) asserts that at most one row was +-- removed, but insert_pending_deposit() had no dedup and the column had +-- no unique index. Drop the duplicates (keeping the most recent report +-- for each deposit) and constrain the column. +DELETE FROM auditor_pending_deposits ap + WHERE ap.row_id < + (SELECT MAX(ap2.row_id) + FROM auditor_pending_deposits ap2 + WHERE ap2.batch_deposit_serial_id=ap.batch_deposit_serial_id); + +ALTER TABLE auditor_pending_deposits + ADD CONSTRAINT auditor_pending_deposits_batch_deposit_serial_id_key + UNIQUE (batch_deposit_serial_id); diff --git a/src/auditordb/0004-auditor_purse_not_closed_inconsistencies.sql b/src/auditordb/0004-auditor_purse_not_closed_inconsistencies.sql @@ -0,0 +1,29 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2026 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/> +-- + +-- handle_purse_expired() re-reports every expired purse on every auditor +-- round and the INSERT had no dedup, so the table grew by one row per +-- purse per round for the ten years until GC removes the purse. Keep +-- one report per purse. +DELETE FROM auditor_purse_not_closed_inconsistencies pnc + WHERE pnc.row_id < + (SELECT MAX(pnc2.row_id) + FROM auditor_purse_not_closed_inconsistencies pnc2 + WHERE pnc2.purse_pub=pnc.purse_pub); + +ALTER TABLE auditor_purse_not_closed_inconsistencies + ADD CONSTRAINT auditor_purse_not_closed_inconsistencies_purse_pub_key + UNIQUE (purse_pub); diff --git a/src/auditordb/0004-auditor_reserve_in_inconsistency.sql b/src/auditordb/0004-auditor_reserve_in_inconsistency.sql @@ -0,0 +1,23 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2026 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/> +-- + +-- TALER_AUDITORDB_get_reserve_in_inconsistency() looks up the most recent +-- finding for a bank row. bank_row_id is not unique, so support the +-- "ORDER BY row_id DESC LIMIT 1" lookup with an index. +CREATE INDEX IF NOT EXISTS auditor_reserve_in_inconsistency_by_bank_row_id + ON auditor_reserve_in_inconsistency + (bank_row_id ASC + ,row_id DESC); diff --git a/src/auditordb/0004-preamble.sql b/src/auditordb/0004-preamble.sql @@ -0,0 +1,21 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2026 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/> +-- + +BEGIN; + +SELECT _v.register_patch('auditor-0004', NULL, NULL); + +SET search_path TO auditor; diff --git a/src/auditordb/insert_purse_not_closed_inconsistencies.c b/src/auditordb/insert_purse_not_closed_inconsistencies.c @@ -19,13 +19,9 @@ enum GNUNET_DB_QueryStatus -TALER_AUDITORDB_insert_purse_not_closed_inconsistencies (struct - TALER_AUDITORDB_PostgresContext - * - ctx, - const struct - TALER_AUDITORDB_PurseNotClosedInconsistencies - *dc) +TALER_AUDITORDB_insert_purse_not_closed_inconsistencies ( + struct TALER_AUDITORDB_PostgresContext *ctx, + const struct TALER_AUDITORDB_PurseNotClosedInconsistencies *dc) { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (&dc->purse_pub), @@ -42,6 +38,12 @@ TALER_AUDITORDB_insert_purse_not_closed_inconsistencies (struct ",amount" ",expiration_date" ") VALUES ($1,$2,$3)" + /* The purse auditor re-reports every expired purse on every + round; without this the table grows by one row per purse + per round until GC removes the purse ten years later. */ + " ON CONFLICT (purse_pub) DO UPDATE" + " SET amount=excluded.amount" + " ,expiration_date=excluded.expiration_date" ); return GNUNET_PQ_eval_prepared_non_select (ctx->conn, "insert_purse_not_closed_inconsistencies", diff --git a/src/auditordb/meson.build b/src/auditordb/meson.build @@ -62,6 +62,8 @@ auditor_0004_sql = [ '0004-preamble.sql', '0004-auditor_reserve_in_inconsistency.sql', '0004-auditor_kycauth_in_inconsistency.sql', + '0004-auditor_pending_deposits.sql', + '0004-auditor_purse_not_closed_inconsistencies.sql', 'commit.sql', ] diff --git a/src/auditordb/test_auditordb_regression.c b/src/auditordb/test_auditordb_regression.c @@ -31,6 +31,8 @@ #include "auditor-database/get_balance.h" #include "auditor-database/insert_auditor_progress.h" #include "auditor-database/insert_balance.h" +#include "auditor-database/insert_purse_not_closed_inconsistencies.h" +#include "auditor-database/iterate_purse_not_closed_inconsistencies.h" #include "auditor-database/preflight.h" #include "auditor-database/start.h" @@ -176,6 +178,70 @@ test_checkpoint_upsert (void) /** + * Callback for #test_purse_not_closed_dedup(), keeps the last row seen. + * + * @param cls a `struct TALER_AUDITORDB_PurseNotClosedInconsistencies *` + * @param dc the row + * @return #GNUNET_OK + */ +static enum GNUNET_GenericReturnValue +purse_not_closed_cb ( + void *cls, + const struct TALER_AUDITORDB_PurseNotClosedInconsistencies *dc) +{ + struct TALER_AUDITORDB_PurseNotClosedInconsistencies *last = cls; + + *last = *dc; + return GNUNET_OK; +} + + +/** + * A-5: the purse auditor re-reports every expired purse on every round. + * Without dedup on purse_pub the report table grows without bound. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +test_purse_not_closed_dedup (void) +{ + struct TALER_AUDITORDB_PurseNotClosedInconsistencies pnc = { + .expiration_date = GNUNET_TIME_absolute_get () + }; + struct TALER_AUDITORDB_PurseNotClosedInconsistencies last = { 0 }; + struct TALER_Amount three; + + GNUNET_CRYPTO_random_block (&pnc.purse_pub, + sizeof (pnc.purse_pub)); + amount ("1", + &pnc.amount); + amount ("3", + &three); + for (unsigned int round = 0; round < 3; round++) + { + if (2 == round) + pnc.amount = three; + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + TALER_AUDITORDB_insert_purse_not_closed_inconsistencies (pg, + &pnc)); + } + /* Three rounds, one purse, one row -- carrying the latest amount. */ + FAILIF (1 != + TALER_AUDITORDB_iterate_purse_not_closed_inconsistencies ( + pg, + 1024, + 0, + true, + &purse_not_closed_cb, + &last)); + FAILIF (0 != + TALER_amount_cmp (&last.amount, + &three)); + return GNUNET_OK; +} + + +/** * Main function that will be run by the scheduler. * * @param cls closure with the configuration @@ -225,6 +291,9 @@ run (void *cls) if (GNUNET_OK != test_checkpoint_upsert ()) goto rollback; + if (GNUNET_OK != + test_purse_not_closed_dedup ()) + goto rollback; result = 0; GNUNET_break (0 <= TALER_AUDITORDB_commit (pg)); diff --git a/src/include/auditor-database/insert_purse_not_closed_inconsistencies.h b/src/include/auditor-database/insert_purse_not_closed_inconsistencies.h @@ -21,7 +21,9 @@ /** - * Insert information about a purse not closed inconsistencies into the database. + * Insert information about a purse not closed inconsistencies into the + * database. There is at most one report per purse: an existing report + * for the same purse is overwritten. * * @param pg the database context * @param dc deposit confirmation information to store