libeufin

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

commit 15269b462cc172258702377babe03529de29234c
parent d7064c851ae27cd5495f509fbc191df710e9e724
Author: Antoine A <>
Date:   Fri, 13 Oct 2023 11:17:13 +0000

Move all transaction metadata logic in a single file

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/Database.kt | 37++++++++++++++-----------------------
Abank/src/main/kotlin/tech/libeufin/bank/Metadata.kt | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt | 6++++--
Mbank/src/test/kotlin/TalerApiTest.kt | 2+-
4 files changed, 74 insertions(+), 26 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt @@ -768,49 +768,40 @@ class Database(dbConfig: String, private val bankCurrency: String): java.io.Clos BankTransactionResult.CONFLICT } else -> { + val metadata = TxMetadata.parse(tx.subject) if (it.getBoolean("out_creditor_is_exchange")) { - // Parse subject - val reservePub = try { - EddsaPublicKey(tx.subject) - } catch (e: Exception) { - null - } - if (reservePub != null) { - val rowId = it.getLong("out_credit_row_id") + val rowId = it.getLong("out_credit_row_id") + if (metadata is IncomingTxMetadata) { + val stmt = conn.prepareStatement(""" INSERT INTO taler_exchange_incoming (reserve_pub, bank_transaction) VALUES (?, ?) """) - stmt.setBytes(1, reservePub.raw) + stmt.setBytes(1, metadata.reservePub.raw) stmt.setLong(2, rowId) stmt.executeUpdate() conn.execSQLUpdate("NOTIFY incoming_tx, '${"${tx.creditorAccountId} $rowId"}'") } else { // TODO bounce + logger.warn("exchange account ${tx.creditorAccountId} received a transaction $rowId with malformed metadata, will bounce in future version") } - } else if (it.getBoolean("out_debtor_is_exchange")) { - // Parse subject - val metadata = try { - val split = tx.subject.split(" ", limit=2) ; - Pair(ShortHashCode(split[0]), split[2]) - } catch (e: Exception) { - null - } - if (metadata != null) { - val rowId = it.getLong("out_debit_row_id") + } + if (it.getBoolean("out_debtor_is_exchange")) { + val rowId = it.getLong("out_debit_row_id") + if (metadata is OutgoingTxMetadata) { val stmt = conn.prepareStatement(""" INSERT INTO taler_exchange_outgoing (wtid, exchange_base_url, bank_transaction) VALUES (?, ?, ?) """) - stmt.setBytes(1, metadata.first.raw) - stmt.setString(2, metadata.second) + stmt.setBytes(1, metadata.wtid.raw) + stmt.setString(2, metadata.exchangeBaseUrl) stmt.setLong(3, rowId) stmt.executeUpdate() conn.execSQLUpdate("NOTIFY outgoing_tx, '${"${tx.debtorAccountId} $rowId"}'") } else { - // TODO log ? + logger.warn("exchange account ${tx.debtorAccountId} sent a transaction $rowId with malformed metadata") } } BankTransactionResult.SUCCESS @@ -1498,7 +1489,7 @@ class Database(dbConfig: String, private val bankCurrency: String): java.io.Clos pmtInfId: String = "not used", endToEndId: String = "not used", ): TalerTransferCreationResult = conn { conn -> - val subject = "${req.wtid.encoded()} ${req.exchange_base_url}" + val subject = OutgoingTxMetadata(req.wtid, req.exchange_base_url).toString() val stmt = conn.prepareStatement(""" SELECT out_exchange_balance_insufficient diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Metadata.kt b/bank/src/main/kotlin/tech/libeufin/bank/Metadata.kt @@ -0,0 +1,54 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2019 Stanisci and Dold. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin 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 Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ +package tech.libeufin.bank + +sealed interface TxMetadata { + // TODO versioning ? + companion object { + fun parse(subject: String): TxMetadata? { + // IncomingTxMetadata + try { + return IncomingTxMetadata(EddsaPublicKey(subject)) + } catch (e: Exception) { } + + // OutgoingTxMetadata + try { + val (wtid, exchangeBaseUrl) = subject.split(" ", limit=2) ; + return OutgoingTxMetadata(ShortHashCode(wtid), exchangeBaseUrl) + } catch (e: Exception) { } + + // No well formed metadata + return null + } + + fun encode(metadata: TxMetadata): String { + return when (metadata) { + is IncomingTxMetadata -> "${metadata.reservePub}" + is OutgoingTxMetadata -> "${metadata.wtid} ${metadata.exchangeBaseUrl}" + } + } + } +} + +data class IncomingTxMetadata(val reservePub: EddsaPublicKey): TxMetadata { + override fun toString(): String = TxMetadata.encode(this) +} +data class OutgoingTxMetadata(val wtid: ShortHashCode, val exchangeBaseUrl: String): TxMetadata { + override fun toString(): String = TxMetadata.encode(this) +} +\ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt @@ -136,9 +136,11 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: BankApplicationContext) "Currency mismatch", TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH ) + + val subject = IncomingTxMetadata(req.reserve_pub).toString() // TODO check conflict in transaction - if (db.bankTransactionCheckExists(req.reserve_pub.encoded()) != null) + if (db.bankTransactionCheckExists(subject) != null) throw conflict( "Reserve pub. already used", TalerErrorCode.TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT @@ -158,7 +160,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: BankApplicationContext) amount = req.amount, creditorAccountId = exchangeAccount.id, transactionDate = txTimestamp, - subject = req.reserve_pub.encoded() + subject = subject ) val res = db.bankTransactionCreate(op) /** diff --git a/bank/src/test/kotlin/TalerApiTest.kt b/bank/src/test/kotlin/TalerApiTest.kt @@ -74,7 +74,7 @@ class TalerApiTest { BankInternalTransaction( creditorAccountId = from, debtorAccountId = to, - subject = randShortHashCode().encoded(), + subject = IncomingTxMetadata(randShortHashCode()).toString(), amount = TalerAmount( 10, 0, "KUDOS"), accountServicerReference = "acct-svcr-ref", endToEndId = "end-to-end-id",