gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] 03/03: Stop using longs to manipulate time.


From: gnunet
Subject: [libeufin] 03/03: Stop using longs to manipulate time.
Date: Fri, 29 Sep 2023 09:55:14 +0200

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

ms pushed a commit to branch master
in repository libeufin.

commit 46e4838f80db6f0b086c579c4bede065fc630025
Author: MS <ms@taler.net>
AuthorDate: Fri Sep 29 09:51:23 2023 +0200

    Stop using longs to manipulate time.
    
    The application uses now Instant and Duration deferring
    their conversion to Long right before storing the time
    information to the database.
---
 .../main/kotlin/tech/libeufin/bank/BankMessages.kt | 12 +++---
 .../tech/libeufin/bank/CorebankApiHandlers.kt      | 16 +++----
 .../src/main/kotlin/tech/libeufin/bank/Database.kt | 50 ++++++++++++++++------
 bank/src/main/kotlin/tech/libeufin/bank/Main.kt    |  6 ++-
 .../tech/libeufin/bank/WireGatewayApiHandlers.kt   | 11 ++---
 bank/src/main/kotlin/tech/libeufin/bank/helpers.kt |  7 +--
 bank/src/test/kotlin/DatabaseTest.kt               | 19 ++++----
 bank/src/test/kotlin/LibeuFinApiTest.kt            |  9 ++--
 util/src/main/kotlin/time.kt                       | 27 ++++++++++--
 9 files changed, 100 insertions(+), 57 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
index 9911c933..bf400026 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
@@ -245,8 +245,8 @@ data class BearerToken(
     val content: ByteArray,
     val scope: TokenScope,
     val isRefreshable: Boolean = false,
-    val creationTime: Long,
-    val expirationTime: Long,
+    val creationTime: Instant,
+    val expirationTime: Instant,
     /**
      * Serial ID of the database row that hosts the bank customer
      * that is associated with this token.  NOTE: if the token is
@@ -270,7 +270,7 @@ data class BankInternalTransaction(
     val debtorAccountId: Long,
     val subject: String,
     val amount: TalerAmount,
-    val transactionDate: Long,
+    val transactionDate: Instant,
     val accountServicerReference: String = "not used", // ISO20022
     val endToEndId: String = "not used", // ISO20022
     val paymentInformationId: String = "not used" // ISO20022
@@ -288,7 +288,7 @@ data class BankAccountTransaction(
     val debtorName: String,
     val subject: String,
     val amount: TalerAmount,
-    val transactionDate: Long, // microseconds
+    val transactionDate: Instant,
     /**
      * Is the transaction debit, or credit for the
      * bank account pointed by this object?
@@ -338,8 +338,8 @@ data class Cashout(
     val sellAtRatio: Int,
     val sellOutFee: TalerAmount,
     val subject: String,
-    val creationTime: Long,
-    val tanConfirmationTime: Long? = null,
+    val creationTime: Instant,
+    val tanConfirmationTime: Instant? = null,
     val tanChannel: TanChannel,
     val tanCode: String,
     val bankAccount: Long,
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
index 797d1195..f4286e58 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
@@ -72,10 +72,8 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
         val token = BearerToken(
             bankCustomer = customerDbRow,
             content = tokenBytes,
-            creationTime = creationTime.toDbMicros()
-                ?: throw internalServerError("Could not get micros out of 
token creationTime Instant."),
-            expirationTime = expirationTimestamp.toDbMicros()
-                ?: throw internalServerError("Could not get micros out of 
token expirationTime Instant."),
+            creationTime = creationTime,
+            expirationTime = expirationTimestamp,
             scope = req.scope,
             isRefreshable = req.refreshable
         )
@@ -179,7 +177,7 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
                 debtorAccountId = adminBankAccount.expectRowId(),
                 amount = bonusAmount,
                 subject = "Registration bonus.",
-                transactionDate = getNowUs()
+                transactionDate = Instant.now()
             )
             when (db.bankTransactionCreate(adminPaysBonus)) {
                 Database.BankTransactionResult.NO_CREDITOR -> throw 
internalServerError("Bonus impossible: creditor not found, despite its recent 
creation.")
@@ -306,7 +304,7 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
         // to the selected state _and_ wire the funds to the exchange.
         // Note: 'when' helps not to omit more result codes, should more
         // be added.
-        when (db.talerWithdrawalConfirm(op.withdrawalUuid, getNowUs())) {
+        when (db.talerWithdrawalConfirm(op.withdrawalUuid, Instant.now())) {
             WithdrawalConfirmationResult.BALANCE_INSUFFICIENT ->
                 throw conflict(
                 "Insufficient funds",
@@ -358,7 +356,7 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
                     subject = it.subject,
                     amount = it.amount.toString(),
                     direction = it.direction,
-                    date = 
TalerProtocolTimestamp.fromMicroseconds(it.transactionDate),
+                    date = TalerProtocolTimestamp(it.transactionDate),
                     row_id = it.dbRowId ?: throw internalServerError(
                         "Transaction timestamped with '${it.transactionDate}' 
did not have row ID"
                     )
@@ -393,7 +391,7 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
             creditorAccountId = creditorCustomerData.owningCustomerId,
             subject = subject,
             amount = amount,
-            transactionDate = getNowUs()
+            transactionDate = Instant.now()
         )
         val res = db.bankTransactionCreate(dbInstructions)
         when (res) {
@@ -433,7 +431,7 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
                 amount = 
"${tx.amount.currency}:${tx.amount.value}.${tx.amount.frac}",
                 creditor_payto_uri = tx.creditorPaytoUri,
                 debtor_payto_uri = tx.debtorPaytoUri,
-                date = 
TalerProtocolTimestamp.fromMicroseconds(tx.transactionDate),
+                date = TalerProtocolTimestamp(tx.transactionDate),
                 direction = tx.direction,
                 subject = tx.subject,
                 row_id = txRowId
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
index d77c89a9..c5f06190 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -24,11 +24,14 @@ import org.postgresql.jdbc.PgConnection
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import tech.libeufin.util.getJdbcConnectionFromPg
+import tech.libeufin.util.microsToJavaInstant
+import tech.libeufin.util.toDbMicros
 import java.io.File
 import java.sql.DriverManager
 import java.sql.PreparedStatement
 import java.sql.ResultSet
 import java.sql.SQLException
+import java.time.Instant
 import java.util.*
 import kotlin.math.abs
 
@@ -41,6 +44,25 @@ fun BankAccountTransaction.expectRowId(): Long = 
this.dbRowId ?: throw internalS
 
 private val logger: Logger = 
LoggerFactory.getLogger("tech.libeufin.bank.Database")
 
+/**
+ * This error occurs in case the timestamp took by the bank for some
+ * event could not be converted in microseconds.  Note: timestamp are
+ * taken via the Instant.now(), then converted to nanos, and then divided
+ * by 1000 to obtain the micros.
+ *
+ * It could be that a legitimate timestamp overflows in the process of
+ * being converted to micros - as described above.  In the case of a timestamp,
+ * the fault lies to the bank, because legitimate timestamps must (at the
+ * time of writing!) go through the conversion to micros.
+ *
+ * On the other hand (and for the sake of completeness), in the case of a
+ * timestamp that was calculated after a client-submitted duration, the 
overflow
+ * lies to the client, because they must have specified a gigantic amount of 
time
+ * that overflew the conversion to micros and should simply have specified 
"forever".
+ */
+private fun faultyTimestampByBank() = internalServerError("Bank took 
overflowing timestamp")
+private fun faultyDurationByClient() = badRequest("Overflowing duration, 
please specify 'forever' instead.")
+
 fun initializeDatabaseTables(dbConfig: String, sqlDir: String) {
     logger.info("doing DB initialization, sqldir $sqlDir, dbConfig $dbConfig")
     val jdbcConnStr = getJdbcConnectionFromPg(dbConfig)
@@ -299,8 +321,8 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
                (?, ?, ?, ?::token_scope_enum, ?, ?)
         """)
         stmt.setBytes(1, token.content)
-        stmt.setLong(2, token.creationTime)
-        stmt.setLong(3, token.expirationTime)
+        stmt.setLong(2, token.creationTime.toDbMicros() ?: throw 
faultyTimestampByBank())
+        stmt.setLong(3, token.expirationTime.toDbMicros() ?: throw 
faultyDurationByClient())
         stmt.setString(4, token.scope.name)
         stmt.setLong(5, token.bankCustomer)
         stmt.setBoolean(6, token.isRefreshable)
@@ -323,8 +345,8 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
             if (!it.next()) return null
             return BearerToken(
                 content = token,
-                creationTime = it.getLong("creation_time"),
-                expirationTime = it.getLong("expiration_time"),
+                creationTime = 
it.getLong("creation_time").microsToJavaInstant() ?: throw 
faultyTimestampByBank(),
+                expirationTime = 
it.getLong("expiration_time").microsToJavaInstant() ?: throw 
faultyDurationByClient(),
                 bankCustomer = it.getLong("bank_customer"),
                 scope = it.getString("scope").run {
                     if (this == TokenScope.readwrite.name) return@run 
TokenScope.readwrite
@@ -511,7 +533,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
         stmt.setString(3, tx.subject)
         stmt.setLong(4, tx.amount.value)
         stmt.setInt(5, tx.amount.frac)
-        stmt.setLong(6, tx.transactionDate)
+        stmt.setLong(6, tx.transactionDate.toDbMicros() ?: throw 
faultyTimestampByBank())
         stmt.setString(7, tx.accountServicerReference)
         stmt.setString(8, tx.paymentInformationId)
         stmt.setString(9, tx.endToEndId)
@@ -603,7 +625,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
                 bankAccountId = it.getLong("bank_account_id"),
                 paymentInformationId = it.getString("payment_information_id"),
                 subject = it.getString("subject"),
-                transactionDate = it.getLong("transaction_date")
+                transactionDate = 
it.getLong("transaction_date").microsToJavaInstant() ?: throw 
faultyTimestampByBank()
             )
         }
     }
@@ -699,7 +721,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
                         bankAccountId = it.getLong("bank_account_id"),
                         paymentInformationId = 
it.getString("payment_information_id"),
                         subject = it.getString("subject"),
-                        transactionDate = it.getLong("transaction_date"),
+                        transactionDate = 
it.getLong("transaction_date").microsToJavaInstant() ?: throw 
faultyTimestampByBank(),
                         dbRowId = it.getLong("bank_transaction_id")
                 ))
             } while (it.next())
@@ -815,7 +837,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
      */
     fun talerWithdrawalConfirm(
         opUuid: UUID,
-        timestamp: Long,
+        timestamp: Instant,
         accountServicerReference: String = "NOT-USED",
         endToEndId: String = "NOT-USED",
         paymentInfId: String = "NOT-USED"
@@ -831,7 +853,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
         """
         )
         stmt.setObject(1, opUuid)
-        stmt.setLong(2, timestamp)
+        stmt.setLong(2, timestamp.toDbMicros() ?: throw 
faultyTimestampByBank())
         stmt.setString(3, accountServicerReference)
         stmt.setString(4, endToEndId)
         stmt.setString(5, paymentInfId)
@@ -898,7 +920,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
         stmt.setLong(10, op.sellOutFee.value)
         stmt.setInt(11, op.sellOutFee.frac)
         stmt.setString(12, op.subject)
-        stmt.setLong(13, op.creationTime)
+        stmt.setLong(13, op.creationTime.toDbMicros() ?: throw 
faultyTimestampByBank())
         stmt.setString(14, op.tanChannel.name)
         stmt.setString(15, op.tanCode)
         stmt.setLong(16, op.bankAccount)
@@ -1008,7 +1030,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
                 credit_payto_uri = it.getString("credit_payto_uri"),
                 cashoutCurrency = it.getString("cashout_currency"),
                 cashoutUuid = opUuid,
-                creationTime = it.getLong("creation_time"),
+                creationTime = 
it.getLong("creation_time").microsToJavaInstant() ?: throw 
faultyTimestampByBank(),
                 sellAtRatio = it.getInt("sell_at_ratio"),
                 sellOutFee = TalerAmount(
                     value = it.getLong("sell_out_fee_val"),
@@ -1028,7 +1050,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
                 localTransaction = it.getLong("local_transaction"),
                 tanConfirmationTime = it.getLong("tan_confirmation_time").run {
                     if (this == 0L) return@run null
-                    return@run this
+                    return@run this.microsToJavaInstant() ?: throw 
faultyTimestampByBank()
                 }
             )
         }
@@ -1111,7 +1133,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
     fun talerTransferCreate(
         req: TransferRequest,
         exchangeBankAccountId: Long,
-        timestamp: Long,
+        timestamp: Instant,
         acctSvcrRef: String = "not used",
         pmtInfId: String = "not used",
         endToEndId: String = "not used",
@@ -1144,7 +1166,7 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
         stmt.setString(5, req.exchange_base_url)
         stmt.setString(6, req.credit_account)
         stmt.setLong(7, exchangeBankAccountId)
-        stmt.setLong(8, timestamp)
+        stmt.setLong(8, timestamp.toDbMicros() ?: throw 
faultyTimestampByBank())
         stmt.setString(9, acctSvcrRef)
         stmt.setString(10, pmtInfId)
         stmt.setString(11, endToEndId)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index 115264c8..a8ef3e81 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -132,8 +132,7 @@ object TalerProtocolTimestampSerializer : 
KSerializer<TalerProtocolTimestamp> {
                 encodeStringElement(descriptor, 0, "never")
                 return@encodeStructure
             }
-            val ts = value.t_s.toDbMicros() ?: throw 
internalServerError("Could not serialize timestamp")
-            encodeLongElement(descriptor, 0, ts)
+            encodeLongElement(descriptor, 0, value.t_s.epochSecond)
         }
     }
 
@@ -368,6 +367,9 @@ fun Application.corebankWebApp(db: Database, ctx: 
BankApplicationContext) {
          */
         exception<LibeufinBankException> { call, cause ->
             logger.error(cause.talerError.hint)
+            // Stacktrace if bank's fault
+            if (cause.httpStatus.toString().startsWith('5'))
+                cause.printStackTrace()
             call.respond(
                 status = cause.httpStatus,
                 message = cause.talerError
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt
index 5ec4b672..b040883d 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApiHandlers.kt
@@ -27,6 +27,7 @@ import io.ktor.server.request.*
 import io.ktor.server.response.*
 import io.ktor.server.routing.*
 import net.taler.common.errorcodes.TalerErrorCode
+import java.time.Instant
 
 fun Routing.talerWireGatewayHandlers(db: Database, ctx: 
BankApplicationContext) {
     get("/taler-wire-gateway/config") {
@@ -59,7 +60,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: 
BankApplicationContext)
                 IncomingReserveTransaction(
                     row_id = it.expectRowId(),
                     amount = it.amount.toString(),
-                    date = 
TalerProtocolTimestamp.fromMicroseconds(it.transactionDate),
+                    date = TalerProtocolTimestamp(it.transactionDate),
                     debit_account = it.debtorPaytoUri,
                     reserve_pub = it.subject
                 )
@@ -101,7 +102,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: 
BankApplicationContext)
             throw badRequest("Currency mismatch: $internalCurrency vs 
${req.amount.currency}")
         val exchangeBankAccount = db.bankAccountGetFromOwnerId(c.expectRowId())
             ?: throw internalServerError("Exchange does not have a bank 
account")
-        val transferTimestamp = getNowUs()
+        val transferTimestamp = Instant.now()
         val dbRes = db.talerTransferCreate(
             req = req,
             exchangeBankAccountId = exchangeBankAccount.expectRowId(),
@@ -121,7 +122,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: 
BankApplicationContext)
             ?: throw internalServerError("Database did not return the debit tx 
row ID")
         call.respond(
             TransferResponse(
-                timestamp = 
TalerProtocolTimestamp.fromMicroseconds(transferTimestamp),
+                timestamp = TalerProtocolTimestamp(transferTimestamp),
                 row_id = debitRowId
             )
         )
@@ -151,7 +152,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: 
BankApplicationContext)
             )
         val exchangeAccount = db.bankAccountGetFromOwnerId(c.expectRowId())
             ?: throw internalServerError("exchange bank account not found, 
despite it's a customer")
-        val txTimestamp = getNowUs()
+        val txTimestamp = Instant.now()
         val op = BankInternalTransaction(
             debtorAccountId = walletAccount.expectRowId(),
             amount = amount,
@@ -174,7 +175,7 @@ fun Routing.talerWireGatewayHandlers(db: Database, ctx: 
BankApplicationContext)
         call.respond(
             AddIncomingResponse(
                 row_id = rowId,
-                timestamp = 
TalerProtocolTimestamp.fromMicroseconds(txTimestamp)
+                timestamp = TalerProtocolTimestamp(txTimestamp)
             )
         )
         return@post
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
index 2a79fde6..3007021b 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
@@ -118,7 +118,7 @@ fun doTokenAuth(
         logger.error("Auth token not found")
         return null
     }
-    if (maybeToken.expirationTime - getNowUs() < 0) {
+    if (maybeToken.expirationTime.isBefore(Instant.now())) {
         logger.error("Auth token is expired")
         return null
     }
@@ -438,7 +438,4 @@ fun maybeCreateAdminAccount(db: Database, ctx: 
BankApplicationContext): Boolean
         }
     }
     return true
-}
-
-fun getNowUs(): Long = Instant.now().toDbMicros()
-    ?: throw internalServerError("Could not get micros out of Instant.now()")
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/bank/src/test/kotlin/DatabaseTest.kt 
b/bank/src/test/kotlin/DatabaseTest.kt
index 80ade7c4..890ec41a 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -20,6 +20,7 @@
 import org.junit.Test
 import tech.libeufin.bank.*
 import tech.libeufin.util.CryptoUtil
+import java.time.Instant
 import java.util.Random
 import java.util.UUID
 
@@ -37,7 +38,7 @@ fun genTx(
         accountServicerReference = "acct-svcr-ref",
         endToEndId = "end-to-end-id",
         paymentInformationId = "pmtinfid",
-        transactionDate = 100000L
+        transactionDate = Instant.now()
     )
 
 class DatabaseTest {
@@ -122,7 +123,7 @@ class DatabaseTest {
         val res = db.talerTransferCreate(
             req = exchangeReq,
             exchangeBankAccountId = 1L,
-            timestamp = getNowUs()
+            timestamp = Instant.now()
         )
         assert(res.txResult == Database.BankTransactionResult.SUCCESS)
     }
@@ -135,8 +136,8 @@ class DatabaseTest {
         val token = BearerToken(
             bankCustomer = 1L,
             content = tokenBytes,
-            creationTime = getNowUs(),
-            expirationTime = getNowUs(),
+            creationTime = Instant.now(),
+            expirationTime = Instant.now().plusSeconds(10),
             scope = TokenScope.readonly
         )
         assert(db.bearerTokenGet(token.content) == null)
@@ -196,7 +197,7 @@ class DatabaseTest {
             accountServicerReference = "acct-svcr-ref",
             endToEndId = "end-to-end-id",
             paymentInformationId = "pmtinfid",
-            transactionDate = 100000L
+            transactionDate = Instant.now()
         )
         val barPays = db.bankTransactionCreate(barPaysFoo)
         assert(barPays == Database.BankTransactionResult.SUCCESS)
@@ -272,7 +273,7 @@ class DatabaseTest {
         ))
         val opSelected = db.talerWithdrawalGet(uuid)
         assert(opSelected?.selectionDone == true && 
!opSelected.confirmationDone)
-        assert(db.talerWithdrawalConfirm(uuid, 1L) == 
WithdrawalConfirmationResult.SUCCESS)
+        assert(db.talerWithdrawalConfirm(uuid, Instant.now()) == 
WithdrawalConfirmationResult.SUCCESS)
         // Finally confirming the operation (means customer wired funds to the 
exchange.)
         assert(db.talerWithdrawalGet(uuid)?.confirmationDone == true)
     }
@@ -315,10 +316,10 @@ class DatabaseTest {
             sellOutFee = TalerAmount(0, 44, currency),
             credit_payto_uri = "IBAN",
             cashoutCurrency = "KUDOS",
-            creationTime = 3L,
+            creationTime = Instant.now(),
             subject = "31st",
             tanChannel = TanChannel.sms,
-            tanCode = "secret",
+            tanCode = "secret"
         )
         val fooId = db.customerCreate(customerFoo)
         assert(fooId != null)
@@ -343,7 +344,7 @@ class DatabaseTest {
             accountServicerReference = "acct-svcr-ref",
             endToEndId = "end-to-end-id",
             paymentInformationId = "pmtinfid",
-            transactionDate = 100000L
+            transactionDate = Instant.now()
         )
         ) == Database.BankTransactionResult.SUCCESS)
         // Confirming the cash-out
diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt 
b/bank/src/test/kotlin/LibeuFinApiTest.kt
index 4eb0349e..8683fef8 100644
--- a/bank/src/test/kotlin/LibeuFinApiTest.kt
+++ b/bank/src/test/kotlin/LibeuFinApiTest.kt
@@ -10,6 +10,7 @@ import tech.libeufin.bank.*
 import tech.libeufin.util.CryptoUtil
 import java.time.Duration
 import java.time.Instant
+import java.time.temporal.ChronoUnit
 import kotlin.random.Random
 
 class LibeuFinApiTest {
@@ -179,8 +180,8 @@ class LibeuFinApiTest {
             // Checking that the token lifetime defaulted to 24 hours.
             val newTokObj = 
Json.decodeFromString<TokenSuccessResponse>(newTok.bodyAsText())
             val newTokDb = 
db.bearerTokenGet(Base32Crockford.decode(newTokObj.access_token))
-            val lifeTime = newTokDb!!.expirationTime - newTokDb.creationTime
-            assert(Duration.ofHours(24).seconds * 1000000 == lifeTime)
+            val lifeTime = Duration.between(newTokDb!!.creationTime, 
newTokDb.expirationTime)
+            assert(lifeTime == Duration.ofDays(1))
             // foo tries on bar endpoint
             val r = client.post("/accounts/bar/token") {
                 expectSuccess = false
@@ -195,9 +196,9 @@ class LibeuFinApiTest {
                         content = fooTok,
                         bankCustomer = 1L, // only foo exists.
                         scope = TokenScope.readonly,
-                        creationTime = getNowUs(),
+                        creationTime = Instant.now(),
                         isRefreshable = true,
-                        expirationTime = getNowUs() + 
(Duration.ofHours(1).toMillis() * 1000)
+                        expirationTime = Instant.now().plus(1, ChronoUnit.DAYS)
                     )
                 )
             )
diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt
index ff86c4bf..6c1b9464 100644
--- a/util/src/main/kotlin/time.kt
+++ b/util/src/main/kotlin/time.kt
@@ -20,6 +20,7 @@
 package tech.libeufin.util
 
 import java.time.*
+import java.time.temporal.ChronoUnit
 import java.util.concurrent.TimeUnit
 
 /**
@@ -28,13 +29,17 @@ import java.util.concurrent.TimeUnit
  * if one arithmetic overflow occurred.
  */
 private fun Instant.toNanos(): Long? {
-    val oneSecNanos = TimeUnit.SECONDS.toNanos(1)
+    val oneSecNanos = ChronoUnit.SECONDS.duration.toNanos()
     val nanoBase: Long = this.epochSecond * oneSecNanos
-    if (nanoBase != 0L && nanoBase / this.epochSecond != oneSecNanos)
+    if (nanoBase != 0L && nanoBase / this.epochSecond != oneSecNanos) {
+        logger.error("Multiplication overflow: could not convert Instant to 
nanos.")
         return null
+    }
     val res = nanoBase + this.nano
-    if (res < nanoBase)
+    if (res < nanoBase) {
+        logger.error("Addition overflow: could not convert Instant to nanos.")
         return null
+    }
     return res
 }
 
@@ -55,4 +60,20 @@ fun Instant.toDbMicros(): Long? {
         return Long.MAX_VALUE
     val nanos = this.toNanos() ?: return null
     return nanos / 1000L
+}
+
+/**
+ * This helper is typically used to convert a timestamp expressed
+ * in microseconds from the DB back to the Web application.  In case
+ * of _any_ error, it logs it and returns null.
+ */
+fun Long.microsToJavaInstant(): Instant? {
+    if (this == Long.MAX_VALUE)
+        return Instant.MAX
+    return try {
+        Instant.EPOCH.plus(this, ChronoUnit.MICROS)
+    } catch (e: Exception) {
+        logger.error(e.message)
+        return null
+    }
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]