exchange

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

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:
Asrc/auditordb/0005-auditor_historic_denomination_revenue.sql | 35+++++++++++++++++++++++++++++++++++
Asrc/auditordb/0005-preamble.sql | 21+++++++++++++++++++++
Asrc/auditordb/get_exists_historic_denom_revenue.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/auditordb/meson.build | 8++++++++
Msrc/auditordb/test_auditordb.c | 25++++++++++++++++++++++++-
Asrc/include/auditor-database/get_exists_historic_denom_revenue.h | 44++++++++++++++++++++++++++++++++++++++++++++
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