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:
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