gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (fe229973 -> 8de07671)


From: gnunet
Subject: [libeufin] branch master updated (fe229973 -> 8de07671)
Date: Sat, 13 Nov 2021 12:52:13 +0100

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

ms pushed a change to branch master
in repository libeufin.

    from fe229973 Fixes after wallet tests harness.
     new 2a438b1e pad IBAN checksum
     new 1c58a511 fixes after wallet harness tests
     new c384fd4a Fixes after wallet harness tests.
     new 8de07671 fix negative balance report

The 4 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    | 13 +++-
 .../tech/libeufin/sandbox/EbicsProtocolBackend.kt  | 87 ++++++++++++++--------
 .../main/kotlin/tech/libeufin/sandbox/Helpers.kt   | 31 ++++++--
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  |  2 +-
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 58 ++++++---------
 .../kotlin/tech/libeufin/sandbox/bankAccount.kt    |  6 --
 sandbox/src/test/kotlin/BalanceTest.kt             |  8 ++
 util/src/main/kotlin/iban.kt                       |  5 +-
 8 files changed, 127 insertions(+), 83 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index e5f6a695..6e86b844 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -370,10 +370,16 @@ object BankAccountsTable : IntIdTable() {
     val iban = text("iban")
     val bic = text("bic").default("SANDBOXX")
     val label = text("label").uniqueIndex("accountLabelIndex")
-    val isDebit = bool("isDebit").default(false)
     /**
-     * Allow to assign "admin" - who doesn't have a customer DB entry -
-     * as the owner.  That allows tests using the --no-auth option to go on.
+     * This field is the username of the customer that owns the
+     * bank account.  Some actors do not have a customer registered,
+     * but they can still specify their "username" - merely a label
+     * that identifies their operations - to this field.
+     *
+     * Two examples of such actors are: "admin" and "bank".  Note:
+     * "admin" cannot act as the bank, because it participates in
+     * tests and therefore should not have its balance affected by
+     * awarding sign-up bonuses.
      */
     val owner = text("owner")
     val isPublic = bool("isPublic").default(false)
@@ -386,7 +392,6 @@ class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
     var iban by BankAccountsTable.iban
     var bic by BankAccountsTable.bic
     var label by BankAccountsTable.label
-    var isDebit by BankAccountsTable.isDebit
     var owner by BankAccountsTable.owner
     var isPublic by BankAccountsTable.isPublic
     var demoBank by DemobankConfigEntity referencedOn 
BankAccountsTable.demoBank
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index 2a819a7a..0594e934 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -20,7 +20,7 @@
 
 package tech.libeufin.sandbox
 
-import io.ktor.application.ApplicationCall
+import io.ktor.application.*
 import io.ktor.http.ContentType
 import io.ktor.http.HttpStatusCode
 import io.ktor.request.receiveText
@@ -73,6 +73,11 @@ open class EbicsRequestError(
     val errorCode: String
 ) : Exception("EBICS request  error: $errorText ($errorCode)")
 
+class EbicsNoDownloadDataAvailable(camtType: Int) : EbicsRequestError(
+    "[EBICS_NO_DOWNLOAD_DATA_AVAILABLE] for Camt $camtType",
+    "090005"
+)
+
 class EbicsInvalidRequestError : EbicsRequestError(
     "[EBICS_INVALID_REQUEST] Invalid request",
     "060102"
@@ -114,6 +119,36 @@ class EbicsProcessingError(detail: String) : 
EbicsRequestError(
     "091116"
 )
 
+suspend fun respondEbicsTransfer(
+    call: ApplicationCall,
+    errorText: String,
+    errorCode: String
+) {
+    val resp = EbicsResponse.createForUploadWithError(
+        errorText,
+        errorCode,
+        // For now, phase gets hard-coded as TRANSFER,
+        // because errors during initialization should have
+        // already been caught by the chunking logic.
+        EbicsTypes.TransactionPhaseType.TRANSFER
+    )
+    val hostAuthPriv = transaction {
+        val host = EbicsHostEntity.find {
+            EbicsHostsTable.hostID.upperCase() eq 
call.attributes[EbicsHostIdAttribute]
+                .uppercase()
+        }.firstOrNull() ?: throw SandboxError(
+            io.ktor.http.HttpStatusCode.InternalServerError,
+            "Requested Ebics host ID not found."
+        )
+        CryptoUtil.loadRsaPrivateKey(host.authenticationPrivateKey.bytes)
+    }
+    call.respondText(
+        signEbicsResponse(resp, hostAuthPriv),
+        ContentType.Application.Xml,
+        HttpStatusCode.OK
+    )
+}
+
 private suspend fun ApplicationCall.respondEbicsKeyManagement(
     errorText: String,
     errorCode: String,
@@ -208,6 +243,12 @@ private fun getRelatedParty(branch: XmlElementBuilder, 
payment: RawPayment) {
     }
 }
 
+// This should fix #6269.
+private fun getCreditDebitInd(balance: BigDecimal): String {
+    if (balance < BigDecimal.ZERO) return "DBIT"
+    return "CRDT"
+}
+
 fun buildCamtString(
     type: Int,
     subscriberIban: String,
@@ -307,14 +348,10 @@ fun buildCamtString(
                         }
                         element("Amt") {
                             attribute("Ccy", "EUR")
-                            if (balancePrcd < BigDecimal.ZERO) {
-                                text(balancePrcd.abs().toPlainString())
-                            } else {
-                                text(balancePrcd.toPlainString())
-                            }
+                            text(balancePrcd.abs().toPlainString())
                         }
                         element("CdtDbtInd") {
-                            text("CRDT")
+                            text(getCreditDebitInd(balancePrcd))
                         }
                         element("Dt/Dt") {
                             // date of this balance
@@ -330,22 +367,10 @@ fun buildCamtString(
                         }
                         element("Amt") {
                             attribute("Ccy", "EUR")
-                            // FIXME: the balance computation still not 
working properly
-                            
//text(balanceForAccount(subscriberIban).toString())
-                            if (balanceClbd < BigDecimal.ZERO) {
-                                text(balanceClbd.abs().toPlainString())
-                            } else {
-                                text(balanceClbd.toPlainString())
-                            }
+                            text(balanceClbd.abs().toPlainString())
                         }
                         element("CdtDbtInd") {
-                            // a temporary value to get the camt to validate.
-                            // Should be fixed along #6269
-                            if (balanceClbd < BigDecimal.ZERO) {
-                                text("DBIT")
-                            } else {
-                                text("CRDT")
-                            }
+                            text(getCreditDebitInd(balanceClbd))
                         }
                         element("Dt/Dt") {
                             text(dashedDate)
@@ -549,10 +574,8 @@ private fun constructCamtResponse(
             }
         }
     }
-    if (ret.size == 0) throw EbicsRequestError(
-        "[EBICS_NO_DOWNLOAD_DATA_AVAILABLE] as Camt $type",
-        "090005"
-    )
+    if (ret.size == 0) throw EbicsNoDownloadDataAvailable(type)
+
     return ret
 }
 
@@ -720,9 +743,10 @@ private fun handleEbicsC52(requestContext: 
RequestContext): ByteArray {
         report.size == 1,
         "C52 response does not contain one Camt.052 document"
     )
-    if (!XMLUtil.validateFromString(report[0])) throw EbicsProcessingError(
-        "One statement was found invalid."
-    )
+    if (!XMLUtil.validateFromString(report[0])) {
+        logger.error("This document was generated invalid:\n${report[0]}")
+        throw EbicsProcessingError("One outgoing report was found invalid.")
+    }
     return report.map { it.toByteArray() }.zip()
 }
 
@@ -754,9 +778,10 @@ private fun handleEbicsC53(requestContext: 
RequestContext): ByteArray {
         dateRange
     )
     camtStatements.forEach {
-        if (!XMLUtil.validateFromString(it)) throw EbicsProcessingError(
-            "One statement was found invalid."
-        )
+        if (!XMLUtil.validateFromString(it)) {
+            logger.error("This document was generated invalid:\n$it")
+            throw EbicsProcessingError("One outgoing statement was found 
invalid.")
+        }
     }
     return camtStatements.map { it.toByteArray() }.zip()
 }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 2e927000..5451ca25 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -133,8 +133,10 @@ fun getCustomer(username: String): DemobankCustomerEntity {
  * Get person name from a customer's username.
  */
 fun getPersonNameFromCustomer(ownerUsername: String): String {
-    return if (ownerUsername == "admin") "admin" else {
-        return transaction {
+    return when (ownerUsername) {
+        "admin" -> "admin" // Could be changed to Admin, or some different 
value.
+        "bank" -> "The Bank"
+        else -> transaction {
             val ownerCustomer = DemobankCustomerEntity.find(
                 DemobankCustomersTable.username eq ownerUsername
             ).firstOrNull() ?: throw internalServerError(
@@ -144,6 +146,15 @@ fun getPersonNameFromCustomer(ownerUsername: String): 
String {
         }
     }
 }
+fun getFirstDemobank(): DemobankConfigEntity {
+  return transaction {
+      DemobankConfigEntity.all().firstOrNull() ?: throw SandboxError(
+          HttpStatusCode.InternalServerError,
+          "Cannot find one demobank, please create one!"
+      )
+  }
+}
+
 fun getDefaultDemobank(): DemobankConfigEntity {
     return transaction {
         DemobankConfigEntity.find {
@@ -321,17 +332,25 @@ fun getBankAccountFromSubscriber(subscriber: 
EbicsSubscriberEntity): BankAccount
     }
 }
 
+fun BankAccountEntity.bonus(amount: String) {
+    wireTransfer(
+        "bank",
+        this.label,
+        this.demoBank.name,
+        "Sign-up bonus",
+        amount
+    )
+}
+
 fun ensureDemobank(call: ApplicationCall): DemobankConfigEntity {
     return ensureDemobank(call.getUriComponent("demobankid"))
 }
 
 private fun ensureDemobank(name: String): DemobankConfigEntity {
     return transaction {
-        val res = DemobankConfigEntity.find {
+        DemobankConfigEntity.find {
             DemobankConfigsTable.name eq name
-        }.firstOrNull()
-        if (res == null) throw internalServerError("Demobank '$name' never 
created")
-        res
+        }.firstOrNull() ?: throw internalServerError("Demobank '$name' never 
created")
     }
 }
 
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 1da00738..e85e3fbb 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -123,7 +123,7 @@ data class TalerWithdrawalStatus(
     val wire_types: List<String> = listOf("iban"),
     val suggested_exchange: String? = null,
     val sender_wire: String? = null,
-    val aborted: Boolean = false,
+    val aborted: Boolean
 )
 
 data class TalerWithdrawalSelection(
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 2e405b40..77467916 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -310,6 +310,18 @@ class Serve : CliktCommand("Run sandbox HTTP server") {
             )
             exitProcess(0)
         }
+        /**
+         * Create the bank's bank account, to award the 100 Kudos
+         * when new customers open bank account.  */
+        transaction {
+            BankAccountEntity.new {
+                iban = getIban()
+                label = "bank" // used by the wire helper
+                owner = "bank" // used by the person name finder
+                // For now, the model assumes always one demobank
+                demoBank = getFirstDemobank()
+            }
+        }
         serverMain(port)
     }
 }
@@ -437,7 +449,6 @@ val sandboxApp: Application.() -> Unit = {
                 indentObjectsWith(DefaultIndenter("  ", "\n"))
             })
             registerModule(KotlinModule(nullisSameAsDefault = true))
-            //registerModule(JavaTimeModule())
         }
     }
     install(StatusPages) {
@@ -477,29 +488,7 @@ val sandboxApp: Application.() -> Unit = {
         }
         exception<EbicsRequestError> { e ->
             logger.debug("Handling EbicsRequestError: $e")
-            val resp = 
tech.libeufin.util.ebics_h004.EbicsResponse.createForUploadWithError(
-                e.errorText,
-                e.errorCode,
-                // assuming that the phase is always transfer,
-                // as errors during initialization should have
-                // already been caught by the chunking logic.
-                
tech.libeufin.util.ebics_h004.EbicsTypes.TransactionPhaseType.TRANSFER
-            )
-            val hostAuthPriv = transaction {
-                val host = EbicsHostEntity.find {
-                    EbicsHostsTable.hostID.upperCase() eq 
call.attributes.get(tech.libeufin.sandbox.EbicsHostIdAttribute)
-                        .uppercase()
-                }.firstOrNull() ?: throw SandboxError(
-                    io.ktor.http.HttpStatusCode.InternalServerError,
-                    "Requested Ebics host ID not found."
-                )
-                
CryptoUtil.loadRsaPrivateKey(host.authenticationPrivateKey.bytes)
-            }
-            call.respondText(
-                XMLUtil.signEbicsResponse(resp, hostAuthPriv),
-                ContentType.Application.Xml,
-                HttpStatusCode.OK
-            )
+            respondEbicsTransfer(call, e.errorText, e.errorCode)
         }
         exception<Throwable> { cause ->
             logger.error("Exception while handling '${call.request.uri}'", 
cause)
@@ -938,6 +927,9 @@ val sandboxApp: Application.() -> Unit = {
                     else -> throw EbicsProcessingError("Unknown LibEuFin error 
code: ${e.errorCode}.")
                 }
             }
+            catch (e: EbicsNoDownloadDataAvailable) {
+                respondEbicsTransfer(call, e.errorText, e.errorCode)
+            }
             catch (e: EbicsRequestError) {
                 logger.error(e)
                 // Preventing the last catch-all block
@@ -1082,7 +1074,8 @@ val sandboxApp: Application.() -> Unit = {
                         selection_done = wo.selectionDone,
                         transfer_done = wo.confirmationDone,
                         amount = "${demobank.currency}:${wo.amount}",
-                        suggested_exchange = demobank.suggestedExchangeBaseUrl
+                        suggested_exchange = demobank.suggestedExchangeBaseUrl,
+                        aborted = wo.aborted
                     )
                     call.respond(ret)
                     return@get
@@ -1234,16 +1227,11 @@ val sandboxApp: Application.() -> Unit = {
                     ) throw forbidden(
                             "Customer '$username' cannot access bank account 
'$accountAccessed'"
                         )
-                    val creditDebitIndicator = if (bankAccount.isDebit) {
-                        "debit"
-                    } else {
-                        "credit"
-                    }
                     val balance = balanceForAccount(bankAccount)
                     call.respond(object {
-                        val balance = {
-                            val amount = "${demobank.currency}:${balance}"
-                            val credit_debit_indicator = creditDebitIndicator
+                        val balance = object {
+                            val amount = 
"${demobank.currency}:${balance.abs(). toPlainString()}"
+                            val credit_debit_indicator = if (balance < 
BigDecimal.ZERO) "debit" else "credit"
                         }
                         val paytoUri = buildIbanPaytoUri(
                             iban = bankAccount.iban,
@@ -1332,11 +1320,13 @@ val sandboxApp: Application.() -> Unit = {
                             username = req.username
                             passwordHash = CryptoUtil.hashpw(req.password)
                         }
+                        bankAccount.bonus("${demobank.currency}:100")
                         bankAccount
                     }
+                    val balance = balanceForAccount(bankAccount)
                     call.respond(object {
                         val balance = {
-                            val amount = "${demobank.currency}:0"
+                            val amount = "${demobank.currency}:${balance}"
                             val credit_debit_indicator = "CRDT"
                         }
                         val paytoUri = buildIbanPaytoUri(
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
index b775bac2..873371d0 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
@@ -54,12 +54,6 @@ fun balanceForAccount(bankAccount: BankAccountEntity): 
BigDecimal {
             balance -= amount
         }
     }
-    /**
-     * FIXME: for negative accounts, temporarily return 0, so as to make
-     * the current CAMT generator happy.  Negative amounts need to have their
-     * onw sub-tree in the report, see bug: #6962
-     */
-    if (balance < BigDecimal.ZERO) return BigDecimal.ZERO
     return balance
 }
 
diff --git a/sandbox/src/test/kotlin/BalanceTest.kt 
b/sandbox/src/test/kotlin/BalanceTest.kt
index ef0ac19d..956a2e90 100644
--- a/sandbox/src/test/kotlin/BalanceTest.kt
+++ b/sandbox/src/test/kotlin/BalanceTest.kt
@@ -4,6 +4,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
 import org.junit.Test
 import tech.libeufin.sandbox.*
 import tech.libeufin.util.millis
+import java.math.BigDecimal
 import java.time.LocalDateTime
 
 class BalanceTest {
@@ -86,4 +87,11 @@ class BalanceTest {
             }
         }
     }
+    @Test
+    fun balanceAbsTest() {
+        val minus = BigDecimal.ZERO - BigDecimal.ONE
+        val plus = BigDecimal.ONE
+        println(minus.abs().toPlainString())
+        println(plus.abs().toPlainString())
+    }
 }
diff --git a/util/src/main/kotlin/iban.kt b/util/src/main/kotlin/iban.kt
index cf1fc005..b2de9351 100644
--- a/util/src/main/kotlin/iban.kt
+++ b/util/src/main/kotlin/iban.kt
@@ -7,6 +7,9 @@ fun getIban(): String {
     val bban = (0..3).map {
         (0..9).random()
     }.joinToString("") // 4 digits BBAN.
-    val checkDigits = 
"98".toBigInteger().minus("$bban$ccNoCheck".toBigInteger().mod("97".toBigInteger()))
+    var checkDigits = 
"98".toBigInteger().minus("$bban$ccNoCheck".toBigInteger().mod("97".toBigInteger())).toString()
+    if (checkDigits.length == 1) {
+        checkDigits = "0${checkDigits}"
+    }
     return "DE$checkDigits$bban"
 }
\ No newline at end of file

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