commit 54fee83cebba3194fb637efef9456cad784c4c20
parent b89fb9552dc20d9f03a855f26d64aee3003a2e43
Author: Antoine A <>
Date: Wed, 8 Oct 2025 15:08:47 +0200
common: optimize and clean taler timestamps logic
Diffstat:
13 files changed, 132 insertions(+), 149 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
@@ -1,76 +0,0 @@
-/*
- * This file is part of LibEuFin.
- * 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
- * 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.bank
-
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import kotlinx.serialization.json.JsonDecoder
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.jsonPrimitive
-import kotlinx.serialization.json.longOrNull
-import tech.libeufin.common.badRequest
-import java.time.Duration
-import java.time.temporal.ChronoUnit
-import java.util.concurrent.TimeUnit
-
-/**
- * Internal representation of relative times. The
- * "forever" case is represented with Long.MAX_VALUE.
- */
-@Serializable
-data class RelativeTime(
- @Serializable(with = Serializer::class)
- val d_us: Duration
-) {
- internal object Serializer : KSerializer<Duration> {
- override fun serialize(encoder: Encoder, value: Duration) {
- if (value == ChronoUnit.FOREVER.duration) {
- encoder.encodeString("forever")
- } else {
- encoder.encodeLong(TimeUnit.MICROSECONDS.convert(value))
- }
- }
-
- override fun deserialize(decoder: Decoder): Duration {
- val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
- val maybeDUs = jsonInput.decodeJsonElement().jsonPrimitive
- if (maybeDUs.isString) {
- if (maybeDUs.content != "forever") throw badRequest("Only 'forever' allowed for d_us as string, but '${maybeDUs.content}' was found")
- return ChronoUnit.FOREVER.duration
- }
- val dUs: Long = maybeDUs.longOrNull
- ?: throw badRequest("Could not convert d_us: '${maybeDUs.content}' to a number")
- when {
- dUs < 0 -> throw badRequest("Negative duration specified.")
- dUs > MAX_SAFE_INTEGER -> throw badRequest("d_us value $dUs exceed cap (2^53-1)")
- else -> return Duration.of(dUs, ChronoUnit.MICROS)
- }
- }
-
- override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor
- }
-
- companion object {
- const val MAX_SAFE_INTEGER = 9007199254740991L // 2^53 - 1
- }
-}
-\ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -158,7 +158,7 @@ data class TanTransmission(
@Serializable
data class TokenSuccessResponse(
val access_token: String,
- val expiration: TalerProtocolTimestamp
+ val expiration: TalerTimestamp
)
@@ -322,12 +322,12 @@ data class BearerToken(
@Serializable
data class TokenInfo(
- val creation_time: TalerProtocolTimestamp,
- val expiration: TalerProtocolTimestamp,
+ val creation_time: TalerTimestamp,
+ val expiration: TalerTimestamp,
val scope: TokenScope,
val isRefreshable: Boolean,
val description: String? = null,
- val last_access: TalerProtocolTimestamp,
+ val last_access: TalerTimestamp,
val row_id: Long,
val token_id: Long
)
@@ -460,7 +460,7 @@ data class BankAccountTransactionInfo(
val direction: TransactionDirection,
val subject: String,
val row_id: Long, // is T_ID
- val date: TalerProtocolTimestamp
+ val date: TalerTimestamp
)
// Response type for histories, namely GET /transactions
@@ -593,8 +593,8 @@ data class CashoutStatusResponse(
val amount_debit: TalerAmount,
val amount_credit: TalerAmount,
val subject: String,
- val creation_time: TalerProtocolTimestamp,
- val confirmation_time: TalerProtocolTimestamp? = null,
+ val creation_time: TalerTimestamp,
+ val confirmation_time: TalerTimestamp? = null,
val tan_channel: TanChannel? = null,
val tan_info: String? = null
)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt
@@ -106,7 +106,7 @@ private fun Routing.coreBankTokenApi(db: Database, cfg: BankConfig) {
)
}
val token = Base32Crockford32B.secureRand()
- val tokenDuration: Duration = req.duration?.d_us ?: TOKEN_DEFAULT_DURATION
+ val tokenDuration: Duration = req.duration?.duration ?: TOKEN_DEFAULT_DURATION
val creationTime = Instant.now()
val expirationTimestamp =
@@ -133,7 +133,7 @@ private fun Routing.coreBankTokenApi(db: Database, cfg: BankConfig) {
TokenCreationResult.Success -> call.respond(
TokenSuccessResponse(
access_token = "$TOKEN_PREFIX$token",
- expiration = TalerProtocolTimestamp(t_s = expirationTimestamp)
+ expiration = TalerTimestamp(expirationTimestamp)
)
)
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/WireGatewayApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/WireGatewayApi.kt
@@ -193,7 +193,7 @@ fun Routing.wireGatewayApi(db: Database, cfg: BankConfig) {
)
is AddIncomingResult.Success -> this.respond(
AddIncomingResponse(
- timestamp = TalerProtocolTimestamp(timestamp),
+ timestamp = TalerTimestamp(timestamp),
row_id = res.id
)
)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt
@@ -119,7 +119,7 @@ class CashoutDAO(private val db: Database) {
creation_time = it.getTalerTimestamp("creation_time"),
confirmation_time = when (val timestamp = it.getLong("confirmation_date")) {
0L -> null
- else -> TalerProtocolTimestamp(timestamp.asInstant())
+ else -> TalerTimestamp(timestamp.asInstant())
},
tan_channel = it.getOptEnum<TanChannel>("tan_channel"),
tan_info = it.getString("tan_info"),
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/ExchangeDAO.kt
@@ -99,7 +99,7 @@ class ExchangeDAO(private val db: Database) {
/** Result of taler transfer transaction creation */
sealed interface TransferResult {
/** Transaction [id] and wire transfer [timestamp] */
- data class Success(val id: Long, val timestamp: TalerProtocolTimestamp): TransferResult
+ data class Success(val id: Long, val timestamp: TalerTimestamp): TransferResult
data object NotAnExchange: TransferResult
data object UnknownExchange: TransferResult
data object BothPartyAreExchange: TransferResult
@@ -240,7 +240,7 @@ class ExchangeDAO(private val db: Database) {
/** Result of taler add incoming transaction creation */
sealed interface AddIncomingResult {
/** Transaction [id] and wire transfer [timestamp] */
- data class Success(val id: Long, val timestamp: TalerProtocolTimestamp): AddIncomingResult
+ data class Success(val id: Long, val timestamp: TalerTimestamp): AddIncomingResult
data object NotAnExchange: AddIncomingResult
data object UnknownExchange: AddIncomingResult
data object UnknownDebtor: AddIncomingResult
@@ -292,7 +292,7 @@ class ExchangeDAO(private val db: Database) {
it.getBoolean("out_reserve_pub_reuse") -> AddIncomingResult.ReservePubReuse
else -> AddIncomingResult.Success(
id = it.getLong("out_tx_row_id"),
- timestamp = TalerProtocolTimestamp(timestamp)
+ timestamp = TalerTimestamp(timestamp)
)
}
}
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -193,7 +193,7 @@ class CoreBankTokenApiTest {
}
}
}.assertOkJson<TokenSuccessResponse> {
- assertEquals(Instant.MAX, it.expiration.t_s)
+ assertEquals(Instant.MAX, it.expiration.instant)
}
// Check too big or invalid durations
diff --git a/bank/src/test/kotlin/JsonTest.kt b/bank/src/test/kotlin/JsonTest.kt
@@ -22,9 +22,9 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.junit.Test
import tech.libeufin.bank.CreditDebitInfo
-import tech.libeufin.bank.RelativeTime
+import tech.libeufin.common.RelativeTime
import tech.libeufin.common.TalerAmount
-import tech.libeufin.common.TalerProtocolTimestamp
+import tech.libeufin.common.TalerTimestamp
import java.time.Duration
import java.time.Instant
import java.time.temporal.ChronoUnit
@@ -55,18 +55,18 @@ class JsonTest {
@Test
fun timeSerializers() {
// from JSON to time types
- assert(Json.decodeFromString<RelativeTime>("{\"d_us\": 3}").d_us.toNanos() == 3000L)
- assert(Json.decodeFromString<RelativeTime>("{\"d_us\": \"forever\"}").d_us == ChronoUnit.FOREVER.duration)
- assert(Json.decodeFromString<TalerProtocolTimestamp>("{\"t_s\": 3}").t_s == Instant.ofEpochSecond(3))
- assert(Json.decodeFromString<TalerProtocolTimestamp>("{\"t_s\": \"never\"}").t_s == Instant.MAX)
+ assert(Json.decodeFromString<RelativeTime>("{\"d_us\": 3}").duration.toNanos() == 3000L)
+ assert(Json.decodeFromString<RelativeTime>("{\"d_us\": \"forever\"}").duration == ChronoUnit.FOREVER.duration)
+ assert(Json.decodeFromString<TalerTimestamp>("{\"t_s\": 3}").instant == Instant.ofEpochSecond(3))
+ assert(Json.decodeFromString<TalerTimestamp>("{\"t_s\": \"never\"}").instant == Instant.MAX)
// from time types to JSON
- val oneDay = RelativeTime(d_us = Duration.of(1, ChronoUnit.DAYS))
+ val oneDay = RelativeTime(Duration.of(1, ChronoUnit.DAYS))
val oneDaySerial = Json.encodeToString(oneDay)
- assert(Json.decodeFromString<RelativeTime>(oneDaySerial).d_us == oneDay.d_us)
- val forever = RelativeTime(d_us = ChronoUnit.FOREVER.duration)
+ assert(Json.decodeFromString<RelativeTime>(oneDaySerial).duration == oneDay.duration)
+ val forever = RelativeTime(ChronoUnit.FOREVER.duration)
val foreverSerial = Json.encodeToString(forever)
- assert(Json.decodeFromString<RelativeTime>(foreverSerial).d_us == forever.d_us)
+ assert(Json.decodeFromString<RelativeTime>(foreverSerial).duration == forever.duration)
}
@Test
diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt
@@ -21,21 +21,16 @@ package tech.libeufin.common
import io.ktor.http.*
import io.ktor.server.plugins.*
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.descriptors.PrimitiveKind
-import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import kotlinx.serialization.json.JsonDecoder
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.jsonPrimitive
-import kotlinx.serialization.json.longOrNull
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
import java.net.URI
import java.net.URL
import java.time.Instant
+import java.time.Duration
import java.time.temporal.ChronoUnit
+import java.util.concurrent.TimeUnit
import org.bouncycastle.math.ec.rfc8032.Ed25519
sealed class CommonError(msg: String): Exception(msg) {
@@ -44,39 +39,104 @@ sealed class CommonError(msg: String): Exception(msg) {
class Payto(msg: String): CommonError(msg)
}
+/**
+ * Internal representation of relative times. The
+ * "forever" case is represented with Long.MAX_VALUE.
+ */
+@JvmInline
+@Serializable(with = RelativeTime.Serializer::class)
+value class RelativeTime(val duration: Duration) {
+ private object Serializer : KSerializer<RelativeTime> {
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("RelativeTime") {
+ element<JsonElement>("d_us")
+ }
+
+ override fun serialize(encoder: Encoder, value: RelativeTime) {
+ val composite = encoder.beginStructure(descriptor)
+ if (value.duration == ChronoUnit.FOREVER.duration) {
+ composite.encodeStringElement(descriptor, 0, "forever")
+ } else {
+ composite.encodeLongElement(descriptor, 0, TimeUnit.MICROSECONDS.convert(value.duration))
+ }
+ composite.endStructure(descriptor)
+ }
+
+ override fun deserialize(decoder: Decoder): RelativeTime {
+ val dec = decoder.beginStructure(descriptor)
+ val jsonInput = dec as? JsonDecoder ?: error("Can be deserialized only by JSON")
+ lateinit var maybeDUs: JsonPrimitive
+ loop@ while (true) {
+ when (val index = dec.decodeElementIndex(descriptor)) {
+ 0 -> maybeDUs = jsonInput.decodeJsonElement().jsonPrimitive
+ CompositeDecoder.DECODE_DONE -> break@loop
+ else -> throw SerializationException("Unexpected index: $index")
+ }
+ }
+ dec.endStructure(descriptor)
+ if (maybeDUs.isString) {
+ if (maybeDUs.content != "forever") throw badRequest("Only 'forever' allowed for d_us as string, but '${maybeDUs.content}' was found")
+ return RelativeTime(ChronoUnit.FOREVER.duration)
+ }
+ val dUs: Long = maybeDUs.longOrNull
+ ?: throw badRequest("Could not convert d_us: '${maybeDUs.content}' to a number")
+ when {
+ dUs < 0 -> throw badRequest("Negative duration specified.")
+ dUs > MAX_SAFE_INTEGER -> throw badRequest("d_us value $dUs exceed cap (2^53-1)")
+ else -> return RelativeTime(Duration.of(dUs, ChronoUnit.MICROS))
+ }
+ }
+ }
+
+ companion object {
+ const val MAX_SAFE_INTEGER = 9007199254740991L // 2^53 - 1
+ }
+}
/** Timestamp containing the number of seconds since epoch */
-@Serializable
-data class TalerProtocolTimestamp(
- @Serializable(with = Serializer::class)
- val t_s: Instant,
-) {
- internal object Serializer : KSerializer<Instant> {
- override fun serialize(encoder: Encoder, value: Instant) {
- if (value == Instant.MAX) {
- encoder.encodeString("never")
+@JvmInline
+@Serializable(with = TalerTimestamp.Serializer::class)
+value class TalerTimestamp constructor(val instant: Instant) {
+ private object Serializer : KSerializer<TalerTimestamp> {
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("Timestamp") {
+ element<JsonElement>("t_s")
+ }
+
+ override fun serialize(encoder: Encoder, value: TalerTimestamp) {
+ val composite = encoder.beginStructure(descriptor)
+ if (value.instant == Instant.MAX) {
+ composite.encodeStringElement(descriptor, 0, "never")
} else {
- encoder.encodeLong(value.epochSecond)
+ composite.encodeLongElement(descriptor, 0, value.instant.epochSecond)
}
+ composite.endStructure(descriptor)
}
- override fun deserialize(decoder: Decoder): Instant {
- val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
- val maybeTs = jsonInput.decodeJsonElement().jsonPrimitive
+ override fun deserialize(decoder: Decoder): TalerTimestamp {
+ val dec = decoder.beginStructure(descriptor)
+ val jsonInput = dec as? JsonDecoder ?: error("Can be deserialized only by JSON")
+ lateinit var maybeTs: JsonPrimitive
+ loop@ while (true) {
+ when (val index = dec.decodeElementIndex(descriptor)) {
+ 0 -> maybeTs = jsonInput.decodeJsonElement().jsonPrimitive
+ CompositeDecoder.DECODE_DONE -> break@loop
+ else -> throw SerializationException("Unexpected index: $index")
+ }
+ }
+ dec.endStructure(descriptor)
if (maybeTs.isString) {
if (maybeTs.content != "never") throw badRequest("Only 'never' allowed for t_s as string, but '${maybeTs.content}' was found")
- return Instant.MAX
+ return TalerTimestamp(Instant.MAX)
}
val ts: Long = maybeTs.longOrNull
?: throw badRequest("Could not convert t_s '${maybeTs.content}' to a number")
when {
ts < 0 -> throw badRequest("Negative timestamp not allowed")
ts > Instant.MAX.epochSecond -> throw badRequest("Timestamp $ts too big to be represented in Kotlin")
- else -> return Instant.ofEpochSecond(ts)
+ else -> return TalerTimestamp(Instant.ofEpochSecond(ts))
}
}
-
- override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor
}
}
@@ -103,7 +163,7 @@ value class BaseURL private constructor(val url: URL) {
override fun toString(): String = url.toString()
- internal object Serializer : KSerializer<BaseURL> {
+ private object Serializer : KSerializer<BaseURL> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("BaseURL", PrimitiveKind.STRING)
@@ -164,7 +224,7 @@ class DecimalNumber {
}
}
- internal object Serializer : KSerializer<DecimalNumber> {
+ private object Serializer : KSerializer<DecimalNumber> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("DecimalNumber", PrimitiveKind.STRING)
@@ -277,7 +337,7 @@ class TalerAmount {
return TalerAmount(value - decrement.value, frac - decrement.frac, currency).normalize()
}
- internal object Serializer : KSerializer<TalerAmount> {
+ private object Serializer : KSerializer<TalerAmount> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("TalerAmount", PrimitiveKind.STRING)
@@ -346,7 +406,7 @@ sealed class Payto {
return this.parsed == other.parsed
}
- internal object Serializer : KSerializer<Payto> {
+ private object Serializer : KSerializer<Payto> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Payto", PrimitiveKind.STRING)
@@ -580,7 +640,7 @@ class Base32Crockford64B {
override fun equals(other: Any?) = (other is Base32Crockford64B) && raw.contentEquals(other.raw)
- internal object Serializer : KSerializer<Base32Crockford64B> {
+ private object Serializer : KSerializer<Base32Crockford64B> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Base32Crockford64B", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Base32Crockford64B) {
diff --git a/common/src/main/kotlin/TalerMessage.kt b/common/src/main/kotlin/TalerMessage.kt
@@ -57,7 +57,7 @@ data class TransferRequest(
/** Response POST /taler-wire-gateway/transfer */
@Serializable
data class TransferResponse(
- val timestamp: TalerProtocolTimestamp,
+ val timestamp: TalerTimestamp,
val row_id: Long
)
@@ -73,7 +73,7 @@ data class TransferListStatus(
val status: TransferStatusState,
val amount: TalerAmount,
val credit_account: String,
- val timestamp: TalerProtocolTimestamp
+ val timestamp: TalerTimestamp
)
/** Request GET /taler-wire-gateway/transfers/{ROW_iD} */
@@ -85,7 +85,7 @@ data class TransferStatus(
val origin_exchange_url: String,
val wtid: ShortHashCode,
val credit_account: String,
- val timestamp: TalerProtocolTimestamp
+ val timestamp: TalerTimestamp
)
/** Request POST /taler-wire-gateway/admin/add-incoming */
@@ -99,7 +99,7 @@ data class AddIncomingRequest(
/** Response POST /taler-wire-gateway/admin/add-incoming */
@Serializable
data class AddIncomingResponse(
- val timestamp: TalerProtocolTimestamp,
+ val timestamp: TalerTimestamp,
val row_id: Long
)
@@ -122,7 +122,7 @@ data class IncomingHistory(
@Serializable
sealed interface IncomingBankTransaction {
val row_id: Long
- val date: TalerProtocolTimestamp
+ val date: TalerTimestamp
val amount: TalerAmount
val debit_account: String
val credit_fee: TalerAmount?
@@ -132,7 +132,7 @@ sealed interface IncomingBankTransaction {
@SerialName("KYCAUTH")
data class IncomingKycAuthTransaction(
override val row_id: Long,
- override val date: TalerProtocolTimestamp,
+ override val date: TalerTimestamp,
override val amount: TalerAmount,
override val credit_fee: TalerAmount? = null,
override val debit_account: String,
@@ -142,7 +142,7 @@ data class IncomingKycAuthTransaction(
@SerialName("RESERVE")
data class IncomingReserveTransaction(
override val row_id: Long,
- override val date: TalerProtocolTimestamp,
+ override val date: TalerTimestamp,
override val amount: TalerAmount,
override val credit_fee: TalerAmount? = null,
override val debit_account: String,
@@ -152,7 +152,7 @@ data class IncomingReserveTransaction(
@SerialName("WAD")
data class IncomingWadTransaction(
override val row_id: Long,
- override val date: TalerProtocolTimestamp,
+ override val date: TalerTimestamp,
override val amount: TalerAmount,
override val credit_fee: TalerAmount? = null,
override val debit_account: String,
@@ -171,7 +171,7 @@ data class OutgoingHistory(
@Serializable
data class OutgoingTransaction(
val row_id: Long, // DB row ID of the payment.
- val date: TalerProtocolTimestamp,
+ val date: TalerTimestamp,
val amount: TalerAmount,
val credit_account: String,
val wtid: ShortHashCode,
@@ -202,7 +202,7 @@ data class RevenueIncomingHistory(
@Serializable
data class RevenueIncomingBankTransaction(
val row_id: Long,
- val date: TalerProtocolTimestamp,
+ val date: TalerTimestamp,
val amount: TalerAmount,
val credit_fee: TalerAmount? = null,
val debit_account: String,
diff --git a/common/src/main/kotlin/db/types.kt b/common/src/main/kotlin/db/types.kt
@@ -81,8 +81,8 @@ fun ResultSet.getOptDecimal(name: String): DecimalNumber? {
return amount
}
-fun ResultSet.getTalerTimestamp(name: String): TalerProtocolTimestamp{
- return TalerProtocolTimestamp(getLong(name).asInstant())
+fun ResultSet.getTalerTimestamp(name: String): TalerTimestamp{
+ return TalerTimestamp(getLong(name).asInstant())
}
fun ResultSet.getBankPayto(payto: String, name: String?, ctx: BankPaytoCtx): String {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt
@@ -134,7 +134,7 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) = conditional(cfg.wir
)
is IncomingRegistrationResult.Success -> respond(
AddIncomingResponse(
- timestamp = TalerProtocolTimestamp(timestamp),
+ timestamp = TalerTimestamp(timestamp),
row_id = res.id
)
)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/ExchangeDAO.kt
@@ -96,7 +96,7 @@ class ExchangeDAO(private val db: Database) {
/** Result of taler transfer transaction creation */
sealed interface TransferResult {
/** Transaction [id] and wire transfer [timestamp] */
- data class Success(val id: Long, val timestamp: TalerProtocolTimestamp): TransferResult
+ data class Success(val id: Long, val timestamp: TalerTimestamp): TransferResult
data object RequestUidReuse: TransferResult
data object WtidReuse: TransferResult
}