gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (b421a7b2 -> 86490766)


From: gnunet
Subject: [libeufin] branch master updated (b421a7b2 -> 86490766)
Date: Sun, 25 Feb 2024 19:08:39 +0100

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

antoine pushed a change to branch master
in repository libeufin.

    from b421a7b2 version 0.9.4-dev.27
     new 0067499c Fix nexus failing to parse wellformed incoming transactions 
subject and share more code between nexus and bank
     new 86490766 latest from prebuilt

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:
 .../main/kotlin/tech/libeufin/bank/CoreBankApi.kt  |  15 ++-
 .../main/kotlin/tech/libeufin/bank/TalerCommon.kt  | 119 +-------------------
 .../main/kotlin/tech/libeufin/bank/TalerMessage.kt |   4 +-
 .../kotlin/tech/libeufin/bank/db/ExchangeDAO.kt    |   7 +-
 .../kotlin/tech/libeufin/bank/db/TransactionDAO.kt |  36 +++---
 bank/src/test/kotlin/BankIntegrationApiTest.kt     |  13 +--
 bank/src/test/kotlin/CoreBankApiTest.kt            |  14 +--
 bank/src/test/kotlin/DatabaseTest.kt               |   2 +-
 bank/src/test/kotlin/StatsTest.kt                  |   7 +-
 bank/src/test/kotlin/WireGatewayApiTest.kt         |  30 +++--
 bank/src/test/kotlin/helpers.kt                    |  22 +---
 common/src/main/kotlin/CryptoUtil.kt               |   9 --
 common/src/main/kotlin/TalerCommon.kt              | 121 ++++++++++++++++++++-
 .../src/main/kotlin/TxMedatada.kt                  |  15 +--
 common/src/main/kotlin/{Constants.kt => random.kt} |  17 ++-
 common/src/test/kotlin/CryptoUtilTest.kt           |   8 --
 contrib/ci/jobs/0-codespell/job.sh                 |   1 +
 contrib/wallet-core                                |   2 +-
 .../main/kotlin/tech/libeufin/nexus/Database.kt    |   8 +-
 .../main/kotlin/tech/libeufin/nexus/EbicsFetch.kt  | 100 ++++-------------
 .../main/kotlin/tech/libeufin/nexus/Iso20022.kt    |   2 +-
 nexus/src/test/kotlin/DatabaseTest.kt              |   5 +-
 nexus/src/test/kotlin/Parsing.kt                   |  58 +++-------
 testbench/src/main/kotlin/Main.kt                  |   6 -
 testbench/src/test/kotlin/IntegrationTest.kt       |  22 ++--
 25 files changed, 262 insertions(+), 381 deletions(-)
 rename bank/src/main/kotlin/tech/libeufin/bank/Metadata.kt => 
common/src/main/kotlin/TxMedatada.kt (68%)
 copy common/src/main/kotlin/{Constants.kt => random.kt} (83%)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
index dbb550ac..c2efaf76 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -43,7 +43,6 @@ import java.time.Duration
 import java.time.Instant
 import java.time.temporal.ChronoUnit
 import java.util.*
-import kotlin.random.Random
 
 private val logger: Logger = LoggerFactory.getLogger("libeufin-bank-api")
 
@@ -96,7 +95,7 @@ private fun Routing.coreBankTokenApi(db: Database) {
                         TalerErrorCode.GENERIC_TOKEN_PERMISSION_INSUFFICIENT
                     )
             }
-            val tokenBytes = ByteArray(32).apply { Random.nextBytes(this) }
+            val token = Base32Crockford32B.rand()
             val tokenDuration: Duration = req.duration?.d_us ?: 
TOKEN_DEFAULT_DURATION
 
             val creationTime = Instant.now()
@@ -114,7 +113,7 @@ private fun Routing.coreBankTokenApi(db: Database) {
                 }
             if (!db.token.create(
                 login = username,
-                content = tokenBytes,
+                content = token.raw,
                 creationTime = creationTime,
                 expirationTime = expirationTimestamp,
                 scope = req.scope,
@@ -124,7 +123,7 @@ private fun Routing.coreBankTokenApi(db: Database) {
             }
             call.respond(
                 TokenSuccessResponse(
-                    access_token = Base32Crockford.encode(tokenBytes),
+                    access_token = token.encoded(),
                     expiration = TalerProtocolTimestamp(t_s = 
expirationTimestamp)
                 )
             )
@@ -677,9 +676,13 @@ private fun Routing.coreBankTanApi(db: Database, ctx: 
BankConfig) {
                             } catch (e: Exception) {
                                 process.destroy()
                             }
-                            val exitValue =  process.exitValue()
+                            val exitValue = process.exitValue()
                             if (exitValue != 0) {
-                                val out = 
process.getInputStream().reader().readText() 
+                                val out = runCatching {
+                                    process.getInputStream().use {
+                                        reader().readText()
+                                    }
+                                }.getOrDefault("")
                                 if (out.isNotEmpty()) {
                                     logger.error("TAN ${res.tanChannel} - 
${tanScript}: $out")
                                 }
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
index de0ebc20..93d649ee 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
@@ -1,6 +1,6 @@
 /*
  * This file is part of LibEuFin.
- * Copyright (C) 2023 Taler Systems S.A.
+ * Copyright (C) 2023-2024 Taler Systems S.A.
 
  * LibEuFin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -39,123 +39,6 @@ import java.time.Instant
 import java.time.temporal.ChronoUnit
 import java.util.concurrent.TimeUnit
 
-/** 32-byte Crockford's Base32 encoded data */
-@Serializable(with = Base32Crockford32B.Serializer::class)
-class Base32Crockford32B {
-    private var encoded: String? = null
-    val raw: ByteArray
-
-    constructor(encoded: String) {
-        val decoded = try {
-            Base32Crockford.decode(encoded) 
-        } catch (e: EncodingException) {
-            null
-        }
-        
-        require(decoded != null) {
-            "Data should be encoded using Crockford's Base32"
-        }
-        require(decoded.size == 32) {
-            "Encoded data should be 32 bytes long"
-        }
-        this.raw = decoded
-        this.encoded = encoded
-    }
-    constructor(raw: ByteArray) {
-        require(raw.size == 32) {
-            "Encoded data should be 32 bytes long"
-        }
-        this.raw = raw
-    }
-
-    fun encoded(): String {
-        encoded = encoded ?: Base32Crockford.encode(raw)
-        return encoded!!
-    }
-
-    override fun toString(): String {
-        return encoded()
-    }
-
-    override fun equals(other: Any?) = (other is Base32Crockford32B) && 
raw.contentEquals(other.raw)
-
-    internal object Serializer : KSerializer<Base32Crockford32B> {
-        override val descriptor: SerialDescriptor = 
PrimitiveSerialDescriptor("Base32Crockford32B", PrimitiveKind.STRING)
-    
-        override fun serialize(encoder: Encoder, value: Base32Crockford32B) {
-            encoder.encodeString(value.encoded())
-        }
-    
-        override fun deserialize(decoder: Decoder): Base32Crockford32B {
-            return Base32Crockford32B(decoder.decodeString())
-        }
-    }
-}
-
-/** 64-byte Crockford's Base32 encoded data */
-@Serializable(with = Base32Crockford64B.Serializer::class)
-class Base32Crockford64B {
-    private var encoded: String? = null
-    val raw: ByteArray
-
-    constructor(encoded: String) {
-        val decoded = try {
-            Base32Crockford.decode(encoded) 
-        } catch (e: EncodingException) {
-            null
-        }
-        
-        require(decoded != null) {
-            "Data should be encoded using Crockford's Base32"
-        }
-        require(decoded.size == 64) {
-            "Encoded data should be 32 bytes long"
-        }
-        this.raw = decoded
-        this.encoded = encoded
-    }
-    constructor(raw: ByteArray) {
-        require(raw.size == 64) {
-            "Encoded data should be 32 bytes long"
-        }
-        this.raw = raw
-    }
-
-    fun encoded(): String {
-        encoded = encoded ?: Base32Crockford.encode(raw)
-        return encoded!!
-    }
-
-    override fun toString(): String {
-        return encoded()
-    }
-
-    override fun equals(other: Any?) = (other is Base32Crockford64B) && 
raw.contentEquals(other.raw)
-
-    internal object Serializer : KSerializer<Base32Crockford64B> {
-        override val descriptor: SerialDescriptor = 
PrimitiveSerialDescriptor("Base32Crockford64B", PrimitiveKind.STRING)
-    
-        override fun serialize(encoder: Encoder, value: Base32Crockford64B) {
-            encoder.encodeString(value.encoded())
-        }
-    
-        override fun deserialize(decoder: Decoder): Base32Crockford64B {
-            return Base32Crockford64B(decoder.decodeString())
-        }
-    }
-}
-
-/** 32-byte hash code */
-typealias ShortHashCode = Base32Crockford32B
-/** 64-byte hash code */
-typealias HashCode = Base32Crockford64B
-/**
- * EdDSA and ECDHE public keys always point on Curve25519
- * and represented  using the standard 256 bits Ed25519 compact format,
- * converted to Crockford Base32.
- */
-typealias EddsaPublicKey = Base32Crockford32B
-
 /** Timestamp containing the number of seconds since epoch */
 @Serializable
 data class TalerProtocolTimestamp(
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
index 5f98ae07..facc26d6 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -26,9 +26,7 @@ import kotlinx.serialization.Serializable
 import kotlinx.serialization.descriptors.SerialDescriptor
 import kotlinx.serialization.encoding.Decoder
 import kotlinx.serialization.encoding.Encoder
-import tech.libeufin.common.IbanPayto
-import tech.libeufin.common.Payto
-import tech.libeufin.common.TalerAmount
+import tech.libeufin.common.*
 import java.time.Instant
 
 /**
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt
index af4639df..6540172a 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt
@@ -1,6 +1,6 @@
 /*
  * This file is part of LibEuFin.
- * Copyright (C) 2023 Taler Systems S.A.
+ * Copyright (C) 2023-2024 Taler Systems S.A.
 
  * LibEuFin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -20,10 +20,7 @@
 package tech.libeufin.bank.db
 
 import tech.libeufin.bank.*
-import tech.libeufin.common.BankPaytoCtx
-import tech.libeufin.common.getAmount
-import tech.libeufin.common.getBankPayto
-import tech.libeufin.common.toDbMicros
+import tech.libeufin.common.*
 import java.time.Instant
 
 /** Data access logic for exchange specific logic */
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
index be427b66..9b03cd2d 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
@@ -1,6 +1,6 @@
 /*
  * This file is part of LibEuFin.
- * Copyright (C) 2023 Taler Systems S.A.
+ * Copyright (C) 2023-2024 Taler Systems S.A.
 
  * LibEuFin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -94,23 +94,25 @@ class TransactionDAO(private val db: Database) {
                         if (exchangeCreditor && exchangeDebtor) {
                             logger.warn("exchange account $exchangeDebtor sent 
a manual transaction to exchange account $exchangeCreditor, this should never 
happens and is not bounced to prevent bouncing loop, may fail in the future")
                         } else if (exchangeCreditor) {
-                            val reservePub = parseIncomingTxMetadata(subject)
-                            val bounceCause = if (reservePub != null) {
-                                val registered = conn.prepareStatement("CALL 
register_incoming(?, ?)").run {
-                                    setBytes(1, reservePub.raw)
-                                    setLong(2, creditRowId)
-                                    executeProcedureViolation()
+                            val bounceCause = runCatching { 
parseIncomingTxMetadata(subject) }.fold(
+                                onSuccess = { reservePub -> 
+                                    val registered = 
conn.prepareStatement("CALL register_incoming(?, ?)").run {
+                                        setBytes(1, reservePub.raw)
+                                        setLong(2, creditRowId)
+                                        executeProcedureViolation()
+                                    }
+                                    if (!registered) {
+                                        logger.warn("exchange account 
$creditAccountId received an incoming taler transaction $creditRowId with an 
already used reserve public key")
+                                        "reserve public key reuse"
+                                    } else {
+                                        null
+                                    }
+                                },
+                                onFailure = { e ->
+                                    logger.warn("exchange account 
$creditAccountId received a manual transaction $creditRowId with malformed 
metadata: ${e.message}")
+                                    "malformed metadata: ${e.message}"
                                 }
-                                if (!registered) {
-                                    logger.warn("exchange account 
$creditAccountId received an incoming taler transaction $creditRowId with an 
already used reserve public key")
-                                    "reserve public key reuse"
-                                } else {
-                                    null
-                                }
-                            } else {
-                                logger.warn("exchange account $creditAccountId 
received a manual transaction $creditRowId with malformed metadata")
-                                "malformed metadata"
-                            }
+                            )
                             if (bounceCause != null) {
                                 // No error can happens because an opposite 
transaction already took place in the same transaction
                                 conn.prepareStatement("""
diff --git a/bank/src/test/kotlin/BankIntegrationApiTest.kt 
b/bank/src/test/kotlin/BankIntegrationApiTest.kt
index f84c382c..ba5ff624 100644
--- a/bank/src/test/kotlin/BankIntegrationApiTest.kt
+++ b/bank/src/test/kotlin/BankIntegrationApiTest.kt
@@ -23,10 +23,7 @@ import tech.libeufin.bank.BankAccountCreateWithdrawalResponse
 import tech.libeufin.bank.BankWithdrawalOperationPostResponse
 import tech.libeufin.bank.BankWithdrawalOperationStatus
 import tech.libeufin.bank.WithdrawalStatus
-import tech.libeufin.common.TalerAmount
-import tech.libeufin.common.TalerErrorCode
-import tech.libeufin.common.json
-import tech.libeufin.common.obj
+import tech.libeufin.common.*
 import java.util.*
 import kotlin.test.assertEquals
 
@@ -72,7 +69,7 @@ class BankIntegrationApiTest {
     // POST /taler-integration/withdrawal-operation/UUID
     @Test
     fun select() = bankSetup { _ ->
-        val reserve_pub = randEddsaPublicKey()
+        val reserve_pub = EddsaPublicKey.rand()
         val req = obj {
             "reserve_pub" to reserve_pub
             "selected_exchange" to exchangePayto.canonical
@@ -110,7 +107,7 @@ class BankIntegrationApiTest {
             // Check already selected
             client.post("/taler-integration/withdrawal-operation/$uuid") {
                 json(req) {
-                    "reserve_pub" to randEddsaPublicKey()
+                    "reserve_pub" to EddsaPublicKey.rand()
                 }
             
}.assertConflict(TalerErrorCode.BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT)
         }   
@@ -127,14 +124,14 @@ class BankIntegrationApiTest {
             // Check unknown account
             client.post("/taler-integration/withdrawal-operation/$uuid") {
                 json {
-                    "reserve_pub" to randEddsaPublicKey()
+                    "reserve_pub" to EddsaPublicKey.rand()
                     "selected_exchange" to unknownPayto
                 }
             }.assertConflict(TalerErrorCode.BANK_UNKNOWN_ACCOUNT)
             // Check account not exchange
             client.post("/taler-integration/withdrawal-operation/$uuid") {
                 json {
-                    "reserve_pub" to randEddsaPublicKey()
+                    "reserve_pub" to EddsaPublicKey.rand()
                     "selected_exchange" to merchantPayto
                 }
             }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE)
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt 
b/bank/src/test/kotlin/CoreBankApiTest.kt
index 47f4f53f..fbc3492a 100644
--- a/bank/src/test/kotlin/CoreBankApiTest.kt
+++ b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -942,7 +942,7 @@ class CoreBankTransactionsApiTest {
         assertBalance("exchange", "+KUDOS:0")
         tx("merchant", "KUDOS:1", "exchange", "") // Bounce common to 
transaction
         tx("merchant", "KUDOS:1", "exchange", "Malformed") // Bounce malformed 
transaction
-        val reservePub = randEddsaPublicKey()
+        val reservePub = EddsaPublicKey.rand()
         tx("merchant", "KUDOS:1", "exchange", randIncomingSubject(reservePub)) 
// Accept incoming
         tx("merchant", "KUDOS:1", "exchange", randIncomingSubject(reservePub)) 
// Bounce reserve_pub reuse
         assertBalance("merchant", "-KUDOS:1")
@@ -953,7 +953,7 @@ class CoreBankTransactionsApiTest {
         assertBalance("exchange", "+KUDOS:1")
         tx("exchange", "KUDOS:1", "merchant", "") // Warn common to transaction
         tx("exchange", "KUDOS:1", "merchant", "Malformed") // Warn malformed 
transaction
-        val wtid = randShortHashCode()
+        val wtid = ShortHashCode.rand()
         val exchange = ExchangeUrl("http://exchange.example.com/";)
         tx("exchange", "KUDOS:1", "merchant", randOutgoingSubject(wtid, 
exchange)) // Accept outgoing
         tx("exchange", "KUDOS:1", "merchant", randOutgoingSubject(wtid, 
exchange)) // Warn wtid reuse
@@ -1129,7 +1129,7 @@ class CoreBankCashoutApiTest {
         authRoutine(HttpMethod.Post, "/accounts/merchant/cashouts")
 
         val req = obj {
-            "request_uid" to randShortHashCode()
+            "request_uid" to ShortHashCode.rand()
             "amount_debit" to "KUDOS:1"
             "amount_credit" to convert("KUDOS:1")
         }
@@ -1162,7 +1162,7 @@ class CoreBankCashoutApiTest {
         // Check insufficient fund
         client.postA("/accounts/customer/cashouts") {
             json(req) {
-                "request_uid" to randShortHashCode()
+                "request_uid" to ShortHashCode.rand()
                 "amount_debit" to "KUDOS:75"
                 "amount_credit" to convert("KUDOS:75")
             }
@@ -1192,7 +1192,7 @@ class CoreBankCashoutApiTest {
         assertBalance("customer", "-KUDOS:1")
         client.postA("/accounts/customer/cashouts") {
             json(req) {
-                "request_uid" to randShortHashCode()
+                "request_uid" to ShortHashCode.rand()
             }
         }.assertChallenge { _,_->
             assertBalance("customer", "-KUDOS:1")
@@ -1216,7 +1216,7 @@ class CoreBankCashoutApiTest {
 
         // Check confirm
         client.postA("/accounts/customer/cashouts") {
-            json(req) { "request_uid" to randShortHashCode() }
+            json(req) { "request_uid" to ShortHashCode.rand() }
         }.assertOkJson<CashoutResponse> {
             val id = it.cashout_id
             client.getA("/accounts/customer/cashouts/$id")
@@ -1239,7 +1239,7 @@ class CoreBankCashoutApiTest {
 
         // Check get another user's operation
         client.postA("/accounts/customer/cashouts") {
-            json(req) { "request_uid" to randShortHashCode() }
+            json(req) { "request_uid" to ShortHashCode.rand() }
         }.assertOkJson<CashoutResponse> {
             val id = it.cashout_id
 
diff --git a/bank/src/test/kotlin/DatabaseTest.kt 
b/bank/src/test/kotlin/DatabaseTest.kt
index 3a95d6eb..e41069f7 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -22,7 +22,7 @@ import kotlinx.coroutines.launch
 import org.junit.Test
 import tech.libeufin.bank.createAdminAccount
 import tech.libeufin.bank.db.AccountDAO.AccountCreationResult
-import tech.libeufin.common.oneOrNull
+import tech.libeufin.common.*
 import java.time.Duration
 import java.time.Instant
 import java.time.temporal.ChronoUnit
diff --git a/bank/src/test/kotlin/StatsTest.kt 
b/bank/src/test/kotlin/StatsTest.kt
index edbd26b6..3f235d4f 100644
--- a/bank/src/test/kotlin/StatsTest.kt
+++ b/bank/src/test/kotlin/StatsTest.kt
@@ -22,10 +22,7 @@ import org.junit.Test
 import tech.libeufin.bank.MonitorResponse
 import tech.libeufin.bank.MonitorWithConversion
 import tech.libeufin.bank.Timeframe
-import tech.libeufin.common.TalerAmount
-import tech.libeufin.common.executeQueryCheck
-import tech.libeufin.common.oneOrNull
-import tech.libeufin.common.toDbMicros
+import tech.libeufin.common.*
 import java.time.Instant
 import java.time.LocalDateTime
 import kotlin.test.assertEquals
@@ -41,7 +38,7 @@ class StatsTest {
             db.conn { conn ->
                 val stmt = conn.prepareStatement("SELECT 0 FROM cashin(?, ?, 
(?, ?)::taler_amount, ?)")
                 stmt.setLong(1, Instant.now().toDbMicros()!!)
-                stmt.setBytes(2, randShortHashCode().raw)
+                stmt.setBytes(2, ShortHashCode.rand().raw)
                 val amount = TalerAmount(amount)
                 stmt.setLong(3, amount.value)
                 stmt.setInt(4, amount.frac)
diff --git a/bank/src/test/kotlin/WireGatewayApiTest.kt 
b/bank/src/test/kotlin/WireGatewayApiTest.kt
index 676e455b..82154663 100644
--- a/bank/src/test/kotlin/WireGatewayApiTest.kt
+++ b/bank/src/test/kotlin/WireGatewayApiTest.kt
@@ -21,9 +21,7 @@ import io.ktor.http.*
 import org.junit.Test
 import tech.libeufin.bank.IncomingHistory
 import tech.libeufin.bank.OutgoingHistory
-import tech.libeufin.common.TalerErrorCode
-import tech.libeufin.common.json
-import tech.libeufin.common.obj
+import tech.libeufin.common.*
 
 class WireGatewayApiTest {
     // GET /accounts/{USERNAME}/taler-wire-gateway/config
@@ -38,10 +36,10 @@ class WireGatewayApiTest {
     @Test
     fun transfer() = bankSetup { _ -> 
         val valid_req = obj {
-            "request_uid" to randHashCode()
+            "request_uid" to HashCode.rand()
             "amount" to "KUDOS:55"
             "exchange_base_url" to "http://exchange.example.com/";
-            "wtid" to randShortHashCode()
+            "wtid" to ShortHashCode.rand()
             "credit_account" to merchantPayto.canonical
         }
 
@@ -66,7 +64,7 @@ class WireGatewayApiTest {
         // Trigger conflict due to reused request_uid
         client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
             json(valid_req) { 
-                "wtid" to randShortHashCode()
+                "wtid" to ShortHashCode.rand()
                 "exchange_base_url" to "http://different-exchange.example.com/";
             }
         }.assertConflict(TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED)
@@ -81,8 +79,8 @@ class WireGatewayApiTest {
         // Unknown account
         client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
             json(valid_req) { 
-                "request_uid" to randHashCode()
-                "wtid" to randShortHashCode()
+                "request_uid" to HashCode.rand()
+                "wtid" to ShortHashCode.rand()
                 "credit_account" to unknownPayto
             }
         }.assertConflict(TalerErrorCode.BANK_UNKNOWN_CREDITOR)
@@ -90,8 +88,8 @@ class WireGatewayApiTest {
         // Same account
         client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
             json(valid_req) { 
-                "request_uid" to randHashCode()
-                "wtid" to randShortHashCode()
+                "request_uid" to HashCode.rand()
+                "wtid" to ShortHashCode.rand()
                 "credit_account" to exchangePayto
             }
         }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE)
@@ -106,7 +104,7 @@ class WireGatewayApiTest {
         // Bad BASE32 len wtid
         client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
             json(valid_req) { 
-                "wtid" to randBase32Crockford(31)
+                "wtid" to  randBase32Crockford(31)
             }
         }.assertBadRequest()
 
@@ -143,7 +141,7 @@ class WireGatewayApiTest {
                 },
                 { 
                     // Transactions using raw bank transaction logic
-                    tx("merchant", "KUDOS:10", "exchange", "history test with 
${randShortHashCode()} reserve pub")
+                    tx("merchant", "KUDOS:10", "exchange", "history test with 
${ShortHashCode.rand()} reserve pub")
                 },
                 {
                     // Transaction using withdraw logic
@@ -183,7 +181,7 @@ class WireGatewayApiTest {
             ignored = listOf(
                 { 
                     // gnore manual incoming transaction
-                    tx("exchange", "KUDOS:10", "merchant", 
"${randShortHashCode()} http://exchange.example.com/";)
+                    tx("exchange", "KUDOS:10", "merchant", 
"${ShortHashCode.rand()} http://exchange.example.com/";)
                 },
                 {
                     // Ignore malformed incoming transaction
@@ -202,7 +200,7 @@ class WireGatewayApiTest {
     fun addIncoming() = bankSetup { _ -> 
         val valid_req = obj {
             "amount" to "KUDOS:44"
-            "reserve_pub" to randEddsaPublicKey()
+            "reserve_pub" to EddsaPublicKey.rand()
             "debit_account" to merchantPayto.canonical
         }
 
@@ -232,7 +230,7 @@ class WireGatewayApiTest {
         // Unknown account
         
client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") {
             json(valid_req) { 
-                "reserve_pub" to randEddsaPublicKey()
+                "reserve_pub" to EddsaPublicKey.rand()
                 "debit_account" to unknownPayto
             }
         }.assertConflict(TalerErrorCode.BANK_UNKNOWN_DEBTOR)
@@ -240,7 +238,7 @@ class WireGatewayApiTest {
         // Same account
         
client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") {
             json(valid_req) { 
-                "reserve_pub" to randEddsaPublicKey()
+                "reserve_pub" to EddsaPublicKey.rand()
                 "debit_account" to exchangePayto
             }
         }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE)
diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt
index 4fab9d94..e0378d42 100644
--- a/bank/src/test/kotlin/helpers.kt
+++ b/bank/src/test/kotlin/helpers.kt
@@ -206,10 +206,10 @@ suspend fun ApplicationTestBuilder.tx(from: String, 
amount: String, to: String,
 suspend fun ApplicationTestBuilder.transfer(amount: String) {
     client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
         json {
-            "request_uid" to randHashCode()
+            "request_uid" to HashCode.rand()
             "amount" to TalerAmount(amount)
             "exchange_base_url" to "http://exchange.example.com/";
-            "wtid" to randShortHashCode()
+            "wtid" to ShortHashCode.rand()
             "credit_account" to merchantPayto
         }
     }.assertOk()
@@ -221,7 +221,7 @@ suspend fun ApplicationTestBuilder.addIncoming(amount: 
String) {
         pwAuth("admin")
         json {
             "amount" to TalerAmount(amount)
-            "reserve_pub" to randEddsaPublicKey()
+            "reserve_pub" to EddsaPublicKey.rand()
             "debit_account" to merchantPayto
         }
     }.assertOk()
@@ -231,7 +231,7 @@ suspend fun ApplicationTestBuilder.addIncoming(amount: 
String) {
 suspend fun ApplicationTestBuilder.cashout(amount: String) {
     val res = client.postA("/accounts/customer/cashouts") {
         json {
-            "request_uid" to randShortHashCode()
+            "request_uid" to ShortHashCode.rand()
             "amount_debit" to amount
             "amount_credit" to convert(amount)
         }
@@ -241,7 +241,7 @@ suspend fun ApplicationTestBuilder.cashout(amount: String) {
         fillCashoutInfo("customer")
         client.postA("/accounts/customer/cashouts") {
             json {
-                "request_uid" to randShortHashCode()
+                "request_uid" to ShortHashCode.rand()
                 "amount_debit" to amount
                 "amount_credit" to convert(amount)
             }
@@ -290,7 +290,7 @@ suspend fun ApplicationTestBuilder.fillTanInfo(account: 
String) {
 suspend fun ApplicationTestBuilder.withdrawalSelect(uuid: String) {
     client.post("/taler-integration/withdrawal-operation/$uuid") {
         json {
-            "reserve_pub" to randEddsaPublicKey()
+            "reserve_pub" to EddsaPublicKey.rand()
             "selected_exchange" to exchangePayto
         }
     }.assertOk()
@@ -482,18 +482,8 @@ fun HttpRequestBuilder.pwAuth(username: String? = null) {
 
 /* ----- Random data generation ----- */
 
-fun randBytes(length: Int): ByteArray {
-    val bytes = ByteArray(length)
-    Random.nextBytes(bytes)
-    return bytes
-}
-
 fun randBase32Crockford(length: Int) = 
Base32Crockford.encode(randBytes(length))
 
-fun randHashCode(): HashCode = HashCode(randBase32Crockford(64))
-fun randShortHashCode(): ShortHashCode = ShortHashCode(randBase32Crockford(32))
-fun randEddsaPublicKey(): EddsaPublicKey = 
EddsaPublicKey(randBase32Crockford(32))
-
 fun randIncomingSubject(reservePub: EddsaPublicKey): String {
     return "$reservePub"
 }
diff --git a/common/src/main/kotlin/CryptoUtil.kt 
b/common/src/main/kotlin/CryptoUtil.kt
index 1efdabc4..a4560c29 100644
--- a/common/src/main/kotlin/CryptoUtil.kt
+++ b/common/src/main/kotlin/CryptoUtil.kt
@@ -283,15 +283,6 @@ object CryptoUtil {
         return bundle.encoded
     }
 
-    fun checkValidEddsaPublicKey(enc: String): Boolean {
-        val data = try {
-            Base32Crockford.decode(enc)
-        } catch (e: Exception) {
-            return false
-        }
-        return data.size == 32
-    }
-
     fun hashStringSHA256(input: String): ByteArray {
         return 
MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8))
     }
diff --git a/common/src/main/kotlin/TalerCommon.kt 
b/common/src/main/kotlin/TalerCommon.kt
index d4ca47ba..aff68ee3 100644
--- a/common/src/main/kotlin/TalerCommon.kt
+++ b/common/src/main/kotlin/TalerCommon.kt
@@ -299,4 +299,123 @@ class XTalerBankPayto internal constructor(
 data class BankPaytoCtx(
     val bic: String? = null,
     val hostname: String? = null
-)
\ No newline at end of file
+)
+
+
+/** 32-byte Crockford's Base32 encoded data */
+@Serializable(with = Base32Crockford32B.Serializer::class)
+class Base32Crockford32B {
+    private var encoded: String? = null
+    val raw: ByteArray
+
+    constructor(encoded: String) {
+        val decoded = try {
+            Base32Crockford.decode(encoded) 
+        } catch (e: EncodingException) {
+            null
+        }
+        require(decoded != null && decoded.size == 32) {
+            "expected 32 bytes encoded in Crockford's base32"
+        }
+        this.raw = decoded
+        this.encoded = encoded
+    }
+    constructor(raw: ByteArray) {
+        require(raw.size == 32) {
+            "encoded data should be 32 bytes long"
+        }
+        this.raw = raw
+    }
+
+    fun encoded(): String {
+        encoded = encoded ?: Base32Crockford.encode(raw)
+        return encoded!!
+    }
+
+    override fun toString(): String {
+        return encoded()
+    }
+
+    override fun equals(other: Any?) = (other is Base32Crockford32B) && 
raw.contentEquals(other.raw)
+
+    internal object Serializer : KSerializer<Base32Crockford32B> {
+        override val descriptor: SerialDescriptor = 
PrimitiveSerialDescriptor("Base32Crockford32B", PrimitiveKind.STRING)
+    
+        override fun serialize(encoder: Encoder, value: Base32Crockford32B) {
+            encoder.encodeString(value.encoded())
+        }
+    
+        override fun deserialize(decoder: Decoder): Base32Crockford32B {
+            return Base32Crockford32B(decoder.decodeString())
+        }
+    }
+
+    companion object {
+        fun rand(): Base32Crockford32B = Base32Crockford32B(randBytes(32))
+    }
+}
+
+/** 64-byte Crockford's Base32 encoded data */
+@Serializable(with = Base32Crockford64B.Serializer::class)
+class Base32Crockford64B {
+    private var encoded: String? = null
+    val raw: ByteArray
+
+    constructor(encoded: String) {
+        val decoded = try {
+            Base32Crockford.decode(encoded) 
+        } catch (e: EncodingException) {
+            null
+        }
+        
+        require(decoded != null && decoded.size == 64) {
+            "expected 64 bytes encoded in Crockford's base32"
+        }
+        this.raw = decoded
+        this.encoded = encoded
+    }
+    constructor(raw: ByteArray) {
+        require(raw.size == 64) {
+            "encoded data should be 64 bytes long"
+        }
+        this.raw = raw
+    }
+
+    fun encoded(): String {
+        encoded = encoded ?: Base32Crockford.encode(raw)
+        return encoded!!
+    }
+
+    override fun toString(): String {
+        return encoded()
+    }
+
+    override fun equals(other: Any?) = (other is Base32Crockford64B) && 
raw.contentEquals(other.raw)
+
+    internal object Serializer : KSerializer<Base32Crockford64B> {
+        override val descriptor: SerialDescriptor = 
PrimitiveSerialDescriptor("Base32Crockford64B", PrimitiveKind.STRING)
+    
+        override fun serialize(encoder: Encoder, value: Base32Crockford64B) {
+            encoder.encodeString(value.encoded())
+        }
+    
+        override fun deserialize(decoder: Decoder): Base32Crockford64B {
+            return Base32Crockford64B(decoder.decodeString())
+        }
+    }
+
+    companion object {
+        fun rand(): Base32Crockford64B = Base32Crockford64B(randBytes(64))
+    }
+}
+
+/** 32-byte hash code */
+typealias ShortHashCode = Base32Crockford32B
+/** 64-byte hash code */
+typealias HashCode = Base32Crockford64B
+/**
+ * EdDSA and ECDHE public keys always point on Curve25519
+ * and represented  using the standard 256 bits Ed25519 compact format,
+ * converted to Crockford Base32.
+ */
+typealias EddsaPublicKey = Base32Crockford32B
\ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Metadata.kt 
b/common/src/main/kotlin/TxMedatada.kt
similarity index 68%
rename from bank/src/main/kotlin/tech/libeufin/bank/Metadata.kt
rename to common/src/main/kotlin/TxMedatada.kt
index 8bfbe80c..f6741447 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Metadata.kt
+++ b/common/src/main/kotlin/TxMedatada.kt
@@ -1,6 +1,6 @@
 /*
  * This file is part of LibEuFin.
- * Copyright (C) 2023 Taler Systems S.A.
+ * Copyright (C) 2024 Taler Systems S.A.
 
  * LibEuFin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -16,15 +16,12 @@
  * License along with LibEuFin; see the file COPYING.  If not, see
  * <http://www.gnu.org/licenses/>
  */
-package tech.libeufin.bank
+package tech.libeufin.common
 
 private val PATTERN = Regex("[a-z0-9A-Z]{52}")
 
-fun parseIncomingTxMetadata(subject: String): EddsaPublicKey? {
-    val match = PATTERN.find(subject)?.value ?: return null
-    try {
-        return EddsaPublicKey(match)
-    } catch (e: Exception) { 
-        return null
-    }
+/** Extract the reserve public key from an incoming Taler transaction subject 
*/
+fun parseIncomingTxMetadata(subject: String): EddsaPublicKey {
+    val match = PATTERN.find(subject)?.value ?: throw Exception("Missing 
reserve public key")
+    return EddsaPublicKey(match)
 }
\ No newline at end of file
diff --git a/common/src/main/kotlin/Constants.kt 
b/common/src/main/kotlin/random.kt
similarity index 83%
copy from common/src/main/kotlin/Constants.kt
copy to common/src/main/kotlin/random.kt
index 7cc6ab0b..d939ab80 100644
--- a/common/src/main/kotlin/Constants.kt
+++ b/common/src/main/kotlin/random.kt
@@ -1,23 +1,28 @@
 /*
  * This file is part of LibEuFin.
  * Copyright (C) 2024 Taler Systems S.A.
-
+ *
  * LibEuFin is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
  * published by the Free Software Foundation; either version 3, or
  * (at your option) any later version.
-
+ *
  * LibEuFin is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
  * Public License for more details.
-
+ *
  * You should have received a copy of the GNU Affero General Public
  * License along with LibEuFin; see the file COPYING.  If not, see
  * <http://www.gnu.org/licenses/>
  */
+
 package tech.libeufin.common
 
-// DB
-const val MIN_VERSION: Int = 14
-const val SERIALIZATION_RETRY: Int = 10
\ No newline at end of file
+import kotlin.random.Random
+
+fun randBytes(length: Int): ByteArray {
+    val bytes = ByteArray(length)
+    Random.nextBytes(bytes)
+    return bytes
+}
\ No newline at end of file
diff --git a/common/src/test/kotlin/CryptoUtilTest.kt 
b/common/src/test/kotlin/CryptoUtilTest.kt
index b14ada07..a4778369 100644
--- a/common/src/test/kotlin/CryptoUtilTest.kt
+++ b/common/src/test/kotlin/CryptoUtilTest.kt
@@ -141,14 +141,6 @@ class CryptoUtilTest {
         assertEquals(expectedHash.toString(Charsets.UTF_8), 
pubHash.toHexString())
     }
 
-    @Test
-    fun checkEddsaPublicKey() {
-        val givenEnc = "XZH3P6NF9DSG3BH0C082X38N2RVK1RV2H24KF76028QBKDM24BCG"
-        val non32bytes = "N2RVK1RV2H24KF76028QBKDM24BCG"
-        assertTrue(CryptoUtil.checkValidEddsaPublicKey(givenEnc))
-        assertFalse(CryptoUtil.checkValidEddsaPublicKey(non32bytes))
-    }
-
     @Test
     fun base32Test() {
         val validKey = "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"
diff --git a/contrib/ci/jobs/0-codespell/job.sh 
b/contrib/ci/jobs/0-codespell/job.sh
index 52a5693e..010d9ada 100755
--- a/contrib/ci/jobs/0-codespell/job.sh
+++ b/contrib/ci/jobs/0-codespell/job.sh
@@ -20,6 +20,7 @@ configure~
 */*.xsd
 */*.xml
 */ebics/src/test/kotlin/EbicsOrderUtilTest.kt
+*/common/src/main/kotlin/TalerErrorCode.kt
 EOF
 );
 
diff --git a/contrib/wallet-core b/contrib/wallet-core
index a431332f..8b867205 160000
--- a/contrib/wallet-core
+++ b/contrib/wallet-core
@@ -1 +1 @@
-Subproject commit a431332f59a8557edba64b24b8c6a6fcc140e2bd
+Subproject commit 8b867205faf742df8c2352d4cb2a6604305e4065
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
index b4179f51..956ae58c 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
@@ -273,7 +273,7 @@ class Database(dbConfig: String): DbPool(dbConfig, 
"libeufin_nexus") {
      */
     suspend fun registerTalerableIncoming(
         paymentData: IncomingPayment,
-        reservePub: ByteArray
+        reservePub: EddsaPublicKey
     ): IncomingRegistrationResult = conn { conn ->
         val stmt = conn.prepareStatement("""
             SELECT out_found, out_tx_id
@@ -294,7 +294,7 @@ class Database(dbConfig: String): DbPool(dbConfig, 
"libeufin_nexus") {
         stmt.setLong(4, executionTime)
         stmt.setString(5, paymentData.debitPaytoUri)
         stmt.setString(6, paymentData.bankId)
-        stmt.setBytes(7, reservePub)
+        stmt.setBytes(7, reservePub.raw)
         stmt.executeQuery().use {
             when {
                 !it.next() -> throw Exception("Inserting talerable incoming 
payment gave no outcome")
@@ -348,13 +348,13 @@ class Database(dbConfig: String): DbPool(dbConfig, 
"libeufin_nexus") {
      * @param maybeReservePub reserve public key to look up
      * @return true if found, false otherwise
      */
-    suspend fun isReservePubFound(maybeReservePub: ByteArray): Boolean = conn 
{ conn ->
+    suspend fun isReservePubFound(maybeReservePub: EddsaPublicKey): Boolean = 
conn { conn ->
         val stmt = conn.prepareStatement("""
              SELECT 1
                FROM talerable_incoming_transactions
                WHERE reserve_public_key = ?;
         """)
-        stmt.setBytes(1, maybeReservePub)
+        stmt.setBytes(1, maybeReservePub.raw)
         val res = stmt.executeQuery()
         res.use {
             return@conn it.next()
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
index 2b1a12a9..cd69ada1 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -129,64 +129,6 @@ private fun makeTalerFrac(bankFrac: String): Int {
     return buf
 }
 
-/**
- * Converts valid reserve pubs to its binary representation.
- *
- * @param maybeReservePub input.
- * @return [ByteArray] or null if not valid.
- */
-fun isReservePub(maybeReservePub: String): ByteArray? {
-    if (maybeReservePub.length != 52) {
-        logger.error("Not a reserve pub, length (${maybeReservePub.length}) is 
not 52")
-        return null
-    }
-    val dec = try {
-        Base32Crockford.decode(maybeReservePub)
-    } catch (e: EncodingException) {
-        logger.error("Not a reserve pub: $maybeReservePub")
-        return null
-    }
-    logger.debug("Reserve how many bytes: ${dec.size}")
-    // this check would only be effective after #7980
-    if (dec.size != 32) {
-        logger.error("Not a reserve pub, wrong length: ${dec.size}")
-        return null
-    }
-    return dec
-}
-
-/**
- * Extract the part of the subject that might represent a
- * valid Taler reserve public key.  That addresses some bank
- * policies of adding extra information around the payment
- * subject.
- *
- * @param subject raw subject as read from the bank.
- */
-fun removeSubjectNoise(subject: String): String? {
-    val re = "\\b[a-z0-9A-Z]{52}\\b".toRegex()
-    val result = re.find(subject.replace("[\n]+".toRegex(), "")) ?: return null
-    return result.value
-}
-
-/**
- * Checks the two conditions that may invalidate one incoming
- * payment: subject validity and availability.
- *
- * @param payment incoming payment whose subject is to be checked.
- * @return [ByteArray] as the reserve public key, or null if the
- *         payment cannot lead to a Taler withdrawal.
- */
-private suspend fun getTalerReservePub(
-    payment: IncomingPayment
-): ByteArray? {
-    // Removing noise around the potential reserve public key.
-    val maybeReservePub = removeSubjectNoise(payment.wireTransferSubject) ?: 
return null
-    // Checking validity first.
-    val dec = isReservePub(maybeReservePub) ?: return null
-    return dec
-}
-
 /**
  * Ingests an outgoing payment. It links it to the initiated
  * outgoing transaction that originated it.
@@ -209,6 +151,8 @@ suspend fun ingestOutgoingPayment(
     }
 }
 
+private val PATTERN = Regex("[a-z0-9A-Z]{52}")
+
 /**
  * Ingests an incoming payment.  Stores the payment into valid talerable ones
  * or bounces it, according to the subject.
@@ -221,26 +165,28 @@ suspend fun ingestIncomingPayment(
     db: Database,
     payment: IncomingPayment
 ) {
-    val reservePub = getTalerReservePub(payment)
-    if (reservePub == null) {
-        val result = db.registerMalformedIncoming(
-            payment,
-            payment.amount, 
-            Instant.now()
-        )
-        if (result.new) {
-            logger.info("$payment bounced in '${result.bounceId}'")
-        } else {
-            logger.debug("IN '${payment.bankId}' already seen and bounced in 
'${result.bounceId}'")
-        }
-    } else {
-        val result = db.registerTalerableIncoming(payment, reservePub)
-        if (result.new) {
-            logger.info("$payment")
-        } else {
-            logger.debug("IN '${payment.bankId}' already seen")
+    runCatching { parseIncomingTxMetadata(payment.wireTransferSubject) }.fold(
+        onSuccess = { reservePub -> 
+            val result = db.registerTalerableIncoming(payment, reservePub)
+            if (result.new) {
+                logger.info("$payment")
+            } else {
+                logger.debug("IN '${payment.bankId}' already seen")
+            }
+        },
+        onFailure = { e ->
+            val result = db.registerMalformedIncoming(
+                payment,
+                payment.amount, 
+                Instant.now()
+            )
+            if (result.new) {
+                logger.info("$payment bounced in '${result.bounceId}': 
${e.message}")
+            } else {
+                logger.debug("IN '${payment.bankId}' already seen and bounced 
in '${result.bounceId}': ${e.message}")
+            }
         }
-    }
+    )
 }
 
 private fun ingestDocument(
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
index a98c7cd6..6d9a259a 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
@@ -275,7 +275,7 @@ fun parseTxNotif(
             "CRDT" -> {
                 val bankId: String = one("Refs").one("AcctSvcrRef").text()
                 // Obtaining payment subject. 
-                val subject = opt("RmtInf")?.map("Ustrd") { text() 
}?.joinToString()
+                val subject = opt("RmtInf")?.map("Ustrd") { text() 
}?.joinToString("")
                 if (subject == null) {
                     logger.debug("Skip notification '$bankId', missing 
subject")
                     return@notificationForEachTx
diff --git a/nexus/src/test/kotlin/DatabaseTest.kt 
b/nexus/src/test/kotlin/DatabaseTest.kt
index 050bbb25..1f27a782 100644
--- a/nexus/src/test/kotlin/DatabaseTest.kt
+++ b/nexus/src/test/kotlin/DatabaseTest.kt
@@ -18,7 +18,7 @@
  */
 
 import org.junit.Test
-import tech.libeufin.common.TalerAmount
+import tech.libeufin.common.*
 import tech.libeufin.nexus.DatabaseSubmissionState
 import tech.libeufin.nexus.InitiatedPayment
 import tech.libeufin.nexus.PaymentInitiationOutcome
@@ -116,8 +116,7 @@ class IncomingPaymentsTest {
     // Tests the creation of a talerable incoming payment.
     @Test
     fun talerable() = setup { db, _ ->  
-        val reservePub = ByteArray(32)
-        Random.nextBytes(reservePub)
+        val reservePub = EddsaPublicKey.rand()
 
         val inc = genInPay("reserve-pub")
         // Checking the reserve is not found.
diff --git a/nexus/src/test/kotlin/Parsing.kt b/nexus/src/test/kotlin/Parsing.kt
index 08fb02fd..f5cbfb9c 100644
--- a/nexus/src/test/kotlin/Parsing.kt
+++ b/nexus/src/test/kotlin/Parsing.kt
@@ -18,69 +18,63 @@
  */
 
 import org.junit.Test
-import tech.libeufin.common.TalerAmount
+import tech.libeufin.common.*
 import tech.libeufin.nexus.getAmountNoCurrency
-import tech.libeufin.nexus.isReservePub
-import tech.libeufin.nexus.removeSubjectNoise
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
+import kotlin.test.*
 
 class Parsing {
 
     @Test
     fun reservePublicKey() {
-        assertNull(removeSubjectNoise("does not contain any reserve"))
-        // 4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0
-        
assertNotNull(removeSubjectNoise("4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"))
+        assertFails { parseIncomingTxMetadata("does not contain any reserve") }
+        
         assertEquals(
-            "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0",
-            removeSubjectNoise(
+            
EddsaPublicKey("4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"),
+            parseIncomingTxMetadata(
                 "noise 4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0 
noise"
             )
         )
         assertEquals(
-            "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0",
-            removeSubjectNoise(
+            
EddsaPublicKey("4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"),
+            parseIncomingTxMetadata(
                 "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0 noise to 
the right"
             )
         )
         assertEquals(
-            "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0",
-            removeSubjectNoise(
+            
EddsaPublicKey("4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"),
+            parseIncomingTxMetadata(
                 "noise to the left 
4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"
             )
         )
         assertEquals(
-            "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0",
-            removeSubjectNoise(
+            
EddsaPublicKey("4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"),
+            parseIncomingTxMetadata(
                 "    4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0     "
             )
         )
         assertEquals(
-            "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0",
-            removeSubjectNoise("""
+            
EddsaPublicKey("4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"),
+            parseIncomingTxMetadata("""
                 noise
                 4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0
                 noise
             """)
         )
         // Got the first char removed.
-        
assertNull(removeSubjectNoise("MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"))
+        assertFails { 
parseIncomingTxMetadata("MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0") }
     }
 
     @Test // Could be moved in a dedicated Amounts.kt test module.
     fun generateCurrencyAgnosticAmount() {
-        assertFailsWith<Exception> {
+        assertFails {
             // Too many fractional digits.
             getAmountNoCurrency(TalerAmount(1, 123456789, "KUDOS"))
         }
-        assertFailsWith<Exception> {
+        assertFails {
             // Nexus doesn't support sub-cents.
             getAmountNoCurrency(TalerAmount(1, 12345678, "KUDOS"))
         }
-        assertFailsWith<Exception> {
+        assertFails {
             // Nexus doesn't support sub-cents.
             getAmountNoCurrency(TalerAmount(0, 1, "KUDOS"))
         }
@@ -93,20 +87,4 @@ class Parsing {
             getAmountNoCurrency(TalerAmount(0, 10000000, "KUDOS"))
         )
     }
-
-    // Checks that the input decodes to a 32-bytes value.
-    @Test
-    fun validateReservePub() {
-        val valid = "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"
-        val validBytes = isReservePub(valid)
-        assertNotNull(validBytes)
-        assertEquals(32, validBytes.size)
-        assertNull(isReservePub("noise"))
-        val trimmedInput = valid.dropLast(10)
-        assertNull(isReservePub(trimmedInput))
-        val invalidChar = StringBuilder(valid)
-        invalidChar.setCharAt(10, '*')
-        assertNull(isReservePub(invalidChar.toString()))
-        assertNull(isReservePub(valid.dropLast(1)))
-    }
 }
\ No newline at end of file
diff --git a/testbench/src/main/kotlin/Main.kt 
b/testbench/src/main/kotlin/Main.kt
index 8586c2aa..cf50c59d 100644
--- a/testbench/src/main/kotlin/Main.kt
+++ b/testbench/src/main/kotlin/Main.kt
@@ -34,12 +34,6 @@ import java.time.Instant
 import kotlinx.coroutines.runBlocking
 import io.ktor.client.request.*
 
-fun randBytes(length: Int): ByteArray {
-    val bytes = ByteArray(length)
-    kotlin.random.Random.nextBytes(bytes)
-    return bytes
-}
-
 val nexusCmd = LibeufinNexusCommand()
 val client = HttpClient(CIO)
 
diff --git a/testbench/src/test/kotlin/IntegrationTest.kt 
b/testbench/src/test/kotlin/IntegrationTest.kt
index ddc43ced..b398ad03 100644
--- a/testbench/src/test/kotlin/IntegrationTest.kt
+++ b/testbench/src/test/kotlin/IntegrationTest.kt
@@ -50,12 +50,6 @@ fun HttpResponse.assertNoContent() {
     assertEquals(HttpStatusCode.NoContent, this.status)
 }
 
-fun randBytes(length: Int): ByteArray {
-    val bytes = ByteArray(length)
-    kotlin.random.Random.nextBytes(bytes)
-    return bytes
-}
-
 fun server(lambda: () -> Unit) {
     // Start the HTTP server in another thread
     kotlin.concurrent.thread(isDaemon = true)  {
@@ -148,11 +142,11 @@ class IntegrationTest {
                 it.execSQLUpdate("SET search_path TO libeufin_nexus;")
             }
 
-            val reservePub = randBytes(32)
+            val reservePub = EddsaPublicKey.rand()
             val payment = IncomingPayment(
                 amount = TalerAmount("EUR:10"),
                 debitPaytoUri = userPayTo.toString(),
-                wireTransferSubject = "Error test 
${Base32Crockford.encode(reservePub)}",
+                wireTransferSubject = "Error test $reservePub",
                 executionTime = Instant.now(),
                 bankId = "error"
             )
@@ -211,7 +205,7 @@ class IntegrationTest {
             ingestIncomingPayment(db, IncomingPayment(
                 amount = TalerAmount("EUR:10"),
                 debitPaytoUri = userPayTo.toString(),
-                wireTransferSubject = "Success 
${Base32Crockford.encode(randBytes(32))}",
+                wireTransferSubject = "Success 
${Base32Crockford32B.rand().encoded()}",
                 executionTime = Instant.now(),
                 bankId = "success"
             ))
@@ -278,9 +272,9 @@ class IntegrationTest {
 
             // Cashin
             repeat(3) { i ->
-                val reservePub = randBytes(32)
+                val reservePub = EddsaPublicKey.rand()
                 val amount = TalerAmount("EUR:${20+i}")
-                val subject = "cashin test $i: 
${Base32Crockford.encode(reservePub)}"
+                val subject = "cashin test $i: $reservePub"
                 nexusCmd.run("testing fake-incoming $flags --subject 
\"$subject\" --amount $amount $userPayTo")
                 val converted = 
client.get("http://0.0.0.0:8080/conversion-info/cashin-rate?amount_debit=EUR:${20
 + i}")
                     .assertOkJson<ConversionResponse>().amount_credit
@@ -296,20 +290,20 @@ class IntegrationTest {
                 }.assertOkJson<IncomingHistory> {
                     val tx = it.incoming_transactions.first()
                     assertEquals(converted, tx.amount)
-                    assert(reservePub.contentEquals(tx.reserve_pub.raw))
+                    assertEquals(reservePub, tx.reserve_pub)
                 }
             }
 
             // Cashout
             repeat(3) { i ->  
-                val requestUid = randBytes(32)
+                val requestUid = ShortHashCode.rand()
                 val amount = TalerAmount("KUDOS:${10+i}")
                 val convert = 
client.get("http://0.0.0.0:8080/conversion-info/cashout-rate?amount_debit=$amount";)
                     .assertOkJson<ConversionResponse>().amount_credit
                 client.post("http://0.0.0.0:8080/accounts/customer/cashouts";) {
                     basicAuth("customer", "password")
                     json {
-                        "request_uid" to ShortHashCode(requestUid)
+                        "request_uid" to requestUid
                         "amount_debit" to amount
                         "amount_credit" to convert
                     }

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