commit 435ef8d5a318614508439de3432cc1f709c3e116
parent 3827cb37eb3518647aa4164dafdf8d612fea9166
Author: Antoine A <>
Date: Fri, 2 Oct 2026 14:06:53 +0200
common: sync taler-rust and incoming history ordering fix
Diffstat:
7 files changed, 101 insertions(+), 16 deletions(-)
diff --git a/database-versioning/libeufin-bank-0016.sql b/database-versioning/libeufin-bank-0016.sql
@@ -0,0 +1,34 @@
+--
+-- 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('libeufin-bank-0016', NULL, NULL);
+
+SET search_path TO libeufin_bank;
+
+-- Preserve existing incoming-history IDs and persisted consumer cursors.
+ALTER TABLE taler_exchange_incoming
+ DROP CONSTRAINT taler_exchange_incoming_exchange_incoming_id_key;
+UPDATE taler_exchange_incoming SET exchange_incoming_id = bank_transaction;
+ALTER TABLE taler_exchange_incoming
+ ALTER COLUMN exchange_incoming_id SET GENERATED ALWAYS,
+ ADD PRIMARY KEY (exchange_incoming_id);
+COMMENT ON COLUMN taler_exchange_incoming.exchange_incoming_id IS 'Incoming history publication ID';
+
+SELECT setval(pg_get_serial_sequence('taler_exchange_incoming', 'exchange_incoming_id'),
+ (SELECT COALESCE(MAX(bank_transaction_id), 0) + 1 FROM bank_account_transactions), false);
+
+COMMIT;
diff --git a/database-versioning/libeufin-bank-procedures.sql b/database-versioning/libeufin-bank-procedures.sql
@@ -640,6 +640,7 @@ RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
local_amount taler_amount;
+local_taler_in_id INT8;
BEGIN
-- Register incoming transaction
INSERT INTO taler_exchange_incoming (
@@ -654,7 +655,7 @@ INSERT INTO taler_exchange_incoming (
in_type,
in_authorization_pub,
in_authorization_sig
-);
+) RETURNING exchange_incoming_id INTO local_taler_in_id;
-- Update stats
IF in_type = 'reserve' THEN
SELECT (amount).val, (amount).frac
@@ -663,7 +664,7 @@ IF in_type = 'reserve' THEN
CALL stats_register_payment('taler_in', NULL, local_amount, null);
END IF;
-- Notify new incoming transaction
-PERFORM pg_notify('bank_incoming_tx', in_account_id || ' ' || in_tx_row_id);
+PERFORM pg_notify('bank_incoming_tx', in_account_id || ' ' || local_taler_in_id);
END $$;
COMMENT ON FUNCTION register_incoming
IS 'Register a bank transaction as a taler incoming transaction and announce it';
diff --git a/database-versioning/libeufin-nexus-0015.sql b/database-versioning/libeufin-nexus-0015.sql
@@ -0,0 +1,34 @@
+--
+-- 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('libeufin-nexus-0015', NULL, NULL);
+
+SET search_path TO libeufin_nexus;
+
+-- Preserve existing incoming-history IDs and persisted consumer cursors.
+-- Allocate subsequent IDs when a transaction becomes eligible for history.
+ALTER TABLE talerable_incoming_transactions ADD COLUMN taler_in_id INT8 GENERATED BY DEFAULT AS IDENTITY;
+UPDATE talerable_incoming_transactions SET taler_in_id = incoming_transaction_id;
+ALTER TABLE talerable_incoming_transactions
+ ALTER COLUMN taler_in_id SET GENERATED ALWAYS,
+ ADD PRIMARY KEY (taler_in_id);
+COMMENT ON COLUMN talerable_incoming_transactions.taler_in_id IS 'Incoming history publication ID';
+
+SELECT setval(pg_get_serial_sequence('talerable_incoming_transactions', 'taler_in_id'),
+ (SELECT COALESCE(MAX(incoming_transaction_id), 0) + 1 FROM incoming_transactions), false);
+
+COMMIT;
diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql
@@ -272,6 +272,7 @@ local_subject TEXT;
local_debit_payto TEXT;
local_authorization_pub BYTEA;
local_authorization_sig BYTEA;
+local_taler_in_id INT8;
BEGIN
IF in_credit_fee = (0, 0)::taler_amount THEN
in_credit_fee = NULL;
@@ -385,8 +386,8 @@ IF in_type IS NOT NULL AND NOT out_talerable AND out_bounce_id IS NULL THEN
,in_metadata
,local_authorization_pub
,local_authorization_sig
- );
- PERFORM pg_notify('nexus_incoming_tx', out_tx_id::text);
+ ) RETURNING taler_in_id INTO local_taler_in_id;
+ PERFORM pg_notify('nexus_incoming_tx', local_taler_in_id::text);
out_talerable=TRUE;
END IF;
END IF;
@@ -673,6 +674,7 @@ CREATE FUNCTION register_prepared_transfers (
LANGUAGE plpgsql AS $$
DECLARE
talerable_tx INT8;
+ local_taler_in_id INT8;
idempotent BOOLEAN;
BEGIN
@@ -720,9 +722,9 @@ IF in_recurrent THEN
INSERT INTO talerable_incoming_transactions (incoming_transaction_id, type, metadata, authorization_pub, authorization_sig)
SELECT moved_tx.incoming_transaction_id, in_type, in_account_pub, in_authorization_pub, in_authorization_sig
FROM moved_tx
- RETURNING incoming_transaction_id INTO talerable_tx;
+ RETURNING incoming_transaction_id, taler_in_id INTO talerable_tx, local_taler_in_id;
IF talerable_tx IS NOT NULL THEN
- PERFORM pg_notify('nexus_incoming_tx', talerable_tx::text);
+ PERFORM pg_notify('nexus_incoming_tx', local_taler_in_id::text);
END IF;
ELSE
-- Bounce all pending
diff --git a/libeufin-bank/src/db/exchange.rs b/libeufin-bank/src/db/exchange.rs
@@ -297,13 +297,13 @@ pub async fn incoming_history(
) -> sqlx::Result<Vec<IncomingBankTransaction>> {
history(
db,
- "bank_transaction_id",
+ "exchange_incoming_id",
params,
|| channel.subscribe(exchange_id),
|| {
let mut query = QueryBuilder::new(
"SELECT
- bank_transaction_id
+ exchange_incoming_id
,transaction_date
,amount
,debtor_payto
@@ -323,7 +323,7 @@ pub async fn incoming_history(
|r| {
Ok(match r.try_get_parse("type")? {
IncomingType::reserve => IncomingBankTransaction::Reserve {
- row_id: r.try_get_u64("bank_transaction_id")?,
+ row_id: r.try_get_u64("exchange_incoming_id")?,
date: r.try_get_timestamp("transaction_date")?.into(),
amount: r.try_get_amount("amount", currency)?,
credit_fee: None,
@@ -333,7 +333,7 @@ pub async fn incoming_history(
authorization_sig: r.try_get("authorization_sig")?,
},
IncomingType::kyc => IncomingBankTransaction::Kyc {
- row_id: r.try_get_u64("bank_transaction_id")?,
+ row_id: r.try_get_u64("exchange_incoming_id")?,
date: r.try_get_timestamp("transaction_date")?.into(),
amount: r.try_get_amount("amount", currency)?,
credit_fee: None,
diff --git a/libeufin-nexus/src/api.rs b/libeufin-nexus/src/api.rs
@@ -432,8 +432,8 @@ pub mod test {
use taler_test_utils::{
Router,
routine::{
- admin_add_incoming_routine, out_history_routine, registration_routine, revenue_routine,
- transfer_routine,
+ admin_add_incoming_routine, in_history_routine, out_history_routine,
+ registration_routine, revenue_routine, transfer_routine,
},
server::TestServer as _,
tasks,
@@ -508,6 +508,20 @@ pub mod test {
}
#[db_test]
+ async fn incoming_history(db: PgPool) {
+ let server = api_setup(&db).await;
+ in_history_routine(
+ &server.prefix("/taler-wire-gateway"),
+ &server.prefix("/taler-prepared-transfer"),
+ &CLIENT.as_uri(),
+ &EXCHANGE.as_uri(),
+ tasks!(),
+ tasks!(),
+ )
+ .await;
+ }
+
+ #[db_test]
async fn admin_add_incoming(db: PgPool) {
let server = api_setup(&db).await;
admin_add_incoming_routine(
diff --git a/libeufin-nexus/src/db/exchange.rs b/libeufin-nexus/src/db/exchange.rs
@@ -92,7 +92,7 @@ pub async fn incoming_history(
) -> sqlx::Result<Vec<IncomingBankTransaction>> {
history(
db,
- "incoming_transaction_id",
+ "taler_in_id",
params,
listen,
|| {
@@ -100,7 +100,7 @@ pub async fn incoming_history(
QueryBuilder::new(
"
SELECT
- incoming_transaction_id
+ taler_in_id
,execution_time
,amount
,credit_fee
@@ -121,7 +121,7 @@ pub async fn incoming_history(
.filter(|it| !it.is_zero());
Ok(match r.try_get_parse("type")? {
IncomingType::reserve => IncomingBankTransaction::Reserve {
- row_id: r.try_get_u64("incoming_transaction_id")?,
+ row_id: r.try_get_u64("taler_in_id")?,
amount: r.try_get_amount("amount", currency)?,
credit_fee,
debit_account: r.try_get_payto("debit_payto")?,
@@ -131,7 +131,7 @@ pub async fn incoming_history(
authorization_sig: r.try_get("authorization_sig")?,
},
IncomingType::kyc => IncomingBankTransaction::Kyc {
- row_id: r.try_get_u64("incoming_transaction_id")?,
+ row_id: r.try_get_u64("taler_in_id")?,
amount: r.try_get_amount("amount", currency)?,
credit_fee,
debit_account: r.try_get_payto("debit_payto")?,