[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated (b421a7b2 -> 86490766),
gnunet <=