summaryrefslogtreecommitdiff
path: root/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt')
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt35
1 files changed, 28 insertions, 7 deletions
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index bc826211..ad90770e 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -70,6 +70,8 @@ import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.request.*
import io.ktor.util.date.*
+import kotlinx.coroutines.newSingleThreadContext
+import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import tech.libeufin.util.*
import tech.libeufin.util.ebics_h004.EbicsResponse
import tech.libeufin.util.ebics_h004.EbicsTypes
@@ -364,6 +366,8 @@ suspend inline fun <reified T : Any> ApplicationCall.receiveJson(): T {
}
}
+val singleThreadContext = newSingleThreadContext("DB")
+
fun serverMain(dbName: String, port: Int) {
execThrowableOrTerminate { dbCreateTables(dbName) }
val myLogger = logger
@@ -983,6 +987,7 @@ fun serverMain(dbName: String, port: Int) {
// 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
+
}
}
/**
@@ -1042,30 +1047,46 @@ fun serverMain(dbName: String, port: Int) {
val wopid: String = ensureNonNull(call.parameters["wopid"])
val body = call.receiveJson<TalerWithdrawalConfirmation>()
- transaction {
+ newSuspendedTransaction(context = singleThreadContext) {
var wo = TalerWithdrawalEntity.find {
TalerWithdrawalsTable.wopid eq UUID.fromString(wopid)
}.firstOrNull() ?: throw SandboxError(
HttpStatusCode.NotFound, "Withdrawal operation $wopid not found."
)
- if (wo.transferDone) {
- throw SandboxError(
+ if (wo.selectionDone) {
+ if (wo.transferDone) {
+ logger.info("Wallet performs again this operation that was paid out earlier: idempotent")
+ return@newSuspendedTransaction
+ }
+ // reservePub+exchange selected but not payed: check consistency
+ if (body.reserve_pub != wo.reservePub) throw SandboxError(
HttpStatusCode.Conflict,
- "This withdraw operation was already funded. Aborting"
+ "Selecting a different reserve from the one already selected"
+ )
+ if (body.selected_exchange != wo.selectedExchangePayto) throw SandboxError(
+ HttpStatusCode.Conflict,
+ "Selecting a different exchange from the one already selected"
)
}
- if (wo.selectionDone) {
- logger.warn("This withdraw operation was already confirmed, but not funded. Trying again")
- }
+ // here only if (1) no selection done or (2) _only_ selection done:
+ // both ways no transfer must have happened.
+ SandboxAssert(!wo.transferDone, "Sandbox allowed paid but unselected reserve")
+
wireTransfer(
"sandbox-account-customer",
"sandbox-account-exchange",
"$currencyEnv:5",
body.reserve_pub
)
+ wo.reservePub = body.reserve_pub
+ wo.selectedExchangePayto = body.selected_exchange
wo.selectionDone = true
wo.transferDone = true
}
+ /**
+ * NOTE: is this always guaranteed to run AFTER the suspended
+ * transaction block above?
+ */
call.respond(object {
val transfer_done = true
})