diff options
Diffstat (limited to 'sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt')
-rw-r--r-- | sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 35 |
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 }) |