commit 03b5285dd6f7f5a23a2c53b99f3ab3721bf033b1
parent 2f28326170fe9fef8cadafe07c3f975b901dd573
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 17 Oct 2026 16:01:18 +0200
allow appending additional revenue to denomination
Diffstat:
6 files changed, 183 insertions(+), 1 deletion(-)
diff --git a/src/auditordb/0005-auditor_historic_denomination_revenue.sql b/src/auditordb/0005-auditor_historic_denomination_revenue.sql
@@ -0,0 +1,35 @@
+--
+-- 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/>
+--
+
+-- A denomination can be credited after the auditor booked its remaining
+-- balance as revenue: an expiring purse refunds the coins deposited into
+-- it, and /recoup-refresh credits the old coin of a refresh, neither of
+-- which checks the expiration of the denomination. The additional
+-- revenue has to be recorded without rewriting a row that
+-- /monitoring/historic-denomination-revenue may already have handed out
+-- -- that endpoint pages the table by row_id, so a consumer that is past
+-- a row would never see the change. The table therefore becomes
+-- append-only: row_id is the primary key and denom_pub_hash is merely
+-- indexed, so that a late settlement is a new, final row.
+ALTER TABLE auditor_historic_denomination_revenue
+ DROP CONSTRAINT auditor_historic_denomination_revenue_pkey
+ ,DROP CONSTRAINT auditor_historic_denomination_revenue_row_id_key
+ ,ADD PRIMARY KEY (row_id);
+CREATE INDEX auditor_historic_denomination_revenue_by_denom_pub_hash
+ ON auditor_historic_denomination_revenue
+ (denom_pub_hash);
+COMMENT ON COLUMN auditor_historic_denomination_revenue.denom_pub_hash
+ IS 'hash of the denomination public key that created this revenue; since auditor-0005 there can be more than one row per denomination, as a credit that arrives after the denomination was booked as finished is appended instead of changing the row that was booked';
diff --git a/src/auditordb/0005-preamble.sql b/src/auditordb/0005-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-0005', NULL, NULL);
+
+SET search_path TO auditor;
diff --git a/src/auditordb/get_exists_historic_denom_revenue.c b/src/auditordb/get_exists_historic_denom_revenue.c
@@ -0,0 +1,51 @@
+/*
+ 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/>
+ */
+/**
+ * @file get_exists_historic_denom_revenue.c
+ * @brief Low-level (statement-level) Postgres database access for the auditor
+ * @author Christian Grothoff
+ */
+#include "taler/taler_pq_lib.h"
+#include "auditor-database/get_exists_historic_denom_revenue.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TALER_AUDITORDB_get_exists_historic_denom_revenue (
+ struct TALER_AUDITORDB_PostgresContext *pg,
+ const struct TALER_DenominationHashP *denom_pub_hash)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "get_exists_historic_denom_revenue",
+ "SELECT"
+ " 1"
+ " FROM auditor_historic_denomination_revenue"
+ " WHERE denom_pub_hash=$1"
+ " LIMIT 1;");
+ return GNUNET_PQ_eval_prepared_singleton_select (
+ pg->conn,
+ "get_exists_historic_denom_revenue",
+ params,
+ rs);
+}
diff --git a/src/auditordb/meson.build b/src/auditordb/meson.build
@@ -67,6 +67,12 @@ auditor_0004_sql = [
'commit.sql',
]
+auditor_0005_sql = [
+ '0005-preamble.sql',
+ '0005-auditor_historic_denomination_revenue.sql',
+ 'commit.sql',
+]
+
generated_sql = [
['versioning.sql', ['versioning.sql']],
['drop.sql', ['drop.sql']],
@@ -76,6 +82,7 @@ generated_sql = [
['auditor-0002.sql', auditor_0002_sql],
['auditor-0003.sql', auditor_0003_sql],
['auditor-0004.sql', auditor_0004_sql],
+ ['auditor-0005.sql', auditor_0005_sql],
]
foreach g : generated_sql
@@ -120,6 +127,7 @@ libtalerauditordb = library(
'iterate_balances.c',
'iterate_coin_inconsistencies.c',
'get_denomination_balance.c',
+ 'get_exists_historic_denom_revenue.c',
'iterate_denomination_key_validity_withdraw_inconsistencies.c',
'iterate_denomination_pending.c',
'iterate_denominations_without_sigs.c',
diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c
@@ -27,6 +27,7 @@
#include "auditor-database/drop_tables.h"
#include "auditor-database/gc.h"
#include "auditor-database/get_denomination_balance.h"
+#include "auditor-database/get_exists_historic_denom_revenue.h"
#include "auditor-database/get_reserve_info.h"
#include "auditor-database/insert_auditor_closure_lags.h"
#include "auditor-database/insert_denomination_balance.h"
@@ -134,7 +135,7 @@ select_historic_denom_revenue_result (
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"select_historic_denom_revenue_result: row %u\n", n);
- if ( (2 <= n++)
+ if ( (3 <= n++)
|| (cls != NULL)
|| ((0 != GNUNET_memcmp (&revenue_timestamp2,
&past))
@@ -461,6 +462,28 @@ run (void *cls)
now,
&rbalance,
&rloss));
+ /* The table is append-only: a denomination that is credited after it was
+ booked as finished gets another row instead of an update. */
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ TALER_AUDITORDB_insert_historic_denom_revenue (pg,
+ &denom_pub_hash,
+ now,
+ &rbalance,
+ &rloss));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Test: get_exists_historic_denom_revenue\n");
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ TALER_AUDITORDB_get_exists_historic_denom_revenue (pg,
+ &denom_pub_hash));
+ {
+ struct TALER_DenominationHashP unknown_hash;
+
+ RND_BLK (&unknown_hash);
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ TALER_AUDITORDB_get_exists_historic_denom_revenue (
+ pg,
+ &unknown_hash));
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Test: select_historic_denom_revenue\n");
FAILIF (0 >=
diff --git a/src/include/auditor-database/get_exists_historic_denom_revenue.h b/src/include/auditor-database/get_exists_historic_denom_revenue.h
@@ -0,0 +1,44 @@
+/*
+ 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/>
+ */
+/**
+ * @file get_exists_historic_denom_revenue.h
+ * @brief implementation of the get_exists_historic_denom_revenue function
+ * @author Christian Grothoff
+ */
+#ifndef AUDITOR_DATABASE_GET_EXISTS_HISTORIC_DENOM_REVENUE_H
+#define AUDITOR_DATABASE_GET_EXISTS_HISTORIC_DENOM_REVENUE_H
+
+#include "taler/taler_json_lib.h"
+#include "auditordb_lib.h"
+
+
+/**
+ * Check whether the remaining balance of a denomination was already booked
+ * as revenue, which is the case if and only if the denomination has a
+ * historic revenue row. The table is append-only, so there may be more
+ * than one such row.
+ *
+ * @param pg the database context
+ * @param denom_pub_hash hash of the denomination public key
+ * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the denomination was
+ * booked as finished, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not
+ */
+enum GNUNET_DB_QueryStatus
+TALER_AUDITORDB_get_exists_historic_denom_revenue (
+ struct TALER_AUDITORDB_PostgresContext *pg,
+ const struct TALER_DenominationHashP *denom_pub_hash);
+
+#endif