[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.