[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.