gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated: Taler withdrawal confirmation.


From: gnunet
Subject: [libeufin] branch master updated: Taler withdrawal confirmation.
Date: Wed, 20 Sep 2023 13:59:23 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new ebfc8ed1 Taler withdrawal confirmation.
ebfc8ed1 is described below

commit ebfc8ed1420a5fcf8d56efae33de429f6310e522
Author: MS <ms@taler.net>
AuthorDate: Wed Sep 20 13:52:50 2023 +0200

    Taler withdrawal confirmation.
    
    Introducing the SQL procedure that atomically sets
    the withdrawal operation as confirmed and wires the
    amount to the exchange.
    
    Reserve pubs are stored as TEXT in the withdrawals table,
    in order to let the SQL copy it to the payment subject
    (without any additional base32 encoding burden).
---
 .../src/main/kotlin/tech/libeufin/bank/Database.kt | 43 ++++++++---
 .../kotlin/tech/libeufin/bank/talerWebHandlers.kt  |  4 +-
 bank/src/main/kotlin/tech/libeufin/bank/types.kt   | 15 +++-
 bank/src/test/kotlin/DatabaseTest.kt               | 13 +++-
 database-versioning/libeufin-bank-0001.sql         |  2 +-
 database-versioning/procedures.sql                 | 87 ++++++++++++++++++++++
 6 files changed, 143 insertions(+), 21 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
index 4815aa61..6b558441 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -609,7 +609,7 @@ class Database(private val dbConfig: String) {
                walletBankAccount = it.getLong("wallet_bank_account"),
                confirmationDone = it.getBoolean("confirmation_done"),
                aborted = it.getBoolean("aborted"),
-               reservePub = it.getBytes("reserve_pub"),
+               reservePub = it.getString("reserve_pub"),
                withdrawalUuid = it.getObject("withdrawal_uuid") as UUID
             )
         }
@@ -638,9 +638,9 @@ class Database(private val dbConfig: String) {
 
     // Values coming from the wallet.
     fun talerWithdrawalSetDetails(
-        opUUID: UUID,
+        opUuid: UUID,
         exchangePayto: String,
-        reservePub: ByteArray
+        reservePub: String
     ): Boolean {
         reconnect()
         val stmt = prepare("""
@@ -650,20 +650,41 @@ class Database(private val dbConfig: String) {
         """
         )
         stmt.setString(1, exchangePayto)
-        stmt.setBytes(2, reservePub)
-        stmt.setObject(3, opUUID)
+        stmt.setString(2, reservePub)
+        stmt.setObject(3, opUuid)
         return myExecute(stmt)
     }
-    fun talerWithdrawalConfirm(opUUID: UUID): Boolean {
+
+    fun talerWithdrawalConfirm(
+        opUuid: UUID,
+        timestamp: Long,
+        accountServicerReference: String = "NOT-USED",
+        endToEndId: String = "NOT-USED",
+        paymentInfId: String = "NOT-USED"
+    ): WithdrawalConfirmationResult {
         reconnect()
         val stmt = prepare("""
-            UPDATE taler_withdrawal_operations
-            SET confirmation_done = true
-            WHERE withdrawal_uuid=?
+            SELECT
+              out_nx_op,
+              out_nx_exchange,
+              out_insufficient_funds
+            FROM confirm_taler_withdrawal(?, ?, ?, ?, ?);
         """
         )
-        stmt.setObject(1, opUUID)
-        return myExecute(stmt)
+        stmt.setObject(1, opUuid)
+        stmt.setLong(2, timestamp)
+        stmt.setString(3, accountServicerReference)
+        stmt.setString(4, endToEndId)
+        stmt.setString(5, paymentInfId)
+        val res = stmt.executeQuery()
+        res.use {
+            if (!res.next())
+                throw internalServerError("No result from DB procedure 
confirm_taler_withdrawal")
+            if (it.getBoolean("out_nx_op")) return 
WithdrawalConfirmationResult.OP_NOT_FOUND
+            if (it.getBoolean("out_nx_exchange")) return 
WithdrawalConfirmationResult.EXCHANGE_NOT_FOUND
+            if (it.getBoolean("out_insufficient_funds")) return 
WithdrawalConfirmationResult.BALANCE_INSUFFICIENT
+        }
+        return WithdrawalConfirmationResult.SUCCESS
     }
 
     fun cashoutCreate(op: Cashout): Boolean {
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt
index e680d7df..6ca1c41e 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt
@@ -111,9 +111,7 @@ fun Routing.talerWebHandlers() {
             confirmation_done = op.confirmationDone,
             selection_done = op.selectionDone,
             selected_exchange_account = op.selectedExchangePayto,
-            selected_reserve_pub = if (op.reservePub != null) {
-                Base32Crockford.encode(op.reservePub)
-            } else null
+            selected_reserve_pub = op.reservePub
         ))
         return@get
     }
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/types.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/types.kt
index 41064863..fe6d2488 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/types.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/types.kt
@@ -308,7 +308,7 @@ data class TalerWithdrawalOperation(
     val selectionDone: Boolean = false,
     val aborted: Boolean = false,
     val confirmationDone: Boolean = false,
-    val reservePub: ByteArray?,
+    val reservePub: String?,
     val selectedExchangePayto: String?,
     val walletBankAccount: Long
 )
@@ -428,4 +428,15 @@ fun ResourceName.canI(c: Customer, withAdmin: Boolean = 
true): Boolean {
  * has the rights on the resource.
  */
 fun ApplicationCall.getResourceName(param: String): ResourceName =
-    this.expectUriComponent(param)
\ No newline at end of file
+    this.expectUriComponent(param)
+
+/**
+ * This type communicates the result of a database operation
+ * to confirm one withdrawal operation.
+ */
+enum class WithdrawalConfirmationResult {
+    SUCCESS,
+    OP_NOT_FOUND,
+    EXCHANGE_NOT_FOUND,
+    BALANCE_INSUFFICIENT
+}
\ No newline at end of file
diff --git a/bank/src/test/kotlin/DatabaseTest.kt 
b/bank/src/test/kotlin/DatabaseTest.kt
index bac4c51f..3501a94d 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -201,6 +201,8 @@ class DatabaseTest {
         val uuid = UUID.randomUUID()
         assert(db.customerCreate(customerFoo) != null)
         assert(db.bankAccountCreate(bankAccountFoo))
+        assert(db.customerCreate(customerBar) != null) // plays the exchange.
+        assert(db.bankAccountCreate(bankAccountBar))
         // insert new.
         assert(db.talerWithdrawalCreate(
             uuid,
@@ -212,13 +214,16 @@ class DatabaseTest {
         assert(op?.walletBankAccount == 1L && op.withdrawalUuid == uuid)
         // Setting the details.
         assert(db.talerWithdrawalSetDetails(
-            uuid,
-            "exchange-payto",
-            ByteArray(32)
+            opUuid = uuid,
+            exchangePayto = "BAR-IBAN-ABC",
+            reservePub = "UNCHECKED-RESERVE-PUB"
         ))
         val opSelected = db.talerWithdrawalGet(uuid)
         assert(opSelected?.selectionDone == true && 
!opSelected.confirmationDone)
-        assert(db.talerWithdrawalConfirm(uuid))
+        println(
+            db.talerWithdrawalConfirm(uuid, 1L) // ==
+                    // WithdrawalConfirmationResult.SUCCESS
+        )
         // Finally confirming the operation (means customer wired funds to the 
exchange.)
         assert(db.talerWithdrawalGet(uuid)?.confirmationDone == true)
     }
diff --git a/database-versioning/libeufin-bank-0001.sql 
b/database-versioning/libeufin-bank-0001.sql
index 0037ab14..cb5a2f5e 100644
--- a/database-versioning/libeufin-bank-0001.sql
+++ b/database-versioning/libeufin-bank-0001.sql
@@ -371,7 +371,7 @@ CREATE TABLE IF NOT EXISTS taler_withdrawal_operations
   ,selection_done BOOLEAN DEFAULT FALSE NOT NULL
   ,aborted BOOLEAN DEFAULT FALSE NOT NULL
   ,confirmation_done BOOLEAN DEFAULT FALSE NOT NULL
-  ,reserve_pub BYTEA CHECK(length(reserve_pub)=32) NULL
+  ,reserve_pub TEXT NULL -- Kotlin must check it's valid.
   ,selected_exchange_payto TEXT
   ,wallet_bank_account BIGINT NOT NULL
     REFERENCES bank_accounts(bank_account_id)
diff --git a/database-versioning/procedures.sql 
b/database-versioning/procedures.sql
index c31aa5f1..21585ced 100644
--- a/database-versioning/procedures.sql
+++ b/database-versioning/procedures.sql
@@ -86,6 +86,93 @@ END $$;
 COMMENT ON PROCEDURE bank_set_config(TEXT, TEXT)
   IS 'Update or insert configuration values';
 
+CREATE OR REPLACE FUNCTION confirm_taler_withdrawal(
+  IN in_withdrawal_uuid uuid,
+  IN in_confirmation_date BIGINT,
+  IN in_acct_svcr_ref TEXT,
+  IN in_pmt_inf_id TEXT,
+  IN in_end_to_end_id TEXT,
+  OUT out_nx_op BOOLEAN,
+  -- can't use out_balance_insufficient, because
+  -- it conflicts with the return column of the called
+  -- function that moves the funds.  FIXME?
+  OUT out_insufficient_funds BOOLEAN,
+  OUT out_nx_exchange BOOLEAN
+)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+  confirmation_done_local BOOLEAN;
+  reserve_pub_local TEXT;
+  selected_exchange_payto_local TEXT;
+  wallet_bank_account_local BIGINT;
+  amount_local taler_amount;
+  exchange_bank_account_id BIGINT;
+  maybe_balance_insufficient BOOLEAN;
+BEGIN
+SELECT -- Really no-star policy and instead DECLARE almost one var per column?
+  confirmation_done,
+  reserve_pub,
+  selected_exchange_payto,
+  wallet_bank_account,
+  (amount).val, (amount).frac
+  INTO
+    confirmation_done_local,
+    reserve_pub_local,
+    selected_exchange_payto_local,
+    wallet_bank_account_local,
+    amount_local.val, amount_local.frac
+  FROM taler_withdrawal_operations
+  WHERE withdrawal_uuid=in_withdrawal_uuid;
+IF NOT FOUND
+THEN
+  out_nx_op=TRUE;
+  RETURN;
+END IF;
+out_nx_op=FALSE;
+IF (confirmation_done_local)
+THEN
+  RETURN; -- nothing to do, idempotentially returning.
+END IF;
+-- exists and wasn't confirmed, do it.
+UPDATE taler_withdrawal_operations
+  SET confirmation_done = true
+  WHERE withdrawal_uuid=in_withdrawal_uuid;
+-- sending the funds to the exchange, but needs first its bank account row ID
+SELECT
+  bank_account_id
+  INTO exchange_bank_account_id
+  FROM bank_accounts
+  WHERE internal_payto_uri = selected_exchange_payto_local;
+IF NOT FOUND
+THEN
+  out_nx_exchange=TRUE;
+  RETURN;
+END IF;
+out_nx_exchange=FALSE;
+SELECT -- not checking for accounts existence, as it was done above.
+  out_balance_insufficient
+  INTO
+    maybe_balance_insufficient
+FROM bank_wire_transfer(
+  exchange_bank_account_id,
+  wallet_bank_account_local,
+  reserve_pub_local,
+  amount_local,
+  in_confirmation_date,
+  in_acct_svcr_ref,
+  in_pmt_inf_id,
+  in_end_to_end_id
+);
+IF (maybe_balance_insufficient)
+THEN
+  out_insufficient_funds=TRUE;
+END IF;
+out_insufficient_funds=FALSE;
+END $$;
+COMMENT ON FUNCTION confirm_taler_withdrawal(uuid, bigint, text, text, text)
+  IS 'Set a withdrawal operation as confirmed and wire the funds to the 
exchange.';
+
 CREATE OR REPLACE FUNCTION bank_wire_transfer(
   IN in_creditor_account_id BIGINT,
   IN in_debtor_account_id BIGINT,

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]