taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

commit 8e33065eacbe23ec3dff71e630bb765cab7a7a3a
parent 77f35d8fc62fb899aa7faf9e0f884d695365631f
Author: Antoine A <>
Date:   Wed, 18 Mar 2026 12:15:01 +0100

common: add regex config, replace fastrand by rand and improve utils

Diffstat:
MCargo.toml | 6+++---
Mcommon/taler-api/Cargo.toml | 2+-
Mcommon/taler-api/benches/subject.rs | 14+++++++++-----
Mcommon/taler-api/src/db.rs | 4++--
Mcommon/taler-api/src/subject.rs | 2+-
Mcommon/taler-api/tests/common/db.rs | 6+++---
Mcommon/taler-common/Cargo.toml | 3++-
Mcommon/taler-common/benches/base32.rs | 14++++++++------
Mcommon/taler-common/benches/iban.rs | 14+++++++++-----
Mcommon/taler-common/src/api_common.rs | 19+++++++++++++------
Mcommon/taler-common/src/config.rs | 35+++++++++++++++++++++++++++--------
Mcommon/taler-common/src/lib.rs | 4++--
Mcommon/taler-common/src/types/amount.rs | 8+++++++-
Mcommon/taler-common/src/types/base32.rs | 4+---
Mcommon/taler-common/src/types/iban/registry.rs | 6++++--
Mcommon/taler-common/src/types/utils.rs | 6+++---
Mtaler-cyclos/src/db.rs | 20++++++++++----------
Mtaler-magnet-bank/src/api.rs | 6+++---
Mtaler-magnet-bank/src/db.rs | 22+++++++++++-----------
19 files changed, 119 insertions(+), 76 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -40,7 +40,6 @@ sqlx = { version = "0.8", default-features = false, features = [ ] } url = { version = "2.2", features = ["serde"] } criterion = { version = "0.8", default-features = false } -fastrand = { version = "2.2", default-features = false } tracing = "0.1" tracing-subscriber = "0.3" clap = { version = "4.5", features = ["derive"] } @@ -58,4 +57,6 @@ http-body-util = "0.1.2" base64 = "0.22" owo-colors = "4.2.3" aws-lc-rs = "1.15" -compact_str = { version = "0.9.0", features = ["serde", "sqlx-postgres"] } -\ No newline at end of file +compact_str = { version = "0.9.0", features = ["serde", "sqlx-postgres"] } +rand = { version = "0.10" } +regex = { version = "1" } diff --git a/common/taler-api/Cargo.toml b/common/taler-api/Cargo.toml @@ -30,7 +30,7 @@ compact_str.workspace = true [dev-dependencies] taler-test-utils.workspace = true criterion.workspace = true -fastrand.workspace = true +rand.workspace = true [[bench]] name = "subject" diff --git a/common/taler-api/benches/subject.rs b/common/taler-api/benches/subject.rs @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024-2025 Taler Systems SA + Copyright (C) 2024, 2025, 2026 Taler Systems SA TALER 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 @@ -17,11 +17,12 @@ use std::hint::black_box; use criterion::{Criterion, criterion_group, criterion_main}; +use rand::{SeedableRng, seq::IndexedRandom}; use taler_api::subject::parse_incoming_unstructured; fn parser(c: &mut Criterion) { const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789::: \t\t\t\t\n\n\n\n----++++"; - let mut rng = fastrand::Rng::with_seed(42); + let mut rng = rand::rngs::SmallRng::seed_from_u64(42); let real_simple = [ "Taler TEGY6d9mh9pgwvwpgs0z0095z854xegfy7jj202yd0esp8p0za60", "00Q979QSMJ29S7BJT3DDAVC5A0DR5Z05B7N0QT1RCBQ8FXJPZ6RG", @@ -40,9 +41,12 @@ fn parser(c: &mut Criterion) { ]; let randoms: Vec<String> = (0..30) .map(|_| { - (0..256) - .map(|_| *rng.choice(CHARS).unwrap() as char) - .collect() + CHARS + .choose_iter(&mut rng) + .unwrap() + .take(256) + .map(|c| *c as char) + .collect::<String>() }) .collect(); let chunks: Vec<String> = [ diff --git a/common/taler-api/src/db.rs b/common/taler-api/src/db.rs @@ -33,7 +33,7 @@ use taler_common::{ amount::{Amount, Currency, Decimal}, iban::IBAN, payto::PaytoURI, - utils::date_to_utc_timestamp, + utils::date_to_utc_ts, }, }; use tokio::sync::watch::Receiver; @@ -133,7 +133,7 @@ impl<'q> BindHelper for Query<'q, Postgres, <Postgres as sqlx::Database>::Argume } fn bind_date(self, date: &Date) -> Self { - self.bind_timestamp(&date_to_utc_timestamp(date)) + self.bind_timestamp(&date_to_utc_ts(date)) } } diff --git a/common/taler-api/src/subject.rs b/common/taler-api/src/subject.rs @@ -45,7 +45,7 @@ impl IncomingSubject { pub fn key(&self) -> &[u8] { match self { - IncomingSubject::Kyc(key) | IncomingSubject::Reserve(key) => key.as_ref().as_ref(), + IncomingSubject::Kyc(key) | IncomingSubject::Reserve(key) => key.as_ref(), } } } diff --git a/common/taler-api/tests/common/db.rs b/common/taler-api/tests/common/db.rs @@ -64,8 +64,8 @@ pub async fn transfer(db: &PgPool, transfer: TransferRequest) -> sqlx::Result<Tr .bind(transfer.exchange_base_url.as_str()) .bind(format!("{} {}", transfer.wtid, transfer.exchange_base_url)) .bind(transfer.credit_account.raw()) - .bind(transfer.request_uid.as_slice()) - .bind(transfer.wtid.as_slice()) + .bind(transfer.request_uid) + .bind(transfer.wtid) .bind_timestamp(&Timestamp::now()) .try_map(|r: PgRow| { Ok(if r.try_get_flag("out_request_uid_reuse")? { @@ -221,7 +221,7 @@ pub async fn add_incoming( .bind(subject) .bind(debit_account.raw()) .bind(kind) - .bind(key.as_ref().as_slice()) + .bind(key) .bind_timestamp(timestamp) .try_map(|r: PgRow| { Ok(if r.try_get_flag("out_reserve_pub_reuse")? { diff --git a/common/taler-common/Cargo.toml b/common/taler-common/Cargo.toml @@ -19,7 +19,7 @@ serde_with.workspace = true serde_urlencoded.workspace = true url.workspace = true thiserror.workspace = true -fastrand.workspace = true +rand.workspace = true tracing.workspace = true clap.workspace = true anyhow.workspace = true @@ -28,6 +28,7 @@ tokio = { workspace = true, features = ["rt-multi-thread"] } sqlx = { workspace = true, features = ["macros"] } compact_str.workspace = true aws-lc-rs.workspace = true +regex.workspace = true [dev-dependencies] criterion.workspace = true diff --git a/common/taler-common/benches/base32.rs b/common/taler-common/benches/base32.rs @@ -15,17 +15,14 @@ */ use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use rand::RngExt as _; use taler_common::types::base32::{Base32, decode_static, encode_static}; fn parser(c: &mut Criterion) { let mut buf = [0u8; 255]; c.bench_function("base32_encode_random", |b| { b.iter_batched( - || { - let mut bytes = [0; 64]; - fastrand::fill(&mut bytes); - bytes - }, + rand::random::<[u8; 64]>, |case| { encode_static(&case, &mut buf); }, @@ -41,7 +38,12 @@ fn parser(c: &mut Criterion) { }); c.bench_function("base32_decode_random", |b| { b.iter_batched( - || (0..56).map(|_| fastrand::char(..)).collect::<String>(), + || { + rand::rng() + .sample_iter::<char, _>(&rand::distr::StandardUniform) + .take(56) + .collect::<String>() + }, |case| decode_static::<64>(case.as_bytes()).ok(), BatchSize::SmallInput, ) diff --git a/common/taler-common/benches/iban.rs b/common/taler-common/benches/iban.rs @@ -17,6 +17,7 @@ use std::str::FromStr; use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use rand::{RngExt, distr::Alphanumeric, seq::IndexedRandom}; use taler_common::types::iban::{ Country::{self, *}, IBAN, @@ -33,8 +34,9 @@ fn parser(c: &mut Criterion) { c.bench_function("iban_random_all", |b| { b.iter_batched( || { - (0..fastrand::usize(0..40)) - .map(|_| fastrand::char(..)) + rand::rng() + .sample_iter::<char, _>(rand::distr::StandardUniform) + .take(40) .collect::<String>() }, |case| IBAN::from_str(&case), @@ -44,8 +46,10 @@ fn parser(c: &mut Criterion) { c.bench_function("iban_random_alphanumeric", |b| { b.iter_batched( || { - (0..fastrand::usize(0..40)) - .map(|_| fastrand::alphanumeric()) + rand::rng() + .sample_iter(&Alphanumeric) + .take(40) + .map(char::from) .collect::<String>() }, |case| IBAN::from_str(&case), @@ -55,7 +59,7 @@ fn parser(c: &mut Criterion) { c.bench_function("iban_valid", |b| { b.iter_batched( - || IBAN::random(fastrand::choice(COUNTRIES).unwrap()).to_string(), + || IBAN::random(*COUNTRIES.choose(&mut rand::rng()).unwrap()).to_string(), |case| IBAN::from_str(&case).unwrap(), BatchSize::SmallInput, ) diff --git a/common/taler-common/src/api_common.rs b/common/taler-common/src/api_common.rs @@ -126,8 +126,10 @@ pub type EddsaSignature = Base32<64>; #[derive(Debug, Clone, PartialEq, Eq)] pub struct EddsaPublicKey(Base32<32>); -impl AsRef<Base32<32>> for EddsaPublicKey { - fn as_ref(&self) -> &Base32<32> { +impl Deref for EddsaPublicKey { + type Target = Base32<32>; + + fn deref(&self) -> &Self::Target { &self.0 } } @@ -183,6 +185,15 @@ impl TryFrom<&[u8]> for EddsaPublicKey { } } +impl TryFrom<[u8; 32]> for EddsaPublicKey { + type Error = EddsaPublicKeyError; + + fn try_from(value: [u8; 32]) -> Result<Self, Self::Error> { + let encoded = Base32::from(value); + Self::try_from(encoded) + } +} + impl TryFrom<Base32<32>> for EddsaPublicKey { type Error = EddsaPublicKeyError; @@ -193,10 +204,6 @@ impl TryFrom<Base32<32>> for EddsaPublicKey { } impl EddsaPublicKey { - pub fn slice(&self) -> &[u8; 32] { - self.0.deref() - } - pub fn rand() -> EddsaPublicKey { let signing_key = Ed25519KeyPair::generate().unwrap(); let bytes: [u8; 32] = signing_key.public_key().as_ref().try_into().unwrap(); diff --git a/common/taler-common/src/config.rs b/common/taler-common/src/config.rs @@ -829,12 +829,16 @@ impl<'cfg, 'arg> Section<'cfg, 'arg> { }) } - /** Access [option] as Amount */ - pub fn amount(&self, option: &'arg str, currency: &str) -> Value<'arg, Amount> { - let currency: Currency = currency.parse().unwrap(); + /** Access [option] as a Currency */ + pub fn currency(&self, option: &'arg str) -> Value<'arg, Currency> { + self.parse("currency", option) + } + + /** Access [option] as an Amount */ + pub fn amount(&self, option: &'arg str, currency: &Currency) -> Value<'arg, Amount> { self.value("amount", option, |it| { let amount = it.parse::<Amount>().map_err(|e| e.to_string())?; - if amount.currency != currency { + if amount.currency != *currency { return Err(format!( "expected currency {currency} got {}", amount.currency @@ -893,7 +897,12 @@ impl<'cfg, 'arg> Section<'cfg, 'arg> { self.parse("Timestamp", option) } - /** Access [option] as a date time */ + /** Access [option] as a time */ + pub fn time(&self, option: &'arg str) -> Value<'arg, jiff::civil::Time> { + self.parse("Time", option) + } + + /** Access [option] as a date */ pub fn date(&self, option: &'arg str) -> Value<'arg, jiff::civil::Date> { self.parse("Date", option) } @@ -905,6 +914,11 @@ impl<'cfg, 'arg> Section<'cfg, 'arg> { Ok::<_, String>(Duration::from_millis(tmp.as_millis() as u64)) }) } + + /** Access [option] as a regex */ + pub fn regex(&self, option: &'arg str) -> Value<'arg, regex::Regex> { + self.parse("Pattern", option) + } } pub struct Value<'arg, T> { @@ -940,11 +954,15 @@ mod test { fmt::{Debug, Display}, fs::{File, Permissions}, os::unix::fs::PermissionsExt, + str::FromStr, }; use tracing::error; - use crate::{config::parser::ConfigSource, types::amount}; + use crate::{ + config::parser::ConfigSource, + types::amount::{self, Currency}, + }; use super::{Config, Section, Value}; @@ -1075,7 +1093,7 @@ mod test { fn routine<T: Debug + Eq>( ty: &str, - lambda: for<'cfg, 'arg> fn(&Section<'cfg, 'arg>, &'arg str) -> Value<'arg, T>, + mut lambda: impl for<'cfg, 'arg> FnMut(&Section<'cfg, 'arg>, &'arg str) -> Value<'arg, T>, wellformed: &[(&[&str], T)], malformed: &[(&[&str], fn(&str) -> String)], ) { @@ -1209,9 +1227,10 @@ mod test { #[test] fn amount() { + let currency = Currency::from_str("KUDOS").unwrap(); routine( "amount", - |sect, value| sect.amount(value, "KUDOS"), + |sect, value| sect.amount(value, &currency), &[( &["KUDOS:12", "KUDOS:12.0", "KUDOS:012.0"], amount::amount("KUDOS:12"), diff --git a/common/taler-common/src/lib.rs b/common/taler-common/src/lib.rs @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024-2025 Taler Systems SA + Copyright (C) 2024, 2025, 2026 Taler Systems SA TALER 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 @@ -96,7 +96,7 @@ impl ExpoBackoffDecorr { pub fn backoff(&mut self) -> Duration { self.sleep = - fastrand::u32(self.base..(self.sleep as f32 * self.factor) as u32).min(self.max); + rand::random_range(self.base..(self.sleep as f32 * self.factor) as u32).min(self.max); Duration::from_millis(self.sleep as u64) } diff --git a/common/taler-common/src/types/amount.rs b/common/taler-common/src/types/amount.rs @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024-2025 Taler Systems SA + Copyright (C) 2024, 2025, 2026 Taler Systems SA TALER 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 @@ -308,6 +308,12 @@ impl Amount { let decimal = self.decimal().try_add(&rhs.decimal())?.normalize()?; Some((self.currency, decimal).into()) } + + pub fn try_sub(self, rhs: &Self) -> Option<Self> { + assert_eq!(self.currency, rhs.currency); + let decimal = self.decimal().try_sub(&rhs.decimal())?.normalize()?; + Some((self.currency, decimal).into()) + } } impl From<(Currency, Decimal)> for Amount { diff --git a/common/taler-common/src/types/base32.rs b/common/taler-common/src/types/base32.rs @@ -188,9 +188,7 @@ pub struct Base32<const L: usize>([u8; L]); impl<const L: usize> Base32<L> { pub fn rand() -> Self { - let mut bytes = [0; L]; - fastrand::fill(&mut bytes); - Self(bytes) + Self(rand::random()) } } diff --git a/common/taler-common/src/types/iban/registry.rs b/common/taler-common/src/types/iban/registry.rs @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2025 Taler Systems SA + Copyright (C) 2025, 2026 Taler Systems SA TALER 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 @@ -18,6 +18,7 @@ use std::fmt::Display; use Country::*; use IbanC::*; +use rand::seq::IndexedRandom; /// IBAN ASCII characters rules #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -1016,6 +1017,7 @@ impl Display for Country { /// Generate random ASCII string following an IBAN pattern rules pub fn rng_pattern(out: &mut [u8], pattern: Pattern) { let mut cursor = 0; + let mut rng = rand::rng(); for (len, rule) in pattern { let alphabet = match rule { IbanC::C => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", @@ -1023,7 +1025,7 @@ pub fn rng_pattern(out: &mut [u8], pattern: Pattern) { IbanC::A => "ABCDEFGHIJKLMNOPQRSTUVWXYZ", }; for b in &mut out[cursor..cursor + *len as usize] { - *b = *fastrand::choice(alphabet.as_bytes()).unwrap(); + *b = *alphabet.as_bytes().choose(&mut rng).unwrap(); } cursor += *len as usize } diff --git a/common/taler-common/src/types/utils.rs b/common/taler-common/src/types/utils.rs @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2025 Taler Systems SA + Copyright (C) 2025, 2026 Taler Systems SA TALER 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 @@ -92,11 +92,11 @@ impl<const LEN: usize> Deref for InlineStr<LEN> { } /** Convert a date to a UTC timestamp */ -pub fn date_to_utc_timestamp(date: &Date) -> Timestamp { +pub fn date_to_utc_ts(date: &Date) -> Timestamp { date.to_zoned(TimeZone::UTC).unwrap().timestamp() } /** Get current timestamp truncated to micros precision */ -pub fn now_sql_stable_timestamp() -> Timestamp { +pub fn now_sql_stable_ts() -> Timestamp { Timestamp::from_microsecond(Timestamp::now().as_microsecond()).unwrap() } diff --git a/taler-cyclos/src/db.rs b/taler-cyclos/src/db.rs @@ -324,8 +324,8 @@ pub async fn register_tx_out( .bind(None::<i64>), TxOutKind::Bounce(bounced) => query.bind(None::<&[u8]>).bind(None::<&str>).bind(*bounced), TxOutKind::Talerable(subject) => query - .bind(subject.wtid.as_ref()) - .bind(subject.exchange_base_url.as_ref()) + .bind(&subject.wtid) + .bind(subject.exchange_base_url.as_str()) .bind(None::<i64>), }; query @@ -369,8 +369,8 @@ pub async fn make_transfer<'a>( FROM taler_transfer($1, $2, $3, $4, $5, $6, $7, $8) ", ) - .bind(tx.request_uid.as_ref()) - .bind(tx.wtid.as_ref()) + .bind(&tx.request_uid) + .bind(&tx.wtid) .bind(&subject) .bind(tx.amount) .bind(tx.exchange_base_url.as_str()) @@ -853,7 +853,7 @@ mod test { types::{ amount::{Currency, decimal}, url, - utils::now_sql_stable_timestamp, + utils::now_sql_stable_ts, }, }; @@ -913,7 +913,7 @@ mod test { .fetch_one(&mut *db) .await .unwrap(); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let later = now + Span::new().hours(2); let tx = TxIn { transfer_id: now.as_microsecond() as i64, @@ -1038,7 +1038,7 @@ mod test { Vec::new() ); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let later = now + Span::new().hours(2); let tx = TxInAdmin { amount: decimal("10"), @@ -1109,7 +1109,7 @@ mod test { .fetch_one(&mut *db) .await .unwrap(); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let later = now + Span::new().hours(2); let tx = TxOut { transfer_id, @@ -1251,7 +1251,7 @@ mod test { creditor_id: 31000163100000000, creditor_name: "Name".into(), }; - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let later = now + Span::new().hours(2); // Insert assert_eq!( @@ -1353,7 +1353,7 @@ mod test { let (mut db, _) = setup().await; let amount = decimal("10"); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); // Bounce assert_eq!( diff --git a/taler-magnet-bank/src/api.rs b/taler-magnet-bank/src/api.rs @@ -30,7 +30,7 @@ use taler_common::{ TransferState, TransferStatus, }, error_code::ErrorCode, - types::{payto::PaytoURI, utils::date_to_utc_timestamp}, + types::{payto::PaytoURI, utils::date_to_utc_ts}, }; use tokio::sync::watch::Sender; @@ -171,7 +171,7 @@ impl WireGateway for MagnetApi { row_id, valued_at, .. } => Ok(AddIncomingResponse { row_id: safe_u64(row_id), - timestamp: date_to_utc_timestamp(&valued_at).into(), + timestamp: date_to_utc_ts(&valued_at).into(), }), AddIncomingResult::ReservePubReuse => Err(failure( ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT, @@ -198,7 +198,7 @@ impl WireGateway for MagnetApi { row_id, valued_at, .. } => Ok(AddKycauthResponse { row_id: safe_u64(row_id), - timestamp: date_to_utc_timestamp(&valued_at).into(), + timestamp: date_to_utc_ts(&valued_at).into(), }), AddIncomingResult::ReservePubReuse => Err(failure( ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT, diff --git a/taler-magnet-bank/src/db.rs b/taler-magnet-bank/src/db.rs @@ -312,8 +312,8 @@ pub async fn register_tx_out( .bind(None::<&str>) .bind(*bounced as i64), TxOutKind::Talerable(subject) => query - .bind(subject.wtid.as_ref()) - .bind(subject.exchange_base_url.as_ref()) + .bind(&subject.wtid) + .bind(subject.exchange_base_url.as_str()) .bind(None::<i64>), }; query @@ -387,8 +387,8 @@ pub async fn make_transfer<'a>( FROM taler_transfer($1, $2, $3, $4, $5, $6, $7, $8) ", ) - .bind(tx.request_uid.as_ref()) - .bind(tx.wtid.as_ref()) + .bind(&tx.request_uid) + .bind(&tx.wtid) .bind(&subject) .bind(tx.amount) .bind(tx.exchange_base_url.as_str()) @@ -819,7 +819,7 @@ mod test { types::{ amount::{amount, decimal}, url, - utils::now_sql_stable_timestamp, + utils::now_sql_stable_ts, }, }; @@ -878,7 +878,7 @@ mod test { .fetch_one(&mut *db) .await .unwrap(); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let date = Zoned::now().date(); let later = date.tomorrow().unwrap(); let tx = TxIn { @@ -1005,7 +1005,7 @@ mod test { Vec::new() ); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let later = now + Span::new().hours(2); let date = Zoned::now().date(); let tx = TxInAdmin { @@ -1077,7 +1077,7 @@ mod test { .fetch_one(&mut *db) .await .unwrap(); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let date = Zoned::now().date(); let later = date.tomorrow().unwrap(); let tx = TxOut { @@ -1195,7 +1195,7 @@ mod test { async fn tx_out_failure() { let (mut db, _) = setup().await; - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); // Unknown assert_eq!( @@ -1315,7 +1315,7 @@ mod test { wtid: ShortHashCode::rand(), creditor: magnet_payto("payto://iban/HU02162000031000164800000000?receiver-name=name"), }; - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let later = now + Span::new().hours(2); // Insert assert_eq!( @@ -1401,7 +1401,7 @@ mod test { let amount = amount("HUF:10"); let payto = magnet_payto("payto://iban/HU30162000031000163100000000?receiver-name=name"); - let now = now_sql_stable_timestamp(); + let now = now_sql_stable_ts(); let date = Zoned::now().date(); // Empty db