commit 2d4f778e4c143020e5ada9876134e54df9bd4f47
parent e8490074b3d174acb7863a36c3529792c83158e9
Author: Antoine A <>
Date: Mon, 29 Apr 2024 18:05:31 +0900
nexus: clean wire gateway implementation using relaxed API documentation
Diffstat:
6 files changed, 47 insertions(+), 56 deletions(-)
diff --git a/database-versioning/libeufin-bank-procedures.sql b/database-versioning/libeufin-bank-procedures.sql
@@ -1056,7 +1056,7 @@ SELECT
bank_account_id, is_taler_exchange, cashout_payto IS NULL, (NOT in_is_tan AND tan_channel IS NOT NULL)
INTO account_id, out_account_is_exchange, out_no_cashout_payto, out_tan_required
FROM bank_accounts
- JOIN customers ON bank_accounts.owning_customer_id = customers.customer_id
+ JOIN customers ON owning_customer_id=customer_id
WHERE login=in_login;
IF NOT FOUND THEN
out_account_not_found=TRUE;
diff --git a/database-versioning/libeufin-nexus-0001.sql b/database-versioning/libeufin-nexus-0001.sql
@@ -41,7 +41,7 @@ COMMENT ON TYPE submission_state
never_heard_back is a fallback state, in case one successful submission did
never get confirmed via camt.5x or pain.002.';
-CREATE TABLE IF NOT EXISTS incoming_transactions
+CREATE TABLE incoming_transactions
(incoming_transaction_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE
,amount taler_amount NOT NULL
,wire_transfer_subject TEXT NOT NULL
@@ -53,12 +53,12 @@ COMMENT ON COLUMN incoming_transactions.bank_id
IS 'ISO20022 AccountServicerReference';
-- only active in exchange mode. Note: duplicate keys are another reason to bounce.
-CREATE TABLE IF NOT EXISTS talerable_incoming_transactions
+CREATE TABLE talerable_incoming_transactions
(incoming_transaction_id INT8 NOT NULL UNIQUE REFERENCES incoming_transactions(incoming_transaction_id) ON DELETE CASCADE
,reserve_public_key BYTEA NOT NULL UNIQUE CHECK (LENGTH(reserve_public_key)=32)
);
-CREATE TABLE IF NOT EXISTS outgoing_transactions
+CREATE TABLE outgoing_transactions
(outgoing_transaction_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE
,amount taler_amount NOT NULL
,wire_transfer_subject TEXT
@@ -69,7 +69,7 @@ CREATE TABLE IF NOT EXISTS outgoing_transactions
COMMENT ON COLUMN outgoing_transactions.message_id
IS 'ISO20022 MessageIdentification';
-CREATE TABLE IF NOT EXISTS initiated_outgoing_transactions
+CREATE TABLE initiated_outgoing_transactions
(initiated_outgoing_transaction_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE
,amount taler_amount NOT NULL
,wire_transfer_subject TEXT NOT NULL
@@ -93,15 +93,15 @@ value will be used as a unique identifier for its related pain.001 document.
For this reason, it must have at most 35 characters';
-- only active in exchange mode.
-CREATE TABLE IF NOT EXISTS bounced_transactions
+CREATE TABLE bounced_transactions
(incoming_transaction_id INT8 NOT NULL UNIQUE REFERENCES incoming_transactions(incoming_transaction_id) ON DELETE CASCADE
,initiated_outgoing_transaction_id INT8 NOT NULL UNIQUE REFERENCES initiated_outgoing_transactions(initiated_outgoing_transaction_id) ON DELETE CASCADE
);
-CREATE INDEX IF NOT EXISTS incoming_transaction_timestamp
+CREATE INDEX incoming_transaction_timestamp
ON incoming_transactions (execution_time);
-CREATE INDEX IF NOT EXISTS outgoing_transaction_timestamp
+CREATE INDEX outgoing_transaction_timestamp
ON outgoing_transactions (execution_time);
COMMIT;
diff --git a/database-versioning/libeufin-nexus-0003.sql b/database-versioning/libeufin-nexus-0003.sql
@@ -19,20 +19,18 @@ SELECT _v.register_patch('libeufin-nexus-0003', NULL, NULL);
SET search_path TO libeufin_nexus;
-CREATE TABLE IF NOT EXISTS talerable_outgoing_transactions
- (talerable_outgoing_transaction_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE
- ,initiated_outgoing_transaction_id INT8 UNIQUE REFERENCES initiated_outgoing_transactions(initiated_outgoing_transaction_id) ON DELETE CASCADE
- ,outgoing_transaction_id INT8 UNIQUE REFERENCES outgoing_transactions(outgoing_transaction_id) ON DELETE CASCADE
- ,CONSTRAINT tx_link CHECK (initiated_outgoing_transaction_id IS NOT NULL OR outgoing_transaction_id IS NOT NULL)
- ,request_uid BYTEA UNIQUE CHECK (LENGTH(request_uid)=64)
+CREATE TABLE talerable_outgoing_transactions
+ ( outgoing_transaction_id INT8 UNIQUE NOT NULL REFERENCES outgoing_transactions(outgoing_transaction_id) ON DELETE CASCADE
,wtid BYTEA NOT NULL UNIQUE CHECK (LENGTH(wtid)=32)
,exchange_base_url TEXT NOT NULL
);
-COMMENT ON COLUMN talerable_outgoing_transactions.initiated_outgoing_transaction_id
- IS 'If the transaction have been initiated';
-COMMENT ON COLUMN talerable_outgoing_transactions.outgoing_transaction_id
- IS 'If the transaction have been recovered';
-COMMENT ON CONSTRAINT tx_link ON talerable_outgoing_transactions
- IS 'A transaction is either initiated or recovered';
+CREATE TABLE transfer_operations
+ ( initiated_outgoing_transaction_id INT8 UNIQUE NOT NULL REFERENCES initiated_outgoing_transactions(initiated_outgoing_transaction_id) ON DELETE CASCADE
+ ,request_uid BYTEA UNIQUE NOT NULL CHECK (LENGTH(request_uid)=64)
+ ,wtid BYTEA UNIQUE NOT NULL CHECK (LENGTH(wtid)=32)
+ ,exchange_base_url TEXT NOT NULL
+ );
+COMMENT ON TABLE transfer_operations
+ IS 'Operation table for idempotent wire gateway transfers.';
COMMIT;
diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql
@@ -42,7 +42,6 @@ CREATE FUNCTION register_outgoing(
LANGUAGE plpgsql AS $$
DECLARE
init_id INT8;
-talerable_id INT8;
BEGIN
-- Check if already registered
SELECT outgoing_transaction_id INTO out_tx_id
@@ -90,10 +89,9 @@ IF in_wtid IS NOT NULL OR in_exchange_url IS NOT NULL THEN
wtid,
exchange_base_url
) VALUES (out_tx_id, in_wtid, in_exchange_url)
- ON CONFLICT (wtid) DO NOTHING
- RETURNING talerable_outgoing_transaction_id INTO talerable_id;
- IF talerable_id IS NOT NULL THEN
- PERFORM pg_notify('outgoing_tx', talerable_id::text);
+ ON CONFLICT (wtid) DO NOTHING;
+ IF FOUND THEN
+ PERFORM pg_notify('outgoing_tx', out_tx_id::text);
END IF;
END IF;
END $$;
@@ -233,7 +231,7 @@ BEGIN
-- Check conflict
IF EXISTS (
SELECT FROM talerable_incoming_transactions
- JOIN incoming_transactions ON talerable_incoming_transactions.incoming_transaction_id=incoming_transactions.incoming_transaction_id
+ JOIN incoming_transactions USING(incoming_transaction_id)
WHERE reserve_public_key = in_reserve_public_key
AND bank_id != in_bank_id
) THEN
@@ -280,20 +278,18 @@ CREATE FUNCTION taler_transfer(
OUT out_timestamp INT8
)
LANGUAGE plpgsql AS $$
-DECLARE
- initiated_id INT8;
BEGIN
-- Check for idempotence and conflict
SELECT (amount != in_amount
OR credit_payto_uri != in_credit_account_payto
OR exchange_base_url != in_exchange_base_url
OR wtid != in_wtid)
- ,talerable_outgoing_transaction_id, initiation_time
+ ,transfer_operations.initiated_outgoing_transaction_id, initiation_time
INTO out_request_uid_reuse, out_tx_row_id, out_timestamp
- FROM talerable_outgoing_transactions
+ FROM transfer_operations
JOIN initiated_outgoing_transactions
- ON talerable_outgoing_transactions.initiated_outgoing_transaction_id=initiated_outgoing_transactions.initiated_outgoing_transaction_id
- WHERE talerable_outgoing_transactions.request_uid = in_request_uid;
+ ON transfer_operations.initiated_outgoing_transaction_id=initiated_outgoing_transactions.initiated_outgoing_transaction_id
+ WHERE transfer_operations.request_uid = in_request_uid;
IF FOUND THEN
RETURN;
END IF;
@@ -310,19 +306,19 @@ INSERT INTO initiated_outgoing_transactions (
,in_credit_account_payto
,in_timestamp
,in_bank_id
-) RETURNING initiated_outgoing_transaction_id INTO initiated_id;
+) RETURNING initiated_outgoing_transaction_id INTO out_tx_row_id;
-- Register outgoing transaction
-INSERT INTO talerable_outgoing_transactions(
+INSERT INTO transfer_operations(
initiated_outgoing_transaction_id
,request_uid
,wtid
,exchange_base_url
) VALUES (
- initiated_id
+ out_tx_row_id
,in_request_uid
,in_wtid
,in_exchange_base_url
-) RETURNING talerable_outgoing_transaction_id INTO out_tx_row_id;
+);
out_timestamp = in_timestamp;
PERFORM pg_notify('outgoing_tx', out_tx_row_id::text);
END $$;
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt
@@ -32,17 +32,16 @@ class ExchangeDAO(private val db: Database) {
): List<IncomingReserveTransaction>
= db.poolHistoryGlobal(params, db::listenIncoming, """
SELECT
- tit.incoming_transaction_id
+ incoming_transaction_id
,execution_time
,(amount).val AS amount_val
,(amount).frac AS amount_frac
,debit_payto_uri
,reserve_public_key
- FROM talerable_incoming_transactions AS tit
- JOIN incoming_transactions AS it
- ON tit.incoming_transaction_id=it.incoming_transaction_id
+ FROM talerable_incoming_transactions
+ JOIN incoming_transactions USING(incoming_transaction_id)
WHERE
- """, "tit.incoming_transaction_id") {
+ """, "incoming_transaction_id") {
IncomingReserveTransaction(
row_id = it.getLong("incoming_transaction_id"),
date = it.getTalerTimestamp("execution_time"),
@@ -60,22 +59,19 @@ class ExchangeDAO(private val db: Database) {
// reach database : the initiation first else the recovered transaction.
= db.poolHistoryGlobal(params, db::listenOutgoing, """
SELECT
- talerable_outgoing_transaction_id
- ,COALESCE(iot.initiation_time, ot.execution_time) AS execution_time
- ,(COALESCE(iot.amount, ot.amount)).val AS amount_val
- ,(COALESCE(iot.amount, ot.amount)).frac AS amount_frac
- ,COALESCE(iot.credit_payto_uri, ot.credit_payto_uri) AS credit_payto_uri
+ outgoing_transaction_id
+ ,execution_time AS execution_time
+ ,(amount).val AS amount_val
+ ,(amount).frac AS amount_frac
+ ,credit_payto_uri AS credit_payto_uri
,wtid
,exchange_base_url
- FROM talerable_outgoing_transactions AS tot
- LEFT OUTER JOIN outgoing_transactions AS ot
- ON tot.outgoing_transaction_id=ot.outgoing_transaction_id
- LEFT OUTER JOIN initiated_outgoing_transactions AS iot
- ON tot.initiated_outgoing_transaction_id=iot.initiated_outgoing_transaction_id
+ FROM talerable_outgoing_transactions
+ JOIN outgoing_transactions USING(outgoing_transaction_id)
WHERE
- """, "talerable_outgoing_transaction_id") {
+ """, "outgoing_transaction_id") {
OutgoingTransaction(
- row_id = it.getLong("talerable_outgoing_transaction_id"),
+ row_id = it.getLong("outgoing_transaction_id"),
date = it.getTalerTimestamp("execution_time"),
amount = it.getAmount("amount", db.bankCurrency),
credit_account = it.getString("credit_payto_uri"),
diff --git a/nexus/src/test/kotlin/WireGatewayApiTest.kt b/nexus/src/test/kotlin/WireGatewayApiTest.kt
@@ -149,15 +149,16 @@ class WireGatewayApiTest {
url = "/taler-wire-gateway/history/outgoing",
ids = { it.outgoing_transactions.map { it.row_id } },
registered = listOf(
- {
- transfer()
- },
{
talerableOut(db)
}
),
ignored = listOf(
{
+ // Ignore pending transfers
+ transfer()
+ },
+ {
// Ignore manual incoming transaction
talerableIn(db)
},