commit 8aa7ebfd6b4880a01af57b1dbdfb9a18e86744f3
parent 392dde21040646a83433ddd99c22fae3c2e13d11
Author: Antoine A <>
Date: Tue, 4 Nov 2025 11:00:37 +0100
magnet-bank: use correct date type
Diffstat:
12 files changed, 204 insertions(+), 152 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -4,9 +4,9 @@ version = 4
[[package]]
name = "aho-corasick"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
@@ -211,9 +211,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.2.43"
+version = "1.2.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2"
+checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -260,9 +260,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.50"
+version = "4.5.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
+checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
dependencies = [
"clap_builder",
"clap_derive",
@@ -270,9 +270,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.50"
+version = "4.5.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
+checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
dependencies = [
"anstream",
"anstyle",
@@ -964,7 +964,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
- "webpki-roots 1.0.3",
+ "webpki-roots 1.0.4",
]
[[package]]
@@ -993,9 +993,9 @@ dependencies = [
[[package]]
name = "icu_collections"
-version = "2.0.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
dependencies = [
"displaydoc",
"potential_utf",
@@ -1006,9 +1006,9 @@ dependencies = [
[[package]]
name = "icu_locale_core"
-version = "2.0.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
dependencies = [
"displaydoc",
"litemap",
@@ -1019,11 +1019,10 @@ dependencies = [
[[package]]
name = "icu_normalizer"
-version = "2.0.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
dependencies = [
- "displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
@@ -1034,42 +1033,38 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
-version = "2.0.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
[[package]]
name = "icu_properties"
-version = "2.0.1"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
dependencies = [
- "displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
- "potential_utf",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
-version = "2.0.1"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
[[package]]
name = "icu_provider"
-version = "2.0.0"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
dependencies = [
"displaydoc",
"icu_locale_core",
- "stable_deref_trait",
- "tinystr",
"writeable",
"yoke",
"zerofrom",
@@ -1200,18 +1195,18 @@ checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "libdeflate-sys"
-version = "1.24.0"
+version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "805824325366c44599dfeb62850fe3c7d7b3e3d75f9ab46785bc7dba3676815c"
+checksum = "23bd6304ebf75390d8a99b88bdf2a266f62647838140cb64af8e6702f6e3fddc"
dependencies = [
"cc",
]
[[package]]
name = "libdeflater"
-version = "1.24.0"
+version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b270bcc7e9d6dce967a504a55b1b0444f966aa9184e8605b531bc0492abb30bb"
+checksum = "d5d4880e6d634d3d029d65fa016038e788cc728a17b782684726fb34ee140caf"
dependencies = [
"libdeflate-sys",
]
@@ -1246,9 +1241,9 @@ dependencies = [
[[package]]
name = "litemap"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]]
name = "lock_api"
@@ -1456,9 +1451,9 @@ dependencies = [
[[package]]
name = "potential_utf"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
+checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
dependencies = [
"zerovec",
]
@@ -1692,7 +1687,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots 1.0.3",
+ "webpki-roots 1.0.4",
]
[[package]]
@@ -1773,9 +1768,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.103.7"
+version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf"
+checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"ring",
"rustls-pki-types",
@@ -2337,9 +2332,9 @@ dependencies = [
[[package]]
name = "tinystr"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"zerovec",
@@ -2542,24 +2537,24 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-ident"
-version = "1.0.20"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-normalization"
-version = "0.1.24"
+version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
+checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-properties"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
+checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
[[package]]
name = "untrusted"
@@ -2737,14 +2732,14 @@ version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
- "webpki-roots 1.0.3",
+ "webpki-roots 1.0.4",
]
[[package]]
name = "webpki-roots"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8"
+checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
dependencies = [
"rustls-pki-types",
]
@@ -3035,17 +3030,16 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "writeable"
-version = "0.6.1"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "yoke"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
dependencies = [
- "serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
@@ -3053,9 +3047,9 @@ dependencies = [
[[package]]
name = "yoke-derive"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
@@ -3112,9 +3106,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zerotrie"
-version = "0.2.2"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
dependencies = [
"displaydoc",
"yoke",
@@ -3123,9 +3117,9 @@ dependencies = [
[[package]]
name = "zerovec"
-version = "0.11.4"
+version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
+checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"yoke",
"zerofrom",
@@ -3134,9 +3128,9 @@ dependencies = [
[[package]]
name = "zerovec-derive"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
diff --git a/common/taler-api/src/db.rs b/common/taler-api/src/db.rs
@@ -16,6 +16,10 @@
use std::{str::FromStr, time::Duration};
+use jiff::{
+ civil::{Date, Time},
+ tz::TimeZone,
+};
use sqlx::{
Decode, Error, PgExecutor, PgPool, QueryBuilder, Type, error::BoxDynError, postgres::PgRow,
query::Query,
@@ -30,6 +34,7 @@ use taler_common::{
iban::IBAN,
payto::PaytoURI,
timestamp::Timestamp,
+ utils::date_to_utc_timestamp,
},
};
use tokio::sync::watch::Receiver;
@@ -122,6 +127,7 @@ pub trait BindHelper {
fn bind_amount(self, amount: &Amount) -> Self;
fn bind_decimal(self, decimal: &Decimal) -> Self;
fn bind_timestamp(self, timestamp: &Timestamp) -> Self;
+ fn bind_date(self, date: &Date) -> Self;
}
impl<'q> BindHelper for Query<'q, Postgres, <Postgres as sqlx::Database>::Arguments<'q>> {
@@ -136,6 +142,10 @@ impl<'q> BindHelper for Query<'q, Postgres, <Postgres as sqlx::Database>::Argume
fn bind_timestamp(self, timestamp: &Timestamp) -> Self {
self.bind(timestamp.as_sql_micros())
}
+
+ fn bind_date(self, date: &Date) -> Self {
+ self.bind_timestamp(&date_to_utc_timestamp(date))
+ }
}
/* ----- Get ----- */
@@ -160,6 +170,12 @@ pub trait TypeHelper {
fn try_get_timestamp<I: sqlx::ColumnIndex<Self>>(&self, index: I) -> sqlx::Result<Timestamp> {
self.try_get_map(index, Timestamp::from_sql_micros)
}
+ fn try_get_date<I: sqlx::ColumnIndex<Self>>(&self, index: I) -> sqlx::Result<Date> {
+ let timestamp = self.try_get_timestamp(index)?;
+ let zoned = timestamp.0.to_zoned(TimeZone::UTC);
+ assert_eq!(zoned.time(), Time::midnight());
+ Ok(zoned.date())
+ }
fn try_get_u32<I: sqlx::ColumnIndex<Self>>(&self, index: I) -> sqlx::Result<u32> {
self.try_get_map(index, |signed: i32| signed.try_into())
}
diff --git a/common/taler-common/src/types/timestamp.rs b/common/taler-common/src/types/timestamp.rs
@@ -22,7 +22,7 @@ use serde_json::Value;
/// <https://docs.taler.net/core/api-common.html#tsref-type-Timestamp>
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub struct Timestamp(jiff::Timestamp);
+pub struct Timestamp(pub jiff::Timestamp);
#[derive(Serialize, Deserialize)]
struct TimestampImpl {
diff --git a/common/taler-common/src/types/utils.rs b/common/taler-common/src/types/utils.rs
@@ -16,6 +16,10 @@
use std::{fmt::Debug, ops::Deref};
+use jiff::{civil::Date, tz::TimeZone};
+
+use crate::types::timestamp::Timestamp;
+
#[derive(Clone, PartialEq, Eq)]
pub struct InlineStr<const LEN: usize> {
/// Len of ascii string in buf
@@ -88,3 +92,9 @@ impl<const LEN: usize> Deref for InlineStr<LEN> {
unsafe { self.buf.get_unchecked(..self.len as usize) }
}
}
+
+/** Convert a date to a UTC timestamp */
+pub fn date_to_utc_timestamp(date: &Date) -> Timestamp {
+ let zoned = date.to_zoned(TimeZone::UTC).unwrap();
+ Timestamp(zoned.timestamp())
+}
diff --git a/taler-magnet-bank/src/api.rs b/taler-magnet-bank/src/api.rs
@@ -29,7 +29,7 @@ use taler_common::{
TransferState, TransferStatus,
},
error_code::ErrorCode,
- types::{payto::PaytoURI, timestamp::Timestamp},
+ types::{payto::PaytoURI, timestamp::Timestamp, utils::date_to_utc_timestamp},
};
use tokio::sync::watch::Sender;
@@ -159,7 +159,7 @@ impl WireGateway for MagnetApi {
row_id, valued_at, ..
} => Ok(AddIncomingResponse {
row_id: safe_u64(row_id),
- timestamp: valued_at,
+ timestamp: date_to_utc_timestamp(&valued_at),
}),
AddIncomingResult::ReservePubReuse => Err(failure(
ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
@@ -186,7 +186,7 @@ impl WireGateway for MagnetApi {
row_id, valued_at, ..
} => Ok(AddKycauthResponse {
row_id: safe_u64(row_id),
- timestamp: valued_at,
+ timestamp: date_to_utc_timestamp(&valued_at),
}),
AddIncomingResult::ReservePubReuse => Err(failure(
ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
diff --git a/taler-magnet-bank/src/bin/magnet-bank-harness.rs b/taler-magnet-bank/src/bin/magnet-bank-harness.rs
@@ -156,7 +156,7 @@ impl HarnessClient<'_> {
)
.await?;
self.api
- .sign_tx(
+ .submit_tx(
self.signing_key,
&from.number,
info.code,
@@ -330,28 +330,29 @@ fn main() {
.assert_transfer_status(transfer_id, TransferState::success, None)
.await?;
- step("Test transfer failure create-tx");
+ step("Test transfer failure init-tx");
harness.transfer(10).await?;
- set_failure_logic(FailureLogic::History(vec!["create-tx"]));
+ set_failure_logic(FailureLogic::History(vec!["init-tx"]));
assert!(matches!(
worker.run().await,
- Err(WorkerError::Injected(InjectedErr("create-tx")))
+ Err(WorkerError::Injected(InjectedErr("init-tx")))
));
balance.expect(0).await?;
worker.run().await?;
balance.expect(-10).await?;
+ worker.run().await?;
- step("Test transfer failure sign-tx");
+ step("Test transfer failure submit-tx");
harness.transfer(11).await?;
- set_failure_logic(FailureLogic::History(vec!["sign-tx"]));
+ set_failure_logic(FailureLogic::History(vec!["submit-tx"]));
assert!(matches!(
worker.run().await,
- Err(WorkerError::Injected(InjectedErr("sign-tx")))
+ Err(WorkerError::Injected(InjectedErr("submit-tx")))
));
balance.expect(0).await?;
worker.run().await?;
- // TODO both transactions came through which is VERY WRONG waiting on Magnet Bank on the matter
- balance.expect(-22).await?;
+ balance.expect(-11).await?;
+ worker.run().await?;
Ok(())
});
}
diff --git a/taler-magnet-bank/src/config.rs b/taler-magnet-bank/src/config.rs
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-use jiff::tz::TimeZone;
+use jiff::civil::Date;
use reqwest::Url;
use taler_api::{
Serve,
@@ -81,8 +81,8 @@ pub struct WorkerCfg {
pub consumer: Token,
pub keys_path: String,
pub account_type: AccountType,
- pub ignore_tx_before: Option<jiff::Timestamp>,
- pub ignore_bounces_before: Option<jiff::Timestamp>,
+ pub ignore_tx_before: Option<Date>,
+ pub ignore_bounces_before: Option<Date>,
}
impl WorkerCfg {
@@ -102,14 +102,8 @@ impl WorkerCfg {
secret: sect.str("CONSUMER_SECRET").require()?,
},
keys_path: sect.path("KEYS_FILE").require()?,
- ignore_tx_before: sect
- .date("IGNORE_TRANSACTIONS_BEFORE")
- .opt()?
- .map(|d| d.to_zoned(TimeZone::system()).unwrap().timestamp()),
- ignore_bounces_before: sect
- .date("IGNORE_BOUNCES_BEFORE")
- .opt()?
- .map(|d| d.to_zoned(TimeZone::system()).unwrap().timestamp()),
+ ignore_tx_before: sect.date("IGNORE_TRANSACTIONS_BEFORE").opt()?,
+ ignore_bounces_before: sect.date("IGNORE_BOUNCES_BEFORE").opt()?,
})
}
}
diff --git a/taler-magnet-bank/src/db.rs b/taler-magnet-bank/src/db.rs
@@ -16,6 +16,7 @@
use std::fmt::Display;
+use jiff::{civil::Date, tz::TimeZone};
use sqlx::{PgConnection, PgExecutor, PgPool, QueryBuilder, Row, postgres::PgRow};
use taler_api::{
db::{BindHelper, IncomingType, TypeHelper, history, page},
@@ -32,7 +33,7 @@ use taler_common::{
};
use tokio::sync::watch::{Receiver, Sender};
-use crate::{FullHuPayto, constant::CURRENCY};
+use crate::{FullHuPayto, constant::CURRENCY, magnet::TxStatus};
pub async fn notification_listener(
pool: PgPool,
@@ -63,7 +64,7 @@ pub struct TxIn {
pub amount: Amount,
pub subject: String,
pub debtor: FullHuPayto,
- pub value_date: Timestamp,
+ pub value_date: Date,
}
impl Display for TxIn {
@@ -90,7 +91,8 @@ pub struct TxOut {
pub amount: Amount,
pub subject: String,
pub creditor: FullHuPayto,
- pub value_date: Timestamp,
+ pub value_date: Date,
+ pub status: TxStatus,
}
impl Display for TxOut {
@@ -101,10 +103,11 @@ impl Display for TxOut {
subject,
creditor,
value_date,
+ status,
} = self;
write!(
f,
- "{value_date} {amount} {code} ({} {}) '{subject}'",
+ "{value_date} {amount} {code} ({} {}) {status:?} '{subject}'",
creditor.bban(),
&creditor.name
)
@@ -155,7 +158,7 @@ pub enum AddIncomingResult {
Success {
new: bool,
row_id: u64,
- valued_at: Timestamp,
+ valued_at: Date,
},
ReservePubReuse,
}
@@ -175,7 +178,7 @@ pub async fn register_tx_in_admin(
.bind(&tx.subject)
.bind(tx.debtor.iban())
.bind(&tx.debtor.name)
- .bind_timestamp(now)
+ .bind_date(&now.0.to_zoned(TimeZone::UTC).date())
.bind(tx.metadata.ty())
.bind(tx.metadata.key())
.try_map(|r: PgRow| {
@@ -184,7 +187,7 @@ pub async fn register_tx_in_admin(
} else {
AddIncomingResult::Success {
row_id: r.try_get_u64(1)?,
- valued_at: r.try_get_timestamp(2)?,
+ valued_at: r.try_get_date(2)?,
new: r.try_get(3)?,
}
})
@@ -210,7 +213,7 @@ pub async fn register_tx_in(
.bind(&tx.subject)
.bind(tx.debtor.iban())
.bind(&tx.debtor.name)
- .bind_timestamp(&tx.value_date)
+ .bind_date(&tx.value_date)
.bind(subject.as_ref().map(|it| it.ty()))
.bind(subject.as_ref().map(|it| it.key()))
.bind_timestamp(now)
@@ -220,7 +223,7 @@ pub async fn register_tx_in(
} else {
AddIncomingResult::Success {
row_id: r.try_get_u64(1)?,
- valued_at: r.try_get_timestamp(2)?,
+ valued_at: r.try_get_date(2)?,
new: r.try_get(3)?,
}
})
@@ -253,7 +256,7 @@ pub async fn register_tx_out(
.bind(&tx.subject)
.bind(tx.creditor.iban())
.bind(&tx.creditor.name)
- .bind_timestamp(&tx.value_date);
+ .bind_date(&tx.value_date);
let query = match kind {
TxOutKind::Simple => query
.bind(None::<&[u8]>)
@@ -350,7 +353,7 @@ pub async fn register_bounce_tx_in(
.bind(&tx.subject)
.bind(tx.debtor.iban())
.bind(&tx.debtor.name)
- .bind_timestamp(&tx.value_date)
+ .bind_date(&tx.value_date)
.bind_amount(amount)
.bind(reason)
.bind_timestamp(now)
@@ -676,9 +679,21 @@ pub async fn initiated_submit_permanent_failure<'a>(
Ok(())
}
+/** Check if an initiated transaction exist for a magnet code */
+pub async fn initiated_exists_for_code<'a>(
+ db: impl PgExecutor<'a>,
+ code: u64,
+) -> sqlx::Result<Option<u64>> {
+ sqlx::query("SELECT initiated_id FROM initiated WHERE magnet_code=$1")
+ .bind(code as i64)
+ .try_map(|r| Ok(r.try_get::<i64, _>(0)? as u64))
+ .fetch_optional(db)
+ .await
+}
+
#[cfg(test)]
mod test {
- use jiff::Span;
+ use jiff::{Span, Zoned};
use sqlx::{PgConnection, PgPool, postgres::PgRow};
use taler_api::{
db::TypeHelper,
@@ -700,6 +715,7 @@ mod test {
TxIn, TxOut, TxOutKind, make_transfer, register_bounce_tx_in, register_tx_in,
register_tx_in_admin, register_tx_out,
},
+ magnet::TxStatus,
magnet_payto,
};
@@ -731,7 +747,8 @@ mod test {
.await
.unwrap();
let now = Timestamp::now_stable();
- let later = now + Span::new().hours(4);
+ let date = Zoned::now().date();
+ let later = date.tomorrow().unwrap();
let tx = TxIn {
code: code,
amount: amount("EUR:10"),
@@ -739,7 +756,7 @@ mod test {
debtor: magnet_payto(
"payto://iban/HU30162000031000163100000000?receiver-name=name",
),
- value_date: now,
+ value_date: date,
};
// Insert
assert_eq!(
@@ -749,7 +766,7 @@ mod test {
AddIncomingResult::Success {
new: true,
row_id: id,
- valued_at: now
+ valued_at: date
}
);
// Idempotent
@@ -768,7 +785,7 @@ mod test {
AddIncomingResult::Success {
new: false,
row_id: id,
- valued_at: now
+ valued_at: date
}
);
// Many
@@ -856,7 +873,8 @@ mod test {
);
let now = Timestamp::now_stable();
- let later = now + Span::new().hours(3);
+ let later = now + Span::new().hours(2);
+ let date = Zoned::now().date();
let tx = TxInAdmin {
amount: amount("EUR:10"),
subject: "subject".to_owned(),
@@ -871,7 +889,7 @@ mod test {
AddIncomingResult::Success {
new: true,
row_id: 1,
- valued_at: now
+ valued_at: date
}
);
// Idempotent
@@ -882,7 +900,7 @@ mod test {
AddIncomingResult::Success {
new: false,
row_id: 1,
- valued_at: now
+ valued_at: date
}
);
// Many
@@ -901,7 +919,7 @@ mod test {
AddIncomingResult::Success {
new: true,
row_id: 2,
- valued_at: later
+ valued_at: date
}
);
@@ -927,7 +945,8 @@ mod test {
.await
.unwrap();
let now = Timestamp::now_stable();
- let later = now + Span::new().hours(4);
+ let date = Zoned::now().date();
+ let later = date.tomorrow().unwrap();
let tx = TxOut {
code,
amount: amount("EUR:10"),
@@ -935,7 +954,8 @@ mod test {
creditor: magnet_payto(
"payto://iban/HU30162000031000163100000000?receiver-name=name",
),
- value_date: now,
+ value_date: date,
+ status: TxStatus::Completed,
};
// Insert
assert_eq!(
@@ -1137,6 +1157,7 @@ mod test {
let amount = amount("HUF:10");
let payto = magnet_payto("payto://iban/HU30162000031000163100000000?receiver-name=name");
let now = Timestamp::now_stable();
+ let date = Zoned::now().date();
// Empty db
assert!(db::pending_batch(&mut db, &now).await.unwrap().is_empty());
@@ -1150,7 +1171,7 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- value_date: now
+ value_date: date
},
&None,
&now
@@ -1160,7 +1181,7 @@ mod test {
AddIncomingResult::Success {
new: true,
row_id: 1,
- valued_at: now
+ valued_at: date
}
);
@@ -1173,7 +1194,7 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- value_date: now
+ value_date: date
},
&amount,
"good reason",
@@ -1197,7 +1218,7 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- value_date: now
+ value_date: date
},
&amount,
"good reason",
@@ -1222,7 +1243,7 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- value_date: now
+ value_date: date
},
&amount,
"good reason",
@@ -1246,7 +1267,7 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- value_date: now
+ value_date: date
},
&amount,
"good reason",
diff --git a/taler-magnet-bank/src/dev.rs b/taler-magnet-bank/src/dev.rs
@@ -72,7 +72,7 @@ pub async fn dev(cfg: &Config, cmd: DevCmd) -> anyhow::Result<()> {
for partner in res.partners {
for account in partner.bank_accounts {
let payto = account.iban.as_full_payto(&partner.partner.name);
- info!("{} {} {}", account.code, account.currency.symbol, payto);
+ info!(target: "dev", "{} {} {}", account.code, account.currency.symbol, payto);
}
}
}
@@ -92,8 +92,8 @@ pub async fn dev(cfg: &Config, cmd: DevCmd) -> anyhow::Result<()> {
for item in page.list {
let tx = extract_tx_info(item.tx);
match tx {
- Tx::In(tx_in) => info!("in {tx_in}"),
- Tx::Out(tx_out) => info!("out {tx_out}"),
+ Tx::In(tx_in) => info!(target: "dev", "in {tx_in}"),
+ Tx::Out(tx_out) => info!(target: "dev", "out {tx_out}"),
}
}
if next.is_none() {
@@ -133,7 +133,7 @@ pub async fn dev(cfg: &Config, cmd: DevCmd) -> anyhow::Result<()> {
)
.await?;
client
- .sign_tx(
+ .submit_tx(
&keys.signing_key,
&account.number,
init.code,
diff --git a/taler-magnet-bank/src/magnet.rs b/taler-magnet-bank/src/magnet.rs
@@ -269,9 +269,9 @@ pub struct Transaction {
#[serde(rename = "tranzakcioAltipus")]
pub kind: Option<String>,
#[serde(rename = "eredetiErteknap")]
- pub tx_date: jiff::Timestamp,
+ pub tx_date: jiff::civil::Date,
#[serde(rename = "erteknap")]
- pub value_date: jiff::Timestamp,
+ pub value_date: jiff::civil::Date,
#[serde(rename = "eszamla")]
pub counter_account: String,
#[serde(rename = "epartner")]
@@ -518,7 +518,7 @@ impl ApiClient<'_> {
.info)
}
- pub async fn sign_tx(
+ pub async fn submit_tx(
&self,
signing_key: &SigningKey,
bban: &str,
diff --git a/taler-magnet-bank/src/worker.rs b/taler-magnet-bank/src/worker.rs
@@ -33,7 +33,7 @@ use crate::{
db::{self, AddIncomingResult, Initiated, TxIn, TxOut, TxOutKind},
failure_injection::{InjectedErr, fail_point},
magnet::{
- ApiClient, Direction, Transaction,
+ ApiClient, Direction, Transaction, TxStatus,
error::{ApiError, MagnetError},
},
};
@@ -57,8 +57,8 @@ pub struct Worker<'a> {
pub account_code: u64,
pub key: &'a SigningKey,
pub account_type: AccountType,
- pub ignore_tx_before: Option<jiff::Timestamp>,
- pub ignore_bounces_before: Option<jiff::Timestamp>,
+ pub ignore_tx_before: Option<Date>,
+ pub ignore_bounces_before: Option<Date>,
}
impl Worker<'_> {
@@ -167,6 +167,12 @@ impl Worker<'_> {
}
}
Tx::Out(tx_out) => {
+ if tx_out.status == TxStatus::ToBeRecorded {
+ self.recover_tx(&tx_out).await?;
+ continue;
+ } else if tx_out.status != TxStatus::Completed {
+ continue;
+ }
match self.account_type {
AccountType::Exchange => {
// TODO log status (known | recovered | founded)
@@ -237,8 +243,6 @@ impl Worker<'_> {
}
}
- // Recover pending transaction
-
// Send transactions
let start = Timestamp::now();
let now = Zoned::now();
@@ -249,29 +253,36 @@ impl Worker<'_> {
}
for tx in batch {
debug!(target: "worker", "send tx {tx}");
- self.create_tx(&tx, &now).await?;
+ self.init_tx(&tx, &now).await?;
}
}
Ok(())
}
/// Try to sign an unsigned initiated transaction
- pub async fn recover_tx(&mut self, tx: &Transaction) -> WorkerResult {
- // This transaction have not been signed yet, something went wrong
- // if in db
- // Then try to sign it -> we completed the transaction
- // else
- // The transaction is unknowned (we failed after creating it and before storing it in the db)
- // we delete it
- // TODO
- self.client.delete_tx(tx.code).await?;
- debug!(target: "worker", "out {}: delete uncompleted orphan", tx.code);
+ pub async fn recover_tx(&mut self, tx: &TxOut) -> WorkerResult {
+ if let Some(_) = db::initiated_exists_for_code(&mut *self.db, tx.code).await? {
+ // Known initiated we submit it
+ assert_eq!(tx.amount.frac, 0);
+ self.submit_tx(
+ tx.code,
+ -(tx.amount.val as f64),
+ &tx.value_date,
+ tx.creditor.bban(),
+ )
+ .await?;
+ } else {
+ // The transaction is unknown (we failed after creating it and before storing it in the db)
+ // we delete it
+ self.client.delete_tx(tx.code).await?;
+ debug!(target: "worker", "out {}: delete uncompleted orphan", tx.code);
+ }
Ok(())
}
/// Create and sign a forint transfer
- pub async fn create_tx(&mut self, tx: &Initiated, now: &Zoned) -> WorkerResult {
+ pub async fn init_tx(&mut self, tx: &Initiated, now: &Zoned) -> WorkerResult {
trace!(target: "worker", "create tx {tx}");
assert_eq!(tx.amount.frac, 0);
let date = now.date();
@@ -287,8 +298,7 @@ impl Worker<'_> {
tx.creditor.bban(),
)
.await;
- debug!("{res:?}");
- fail_point("create-tx")?;
+ fail_point("init-tx")?;
let info = match res {
// Check if succeeded
Ok(info) => {
@@ -325,27 +335,27 @@ impl Worker<'_> {
}
Err(e) => return WorkerResult::Err(WorkerError::Api(e)),
};
- trace!(target: "worker", "created tx {}", info.code);
+ trace!(target: "worker", "init tx {}", info.code);
// Sign transaction
- self.sign_tx(info.code, info.amount, &date, tx.creditor.bban())
+ self.submit_tx(info.code, info.amount, &date, tx.creditor.bban())
.await?;
Ok(())
}
- /** Sign an initiated forint transfer */
- pub async fn sign_tx(
+ /** Submit an initiated forint transfer */
+ pub async fn submit_tx(
&mut self,
tx_code: u64,
amount: f64,
date: &Date,
creditor: &str,
) -> WorkerResult {
- debug!(target: "worker", "sign tx {tx_code}");
- fail_point("sign-tx")?;
+ debug!(target: "worker", "submit tx {tx_code}");
+ fail_point("submit-tx")?;
// Sign initiated transaction, on failure we will retry
self.client
- .sign_tx(
+ .submit_tx(
self.key,
self.account_number,
tx_code,
@@ -375,12 +385,13 @@ pub fn extract_tx_info(tx: Transaction) -> Tx {
};
let counter_account = FullHuPayto::new(iban, tx.counter_name);
if tx.amount.is_sign_positive() {
+ assert_eq!(tx.status, TxStatus::Completed, "Can this happen ?");
Tx::In(TxIn {
code: tx.code,
amount,
subject: tx.subject,
debtor: counter_account,
- value_date: Timestamp::from(tx.value_date),
+ value_date: tx.value_date,
})
} else {
Tx::Out(TxOut {
@@ -388,7 +399,8 @@ pub fn extract_tx_info(tx: Transaction) -> Tx {
amount,
subject: tx.subject,
creditor: counter_account,
- value_date: Timestamp::from(tx.value_date),
+ value_date: tx.value_date,
+ status: tx.status,
})
}
}
diff --git a/taler-magnet-bank/tests/api.rs b/taler-magnet-bank/tests/api.rs
@@ -16,6 +16,7 @@
use std::sync::Arc;
+use jiff::Zoned;
use sqlx::PgPool;
use taler_api::{api::TalerRouter as _, auth::AuthMethod, subject::OutgoingSubject};
use taler_common::{
@@ -28,6 +29,7 @@ use taler_magnet_bank::{
CONFIG_SOURCE,
api::MagnetApi,
db::{self, TxOutKind},
+ magnet::TxStatus,
magnet_payto,
};
use taler_test_utils::{
@@ -91,9 +93,10 @@ async fn outgoing_history() {
},
|_, i| {
let acquire = pool.acquire();
+ Timestamp::now().to_string();
async move {
let mut conn = acquire.await.unwrap();
- let now = Timestamp::now();
+ let now = Zoned::now().date();
db::register_tx_out(
&mut *conn,
&db::TxOut {
@@ -104,12 +107,13 @@ async fn outgoing_history() {
"payto://iban/HU30162000031000163100000000?receiver-name=name",
),
value_date: now,
+ status: TxStatus::Completed,
},
&TxOutKind::Talerable(OutgoingSubject(
ShortHashCode::rand(),
url("https://exchange.test"),
)),
- &now,
+ &Timestamp::now(),
)
.await
.unwrap();