gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] 02/02: Implement /withdrawal-operation.


From: gnunet
Subject: [libeufin] 02/02: Implement /withdrawal-operation.
Date: Sat, 18 Sep 2021 09:13:07 +0200

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

ms pushed a commit to branch master
in repository libeufin.

commit f629b3a17e94f1de1ea70bf59b25c71cdcd72b4f
Author: ms <ms@taler.net>
AuthorDate: Sat Sep 18 09:12:28 2021 +0200

    Implement /withdrawal-operation.
---
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |  16 +++
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  |  14 +++
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 130 +++++++++++----------
 .../kotlin/tech/libeufin/sandbox/bankAccount.kt    |  65 +++++++++++
 util/src/main/kotlin/Config.kt                     |   4 +-
 5 files changed, 165 insertions(+), 64 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index fc7c9bd..021c231 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -409,10 +409,26 @@ class BankAccountStatementEntity(id: EntityID<Int>) : 
IntEntity(id) {
 
 object TalerWithdrawalsTable : LongIdTable() {
     val wopid = uuid("wopid").autoGenerate()
+
+    /**
+     * Turns to true after the wallet gave the reserve public key
+     * and the exchange details to the bank.
+     */
+    val selectionDone = bool("selectionDone").default(false)
+
+    /**
+     * Turns to true after the wire transfer to the exchange bank account
+     * gets completed _on the bank's side_.  This does never guarantees that
+     * the payment arrived at the exchange's bank yet.
+     */
+    val transferDone = bool("transferDone").default(false)
+
 }
 class TalerWithdrawalEntity(id: EntityID<Long>) : LongEntity(id) {
     companion object : 
LongEntityClass<TalerWithdrawalEntity>(TalerWithdrawalsTable)
     var wopid by TalerWithdrawalsTable.wopid
+    var selectionDone by TalerWithdrawalsTable.selectionDone
+    var transferDone by TalerWithdrawalsTable.transferDone
 }
 
 object BankAccountReportsTable : IntIdTable() {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 938828b..d2ec84a 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -71,3 +71,17 @@ data class CamtParams(
     val type: Int,
     // need range parameter
 )
+
+data class TalerWithdrawalStatus(
+    val selection_done: Boolean,
+    val transfer_done: Boolean,
+    val amount: String,
+    val wire_types: List<String> = listOf("x-taler-bank"),
+    val suggested_exchange: String? = null,
+    val sender_wire: String? = null
+)
+
+data class TalerWithdrawalConfirmation(
+    val reserve_pub: String,
+    val exchange_wire_details: String
+)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index f42b5bf..7870a8a 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -84,7 +84,8 @@ import kotlin.random.Random
 import kotlin.system.exitProcess
 
 private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
-private val hostName: String? = getHostnameFromEnv("LIBEUFIN_SANDBOX_HOSTNAME")
+private val hostName: String? = getValueFromEnv("LIBEUFIN_SANDBOX_HOSTNAME")
+private val currency: String? = getValueFromEnv("LIBEUFIN_SANDBOX_CURRENCY")
 const val SANDBOX_DB_ENV_VAR_NAME = "LIBEUFIN_SANDBOX_DB_CONNECTION"
 
 data class SandboxError(
@@ -227,67 +228,15 @@ class MakeTransaction : CliktCommand("Wire-transfer money 
between Sandbox bank a
     override fun run() {
         val dbConnString = getDbConnFromEnv(SANDBOX_DB_ENV_VAR_NAME)
         Database.connect(dbConnString)
-        // check accounts exist
-        transaction {
-            val credit = BankAccountEntity.find {
-                BankAccountsTable.label eq creditAccount
-            }.firstOrNull() ?: run {
-                System.err.println("Credit account: $creditAccount, not found")
-                exitProcess(1)
-            }
-            val debit = BankAccountEntity.find {
-                BankAccountsTable.label eq debitAccount
-            }.firstOrNull() ?: run {
-                System.err.println("Debit account: $debitAccount, not found")
-                exitProcess(1)
-            }
-            if (credit.currency != debit.currency) {
-                System.err.println(
-                    "Sandbox has inconsistent state: " +
-                            "currency of credit (${credit.currency}) and debit 
(${debit.currency}) account differs.")
-                exitProcess(1)
-            }
-            val amountObj = try {
-                parseAmount(amount)
-            } catch (e: Exception) {
-                System.err.println("Amount given not valid: $amount")
-                exitProcess(1)
-            }
-            if (amountObj.currency != credit.currency || amountObj.currency != 
debit.currency) {
-                System.err.println("Amount's currency (${amountObj.currency}) 
can't be accepted")
-                exitProcess(1)
-            }
-            val randId = getRandomString(16)
-            BankAccountTransactionEntity.new {
-                creditorIban = credit.iban
-                creditorBic = credit.bic
-                creditorName = credit.name
-                debtorIban = debit.iban
-                debtorBic = debit.bic
-                debtorName = debit.name
-                subject = subjectArg
-                amount = amountObj.amount.toString()
-                currency = amountObj.currency
-                date = getUTCnow().toInstant().toEpochMilli()
-                accountServicerReference = "sandbox-$randId"
-                account = debit
-                direction = "DBIT"
-            }
-            BankAccountTransactionEntity.new {
-                creditorIban = credit.iban
-                creditorBic = credit.bic
-                creditorName = credit.name
-                debtorIban = debit.iban
-                debtorBic = debit.bic
-                debtorName = debit.name
-                subject = subjectArg
-                amount = amountObj.amount.toString()
-                currency = amountObj.currency
-                date = getUTCnow().toInstant().toEpochMilli()
-                accountServicerReference = "sandbox-$randId"
-                account = credit
-                direction = "CRDT"
-            }
+        try {
+            wireTransfer(debitAccount, creditAccount, amount, subjectArg)
+        } catch (e: SandboxError) {
+            print(e.message)
+            exitProcess(1)
+        } catch (e: Exception) {
+            // Here, Sandbox is in a highly unstable state.
+            println(e)
+            exitProcess(1)
         }
     }
 }
@@ -1005,6 +954,10 @@ fun serverMain(dbName: String, port: Int) {
                     hostName != null,
                     "Own hostname not found.  Logs should have warned"
                 )
+                SandboxAssert(
+                    currency != null,
+                    "Currency not found.  Logs should have warned"
+                )
                 // check that the three canonical accounts exist
                 val wo = transaction {
                     val exchange = BankAccountEntity.find {
@@ -1032,6 +985,59 @@ fun serverMain(dbName: String, port: Int) {
                 
call.respondText("taler://withdraw/${hostName}/api/${wo.wopid}")
                 return@get
             }
+            /**
+             * not regulating the access here, as the wopid was only granted
+             * to logged-in users before (at the /taler endpoint) and has 
enough
+             * entropy to prevent guesses.
+             */
+            get("/withdrawal-operation/{wopid}") {
+                val wopid: String = ensureNonNull("wopid")
+                val wo = transaction {
+
+                    TalerWithdrawalEntity.find {
+                        TalerWithdrawalsTable.wopid eq UUID.fromString(wopid)
+                    }.firstOrNull() ?: throw SandboxError(
+                        HttpStatusCode.NotFound,
+                        "Withdrawal operation: $wopid not found"
+                    )
+                }
+                val ret = TalerWithdrawalStatus(
+                    selection_done = wo.selectionDone,
+                    transfer_done = wo.transferDone,
+                    amount = "${currency}:1"
+                )
+                call.respond(ret)
+                return@get
+            }
+            /**
+             * Here Sandbox collects the reserve public key to be used
+             * as the wire transfer subject, and pays the exchange - which
+             * is as well collected in this request.
+             */
+            post("/withdrawal-operation/{wopid}") {
+                val wopid = ensureNonNull("wopid")
+                val body = call.receiveJson<TalerWithdrawalConfirmation>()
+
+                transaction {
+                    var wo = TalerWithdrawalEntity.find {
+                        TalerWithdrawalsTable.wopid eq UUID.fromString(wopid)
+                    }.firstOrNull() ?: throw SandboxError(
+                        HttpStatusCode.NotFound, "Withdrawal operation $wopid 
not found."
+                    )
+                    wireTransfer(
+                        "sandbox-account-customer",
+                        "sandbox-account-exchange",
+                        "$currency:1",
+                        body.reserve_pub
+                    )
+                    wo.selectionDone = true
+                    wo.transferDone = true
+                }
+                call.respond(object {
+                    val transfer_done = true
+                })
+                return@post
+            }
         }
     }
     logger.info("LibEuFin Sandbox running on port $port")
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
index 70c86df..e42cc25 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
@@ -7,6 +7,7 @@ import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import tech.libeufin.util.*
 import java.math.BigDecimal
+import kotlin.system.exitProcess
 
 private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
 
@@ -114,3 +115,67 @@ fun historyForAccount(bankAccount: BankAccountEntity): 
MutableList<RawPayment> {
     }
     return history
 }
+
+fun wireTransfer(
+    debitAccount: String, creditAccount: String,
+    amount: String, subjectArg: String
+) {
+    // check accounts exist
+    transaction {
+        val credit = BankAccountEntity.find {
+            BankAccountsTable.label eq creditAccount
+        }.firstOrNull() ?: run {
+            throw SandboxError(HttpStatusCode.NotFound, "Credit account: 
$creditAccount, not found")
+        }
+        val debit = BankAccountEntity.find {
+            BankAccountsTable.label eq debitAccount
+        }.firstOrNull() ?: run {
+            throw SandboxError(HttpStatusCode.NotFound, "Debit account: 
$debitAccount, not found")
+        }
+        if (credit.currency != debit.currency) {
+            throw SandboxError(HttpStatusCode.InternalServerError,
+                "Sandbox has inconsistent state: " +
+                        "currency of credit (${credit.currency}) and debit 
(${debit.currency}) account differs."
+            )
+        }
+        val amountObj = try {
+            parseAmount(amount)
+        } catch (e: Exception) {
+            throw SandboxError(HttpStatusCode.BadRequest, "Amount given not 
valid: $amount")
+        }
+        if (amountObj.currency != credit.currency || amountObj.currency != 
debit.currency) {
+            throw SandboxError(HttpStatusCode.BadRequest, "currency 
(${amountObj.currency}) can't be accepted")
+        }
+        val randId = getRandomString(16)
+        BankAccountTransactionEntity.new {
+            creditorIban = credit.iban
+            creditorBic = credit.bic
+            creditorName = credit.name
+            debtorIban = debit.iban
+            debtorBic = debit.bic
+            debtorName = debit.name
+            subject = subjectArg
+            this.amount = amountObj.amount.toString()
+            currency = amountObj.currency
+            date = getUTCnow().toInstant().toEpochMilli()
+            accountServicerReference = "sandbox-$randId"
+            account = debit
+            direction = "DBIT"
+        }
+        BankAccountTransactionEntity.new {
+            creditorIban = credit.iban
+            creditorBic = credit.bic
+            creditorName = credit.name
+            debtorIban = debit.iban
+            debtorBic = debit.bic
+            debtorName = debit.name
+            subject = subjectArg
+            this.amount = amountObj.amount.toString()
+            currency = amountObj.currency
+            date = getUTCnow().toInstant().toEpochMilli()
+            accountServicerReference = "sandbox-$randId"
+            account = credit
+            direction = "CRDT"
+        }
+    }
+}
diff --git a/util/src/main/kotlin/Config.kt b/util/src/main/kotlin/Config.kt
index d7a1436..bda57ea 100644
--- a/util/src/main/kotlin/Config.kt
+++ b/util/src/main/kotlin/Config.kt
@@ -50,10 +50,10 @@ fun setLogLevel(logLevel: String?) {
     }
 }
 
-fun getHostnameFromEnv(varName: String): String? {
+fun getValueFromEnv(varName: String): String? {
     val hostName = System.getenv(varName)
     if (hostName.isNullOrBlank() or hostName.isNullOrEmpty()) {
-        println("WARNING, the hostname wasn't found in env's $varName. Will 
stay unknown")
+        println("WARNING, $varName was not found in the environment. Will stay 
unknown")
         return null
     }
     return hostName

-- 
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]