commit 208d112a6ec33adebc7ab6b08a0b400fb5314bc8
parent 0da2155fc1017a211eb4d33cc5a0bc550c41d8b4
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 7 Aug 2026 00:23:30 +0200
fix theoretical issue with duplicate receipt_id
Diffstat:
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/src/donau/donau-httpd_post-batch-issue-CHARITY_ID.c b/src/donau/donau-httpd_post-batch-issue-CHARITY_ID.c
@@ -530,7 +530,25 @@ start:
TALER_EC_GENERIC_DB_FETCH_FAILED,
NULL);
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_assert (! second_time);
+ /* Some unique violation: normally the receipt_hash, i.e. a concurrent
+ idempotent request whose result we serve from the database below.
+ It can also be a database-state anomaly (e.g. a desynchronised
+ receipts_issued identity sequence), in which case the re-read finds
+ nothing and we end up here a second time. That is a 500, not a
+ reason to abort() the whole daemon. */
+ if (second_time)
+ {
+ GNUNET_break (0);
+ free_bkps (num_bkps,
+ bkps);
+ json_decref (blind_signatures);
+ for (unsigned int i = 0; i<num_bkps; i++)
+ GNUNET_CRYPTO_blinded_sig_decref (du_sigs[i].blinded_sig);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ "receipts_issued");
+ }
second_time = true;
for (unsigned int i = 0; i<num_bkps; i++)
GNUNET_CRYPTO_blinded_sig_decref (du_sigs[i].blinded_sig);
diff --git a/src/donaudb/donau-0004.sql b/src/donaudb/donau-0004.sql
@@ -0,0 +1,32 @@
+--
+-- 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('donau-0004', NULL, NULL);
+SET search_path TO donau;
+
+-- receipts_issued.receipt_id was GENERATED BY DEFAULT AS IDENTITY, which
+-- permits explicit inserts (bulk load, partial restore, ad-hoc migration)
+-- and thus permits leaving the identity sequence behind the largest stored
+-- value. Every subsequent insert then fails with a unique violation on
+-- receipts_issued_receipt_id_key -- which donau-httpd cannot distinguish
+-- from "this receipt_hash already exists". The value is never supplied by
+-- the daemon, so make it unforgeable from the outside.
+ALTER TABLE receipts_issued
+ ALTER COLUMN receipt_id SET GENERATED ALWAYS;
+
+COMMIT;
diff --git a/src/donaudb/meson.build b/src/donaudb/meson.build
@@ -32,6 +32,7 @@ generated_sql = [
['donau-0001.sql', ['donau-0001.sql']],
['donau-0002.sql', donau_0002],
['donau-0003.sql', ['donau-0003.sql']],
+ ['donau-0004.sql', ['donau-0004.sql']],
]