gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (f58e802 -> f629b3a)


From: gnunet
Subject: [libeufin] branch master updated (f58e802 -> f629b3a)
Date: Sat, 18 Sep 2021 09:13:05 +0200

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

ms pushed a change to branch master
in repository libeufin.

    from f58e802  same, at Nexus
     new 94740f4  Activating withdrawals from the Sandbox.
     new f629b3a  Implement /withdrawal-operation.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |  26 ++++
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  |  14 ++
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 168 +++++++++++++--------
 .../kotlin/tech/libeufin/sandbox/bankAccount.kt    |  65 ++++++++
 util/src/main/kotlin/Config.kt                     |   9 ++
 5 files changed, 220 insertions(+), 62 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 9c0a279..021c231 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -407,6 +407,30 @@ class BankAccountStatementEntity(id: EntityID<Int>) : 
IntEntity(id) {
     var balanceClbd by BankAccountStatementsTable.balanceClbd
 }
 
+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() {
     val reportId = text("reportId")
     val creationTime = long("creationTime")
@@ -414,6 +438,8 @@ object BankAccountReportsTable : IntIdTable() {
     val bankAccount = reference("bankAccount", BankAccountsTable)
 }
 
+
+
 fun dbDropTables(dbConnectionString: String) {
     Database.connect(dbConnectionString)
     transaction {
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 4a01d5f..7870a8a 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -83,8 +83,10 @@ import java.util.*
 import kotlin.random.Random
 import kotlin.system.exitProcess
 
-const val SANDBOX_DB_ENV_VAR_NAME = "LIBEUFIN_SANDBOX_DB_CONNECTION"
 private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
+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(
     val statusCode: HttpStatusCode,
@@ -226,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)
         }
     }
 }
@@ -994,6 +944,100 @@ fun serverMain(dbName: String, port: Int) {
                 }
 
             }
+            /**
+             * Activates a withdraw operation of 1 currency unit with
+             * the default exchange, from a designated/constant customer.
+             */
+            get("/taler") {
+                requireSuperuser(call.request)
+                SandboxAssert(
+                    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 {
+                        BankAccountsTable.label eq "sandbox-account-exchange"
+                    }.firstOrNull()
+                    val customer = BankAccountEntity.find {
+                        BankAccountsTable.label eq "sandbox-account-customer"
+                    }.firstOrNull()
+                    val merchant = BankAccountEntity.find {
+                        BankAccountsTable.label eq "sandbox-account-merchant"
+                    }.firstOrNull()
+
+                    SandboxAssert(exchange != null, "exchange has no bank 
account")
+                    SandboxAssert(customer != null, "customer has no bank 
account")
+                    SandboxAssert(merchant != null, "merchant has no bank 
account")
+
+                    // At this point, the three actors exist and a new 
withdraw operation can be created.
+                    TalerWithdrawalEntity.new {
+                        // wopid is autogenerated, and momentarily the only 
column
+                    }
+                }
+                /**
+                 * Future versions will include the QR code in this response.
+                 */
+                
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 800ae69..bda57ea 100644
--- a/util/src/main/kotlin/Config.kt
+++ b/util/src/main/kotlin/Config.kt
@@ -50,6 +50,15 @@ fun setLogLevel(logLevel: String?) {
     }
 }
 
+fun getValueFromEnv(varName: String): String? {
+    val hostName = System.getenv(varName)
+    if (hostName.isNullOrBlank() or hostName.isNullOrEmpty()) {
+        println("WARNING, $varName was not found in the environment. Will stay 
unknown")
+        return null
+    }
+    return hostName
+}
+
 fun getDbConnFromEnv(varName: String): String {
     val dbConnStr = System.getenv(varName)
     if (dbConnStr.isNullOrBlank() or dbConnStr.isNullOrEmpty()) {

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