libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 61cf08e25e4f4505d1784bb7fe2fb942a2d24a91
parent 13ed1892fead3ea284f2033b2df9917be698d3d7
Author: Antoine A <>
Date:   Tue, 10 Sep 2024 17:38:31 +0200

nexus: warn on data inconsistency

Diffstat:
Mcommon/src/main/kotlin/db/transaction.kt | 13++++++++++++-
Mdatabase-versioning/libeufin-nexus-procedures.sql | 124++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 106 insertions(+), 31 deletions(-)

diff --git a/common/src/main/kotlin/db/transaction.kt b/common/src/main/kotlin/db/transaction.kt @@ -45,7 +45,18 @@ suspend fun <R> retrySerializationError(lambda: suspend () -> R): R { /** Run a postgres query using a prepared statement */ inline fun <R> PgConnection.withStatement(query: String, lambda: PreparedStatement.() -> R): R { - return prepareStatement(query).use(lambda) + val stmt = prepareStatement(query) + return stmt.use { + val res = stmt.lambda() + // Log warnings + var warning = stmt.getWarnings() + while (warning != null) { + logger.warning(warning.message) + warning = warning.getNextWarning() + } + stmt.clearWarnings() + res + } } /** Run a postgres [transaction] */ diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql @@ -73,19 +73,69 @@ CREATE FUNCTION register_outgoing( LANGUAGE plpgsql AS $$ DECLARE init_id INT8; +local_amount taler_amount; +local_subject TEXT; +local_credit_payto TEXT; +local_wtid BYTEA; +local_exchange_base_url TEXT; BEGIN -- Check if already registered -SELECT outgoing_transaction_id INTO out_tx_id - FROM outgoing_transactions +SELECT outgoing_transaction_id, subject, credit_payto, (amount).val, (amount).frac, + wtid, exchange_base_url + INTO out_tx_id, local_subject, local_credit_payto, local_amount.val, local_amount.frac, + local_wtid, local_exchange_base_url + FROM outgoing_transactions LEFT JOIN talerable_outgoing_transactions USING (outgoing_transaction_id) WHERE end_to_end_id = in_end_to_end_id; -IF FOUND THEN - out_found = true; - -- TODO Should we update the subject and credit payto if it's finally found - -- TODO Should we check that amount and other info match ? - SELECT true INTO out_initiated - FROM initiated_outgoing_transactions - WHERE outgoing_transaction_id = out_tx_id; -ELSE +out_found=FOUND; +IF out_found THEN + -- Check metadata + -- TODO take subject if missing and more detailed credit payto + IF local_subject IS DISTINCT FROM in_subject THEN + RAISE NOTICE 'outgoing tx %: stored subjet is ''%'' got ''%''', in_end_to_end_id, local_subject, in_subject; + END IF; + IF local_credit_payto IS DISTINCT FROM in_credit_payto THEN + RAISE NOTICE 'outgoing tx %: stored subjet credit payto is % got %', in_end_to_end_id, local_credit_payto, in_credit_payto; + END IF; + IF local_amount IS DISTINCT FROM in_amount THEN + RAISE NOTICE 'outgoing tx %: stored amount is % got %', in_end_to_end_id, local_amount, in_amount; + END IF; + IF local_wtid IS DISTINCT FROM in_wtid THEN + RAISE NOTICE 'outgoing tx %: stored wtid is % got %', in_end_to_end_id, local_wtid, in_wtid; + END IF; + IF local_exchange_base_url IS DISTINCT FROM in_exchange_url THEN + RAISE NOTICE 'outgoing tx %: stored exchange base url is % got %', in_end_to_end_id, local_exchange_base_url, in_exchange_url; + END IF; +END IF; + +-- Check if initiated +SELECT initiated_outgoing_transaction_id, subject, credit_payto, (amount).val, (amount).frac, + wtid, exchange_base_url + INTO init_id, local_subject, local_credit_payto, local_amount.val, local_amount.frac, + local_wtid, local_exchange_base_url + FROM initiated_outgoing_transactions LEFT JOIN transfer_operations USING (initiated_outgoing_transaction_id) + WHERE end_to_end_id = in_end_to_end_id; +out_initiated=FOUND; +IF out_initiated AND NOT out_found THEN + -- Check metadata + -- TODO take subject if missing and more detailed credit payto + IF local_subject IS DISTINCT FROM in_subject THEN + RAISE NOTICE 'outgoing tx %: initiated subjet is ''%'' got ''%''', in_end_to_end_id, local_subject, in_subject; + END IF; + IF local_credit_payto IS DISTINCT FROM in_credit_payto THEN + RAISE NOTICE 'outgoing tx %: initiated subjet credit payto is % got %', in_end_to_end_id, local_credit_payto, in_credit_payto; + END IF; + IF local_amount IS DISTINCT FROM in_amount THEN + RAISE NOTICE 'outgoing tx %: initiated amount is % got %', in_end_to_end_id, local_amount, in_amount; + END IF; + IF local_wtid IS DISTINCT FROM in_wtid THEN + RAISE NOTICE 'outgoing tx %: initiated wtid is % got %', in_end_to_end_id, local_wtid, in_wtid; + END IF; + IF local_exchange_base_url IS DISTINCT FROM in_exchange_url THEN + RAISE NOTICE 'outgoing tx %: initiated exchange base url is % got %', in_end_to_end_id, local_exchange_base_url, in_exchange_url; + END IF; +END IF; + +IF NOT out_found THEN -- Store the transaction in the database INSERT INTO outgoing_transactions ( amount @@ -103,33 +153,34 @@ ELSE RETURNING outgoing_transaction_id INTO out_tx_id; + -- Register as talerable if contains wtid and exchange URL + IF in_wtid IS NOT NULL OR in_exchange_url IS NOT NULL THEN + INSERT INTO talerable_outgoing_transactions ( + outgoing_transaction_id, + wtid, + exchange_base_url + ) VALUES (out_tx_id, in_wtid, in_exchange_url) + ON CONFLICT (wtid) DO NOTHING; + IF FOUND THEN + PERFORM pg_notify('outgoing_tx', out_tx_id::text); + END IF; + END IF; +END IF; + +IF NOT out_found AND out_initiated THEN -- Reconciles the related initiated transaction UPDATE initiated_outgoing_transactions SET outgoing_transaction_id = out_tx_id ,status = 'success' ,status_msg = null - WHERE end_to_end_id = in_end_to_end_id - RETURNING true INTO out_initiated; + WHERE initiated_outgoing_transaction_id = init_id; -- Reconciles the related initiated batch UPDATE initiated_outgoing_batches - SET status = 'success' + SET status = 'success', status_msg = null WHERE message_id = in_msg_id; END IF; - --- Register as talerable if contains wtid and exchange URL -IF in_wtid IS NOT NULL OR in_exchange_url IS NOT NULL THEN - INSERT INTO talerable_outgoing_transactions ( - outgoing_transaction_id, - wtid, - exchange_base_url - ) VALUES (out_tx_id, in_wtid, in_exchange_url) - ON CONFLICT (wtid) DO NOTHING; - IF FOUND THEN - PERFORM pg_notify('outgoing_tx', out_tx_id::text); - END IF; -END IF; END $$; COMMENT ON FUNCTION register_outgoing IS 'Register an outgoing transaction and optionally reconciles the related initiated transaction with it'; @@ -144,14 +195,27 @@ CREATE FUNCTION register_incoming( ,OUT out_tx_id INT8 ) LANGUAGE plpgsql AS $$ +DECLARE +local_amount taler_amount; +local_subject TEXT; +local_debit_payto TEXT; BEGIN -- Check if already registered -SELECT incoming_transaction_id INTO out_tx_id +SELECT incoming_transaction_id, subject, debit_payto, (amount).val, (amount).frac + INTO out_tx_id, local_subject, local_debit_payto, local_amount.val, local_amount.frac FROM incoming_transactions WHERE bank_id = in_bank_id; -IF FOUND THEN - out_found = true; - -- TODO Should we check that amount and other info match ? +out_found=FOUND; +IF out_found THEN + IF local_subject != in_subject THEN + RAISE NOTICE 'incoming tx %: stored subjet is ''%'' got ''%''', in_bank_id, local_subject, in_subject; + END IF; + IF local_debit_payto != in_debit_payto THEN + RAISE NOTICE 'incoming tx %: stored subjet debit payto is % got %', in_bank_id, local_debit_payto, in_debit_payto; + END IF; + IF local_amount != in_amount THEN + RAISE NOTICE 'incoming tx %: stored amount is % got %', in_bank_id, local_amount, in_amount; + END IF; ELSE -- Store the transaction in the database INSERT INTO incoming_transactions (