commit 5ede102532d7995f8e953ab18c7941ef69348780
parent a90ee04123141b0544288e1dba3506829fc102a7
Author: Antoine A <>
Date: Fri, 4 Sep 2026 17:58:05 +0200
bank: more fixes
Diffstat:
12 files changed, 41 insertions(+), 45 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1349,7 +1349,7 @@ dependencies = [
[[package]]
name = "http-client"
version = "1.5.0"
-source = "git+git://git.taler.net/taler-rust.git/#14f571a7e85caf215a7e9995914026f064f488fe"
+source = "git+git://git.taler.net/taler-rust.git/#f56f65eed7b2e7d83602ddafd78f707b9e137b68"
dependencies = [
"compact_str",
"futures-util",
@@ -3451,7 +3451,7 @@ dependencies = [
[[package]]
name = "taler-api"
version = "1.5.0"
-source = "git+git://git.taler.net/taler-rust.git/#14f571a7e85caf215a7e9995914026f064f488fe"
+source = "git+git://git.taler.net/taler-rust.git/#f56f65eed7b2e7d83602ddafd78f707b9e137b68"
dependencies = [
"aws-lc-rs",
"axum",
@@ -3479,12 +3479,12 @@ dependencies = [
[[package]]
name = "taler-build"
version = "1.5.0"
-source = "git+git://git.taler.net/taler-rust.git/#14f571a7e85caf215a7e9995914026f064f488fe"
+source = "git+git://git.taler.net/taler-rust.git/#f56f65eed7b2e7d83602ddafd78f707b9e137b68"
[[package]]
name = "taler-common"
version = "1.5.0"
-source = "git+git://git.taler.net/taler-rust.git/#14f571a7e85caf215a7e9995914026f064f488fe"
+source = "git+git://git.taler.net/taler-rust.git/#f56f65eed7b2e7d83602ddafd78f707b9e137b68"
dependencies = [
"anyhow",
"aws-lc-rs",
@@ -3514,7 +3514,7 @@ dependencies = [
[[package]]
name = "taler-macros"
version = "1.5.0"
-source = "git+git://git.taler.net/taler-rust.git/#14f571a7e85caf215a7e9995914026f064f488fe"
+source = "git+git://git.taler.net/taler-rust.git/#f56f65eed7b2e7d83602ddafd78f707b9e137b68"
dependencies = [
"proc-macro2",
"quote",
@@ -3524,7 +3524,7 @@ dependencies = [
[[package]]
name = "taler-test-utils"
version = "1.5.0"
-source = "git+git://git.taler.net/taler-rust.git/#14f571a7e85caf215a7e9995914026f064f488fe"
+source = "git+git://git.taler.net/taler-rust.git/#f56f65eed7b2e7d83602ddafd78f707b9e137b68"
dependencies = [
"aws-lc-rs",
"axum",
diff --git a/libeufin-bank/src/api.rs b/libeufin-bank/src/api.rs
@@ -81,7 +81,7 @@ pub struct BankState {
pub taler_out_channel: NotificationChannel<u64, i64>,
pub taler_in_channel: NotificationChannel<u64, i64>,
pub revenue_channel: NotificationChannel<u64, i64>,
- pub withdrawal_channel: NotificationChannel<Uuid, WithdrawalStatus>,
+ pub withdrawal_channel: NotificationChannel<Uuid, Option<WithdrawalStatus>>,
}
impl BankState {
diff --git a/libeufin-bank/src/api/cashout.rs b/libeufin-bank/src/api/cashout.rs
@@ -41,7 +41,7 @@ use taler_common::{
use crate::{
api::BankState,
- auth::{AdminRAuth, UserRWAuth},
+ auth::{AdminRAuth, UserRAuth},
db::cashout::{CreationResult, create, get_for_user, page_all, page_for_user},
mfa::{CashoutOp, MfaReq},
};
@@ -170,7 +170,7 @@ pub fn cashout_api(state: Arc<BankState>) -> Router<Arc<BankState>> {
)
.get(
async |Query(params): Query<PageParams>,
- auth: UserRWAuth,
+ auth: UserRAuth,
State(state): State<Arc<BankState>>| {
let params = params.check()?;
@@ -187,7 +187,7 @@ pub fn cashout_api(state: Arc<BankState>) -> Router<Arc<BankState>> {
"/accounts/{username}/cashouts/{id}",
get(
async |Path((_, id)): Path<((), u64)>,
- auth: UserRWAuth,
+ auth: UserRAuth,
State(state): State<Arc<BankState>>| {
match get_for_user(
&state.db,
diff --git a/libeufin-bank/src/api/conversion.rs b/libeufin-bank/src/api/conversion.rs
@@ -705,7 +705,7 @@ pub mod test {
.await
.assert_ok_json();
assert_eq!(res.amount_debit, amount("KUDOS:1"));
- assert_eq!(res.amount_credit, amount("EUR:1.25"));
+ assert_eq!(res.amount_credit, amount("EUR:1.24"));
// Check conversion from
let res: ConversionResponse = ctx
diff --git a/libeufin-bank/src/api/tx.rs b/libeufin-bank/src/api/tx.rs
@@ -128,7 +128,7 @@ pub fn tx_api() -> Router<Arc<BankState>> {
Err(failure_code(ErrorCode::BANK_ADMIN_CREDITOR))
}
TxResult::UnknownDebtor => {
- Err(failure_code(ErrorCode::BANK_UNKNOWN_DEBTOR))
+ Err(failure_code(ErrorCode::BANK_UNKNOWN_ACCOUNT))
}
TxResult::BothPartySame => Err(failure_code(ErrorCode::BANK_SAME_ACCOUNT)),
TxResult::BalanceInsufficient => Err(failure(
diff --git a/libeufin-bank/src/api/withdrawal.rs b/libeufin-bank/src/api/withdrawal.rs
@@ -20,10 +20,7 @@
use std::sync::Arc;
use axum::{
- Json, Router,
- extract::State,
- response::{IntoResponse, NoContent},
- routing::{get, post},
+ Json, Router, extract::State, http::StatusCode, response::{IntoResponse, NoContent}, routing::{get, post},
};
use compact_str::CompactString;
use jiff::Timestamp;
@@ -56,11 +53,10 @@ use crate::{
payto::{BankPayto, FullBankPayto},
};
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "TEXT")]
#[allow(non_camel_case_types)]
pub enum WithdrawalStatus {
- #[default]
pending,
aborted,
selected,
@@ -383,7 +379,7 @@ pub fn withdrawal_api() -> Router<Arc<BankState>> {
Err(failure_code(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT))
}
SelectionResult::UnknownAccount => {
- Err(failure_code(ErrorCode::BANK_UNKNOWN_ACCOUNT))
+ Err(failure_code(ErrorCode::BANK_UNKNOWN_ACCOUNT).with_status(StatusCode::CONFLICT))
}
SelectionResult::AccountIsNotExchange => {
Err(failure_code(ErrorCode::BANK_ACCOUNT_IS_NOT_EXCHANGE))
@@ -462,7 +458,7 @@ mod test {
use std::time::Duration;
- use axum::http::Method;
+ use axum::http::{Method, StatusCode};
use serde::de::DeserializeOwned;
use taler_common::{
api::EddsaPublicKey,
@@ -776,7 +772,7 @@ mod test {
"selected_exchange": ctx.unknown_payto
}))
.await
- .assert_error(ErrorCode::BANK_UNKNOWN_ACCOUNT);
+ .assert_error_status(ErrorCode::BANK_UNKNOWN_ACCOUNT, StatusCode::CONFLICT);
// Not exchange
ctx.post(format!("/taler-integration/withdrawal-operation/{uuid}"))
.json(json!({
diff --git a/libeufin-bank/src/db.rs b/libeufin-bank/src/db.rs
@@ -108,7 +108,7 @@ pub async fn notification_listener(
taler_out_channel: NotificationChannel<u64, i64>,
taler_in_channel: NotificationChannel<u64, i64>,
revenue_channel: NotificationChannel<u64, i64>,
- withdrawal_channel: NotificationChannel<Uuid, WithdrawalStatus>,
+ withdrawal_channel: NotificationChannel<Uuid, Option<WithdrawalStatus>>,
) -> sqlx::Result<()> {
join!(
async {
@@ -138,7 +138,7 @@ pub async fn notification_listener(
taler_in_channel.dispatch(&account, row);
},
"bank_withdrawal_status" => (uuid: Uuid, status: WithdrawalStatus) {
- withdrawal_channel.dispatch(&uuid, status);
+ withdrawal_channel.dispatch(&uuid, Some(status));
}
)
}
@@ -153,7 +153,7 @@ pub async fn monitor(
params: &MonitorParams,
) -> sqlx::Result<MonitorResponse> {
let timestamp = if let Some(s) = params.date_s {
- Timestamp::from_second(s as i64).unwrap()
+ Timestamp::from_second(s as i64).unwrap_or(Timestamp::MAX)
} else {
Timestamp::now()
}
@@ -759,16 +759,16 @@ mod test {
monitor_taler_in(6, "KUDOS:78.84").await;
ctx.cashout("3").await;
- monitor_cashout(1, "KUDOS:3", "EUR:3.78").await;
+ monitor_cashout(1, "KUDOS:3", "EUR:3.77").await;
ctx.cashout("7.6").await;
- monitor_cashout(2, "KUDOS:10.6", "EUR:13.35").await;
+ monitor_cashout(2, "KUDOS:10.6", "EUR:13.34").await;
ctx.cashout("12.3").await;
- monitor_cashout(3, "KUDOS:22.9", "EUR:28.85").await;
+ monitor_cashout(3, "KUDOS:22.9", "EUR:28.83").await;
monitor_taler_in(6, "KUDOS:78.84").await;
monitor_taler_out(3, "KUDOS:82.5").await;
monitor_cashin(3, "KUDOS:55.94", "EUR:70").await;
- monitor_cashout(3, "KUDOS:22.9", "EUR:28.85").await;
+ monitor_cashout(3, "KUDOS:22.9", "EUR:28.83").await;
}
#[tokio::test]
diff --git a/libeufin-bank/src/db/conversion.rs b/libeufin-bank/src/db/conversion.rs
@@ -94,7 +94,7 @@ pub async fn update_config(db: &PgPool, cfg: &ConversionRate) -> sqlx::Result<()
.bind(cfg.cashout_fee)
.bind(cfg.cashout_tiny_amount)
.bind(cfg.cashout_min_amount)
- .bind(cfg.cashin_rounding_mode)
+ .bind(cfg.cashout_rounding_mode)
.execute(db)
)?;
Ok(())
diff --git a/libeufin-bank/src/db/gc.rs b/libeufin-bank/src/db/gc.rs
@@ -26,9 +26,9 @@ use taler_api::db::BindHelper;
/** Run garbage collection */
pub async fn collect(
db: &PgPool,
- abort_after: &Timestamp,
- clean_after: &Timestamp,
- delete_after: &Timestamp,
+ abort_before: &Timestamp,
+ clean_before: &Timestamp,
+ delete_before: &Timestamp,
) -> sqlx::Result<()> {
// Abort pending operations
sqlx::query(
@@ -37,7 +37,7 @@ pub async fn collect(
SELECT FROM prepared_transfers WHERE prepared_transfers.withdrawal_id=taler_withdrawal_operations.withdrawal_id
)
"
- ).bind_timestamp(abort_after).execute(db).await?;
+ ).bind_timestamp(abort_before).execute(db).await?;
// Clean aborted operations, expired challenges and expired tokens
for stm in [
@@ -47,12 +47,12 @@ pub async fn collect(
"DELETE FROM tan_challenges WHERE expiration_date < $1",
"DELETE FROM bearer_tokens WHERE expiration_time < $1"
] {
- sqlx::query(stm).bind_timestamp(clean_after).execute(db).await?;
+ sqlx::query(stm).bind_timestamp(clean_before).execute(db).await?;
}
// Delete old bank transactions, linked operations are deleted by CASCADE
sqlx::query("DELETE FROM bank_account_transactions WHERE transaction_date < $1")
- .bind_timestamp(delete_after)
+ .bind_timestamp(delete_before)
.execute(db)
.await?;
diff --git a/libeufin-bank/src/db/withdrawal.rs b/libeufin-bank/src/db/withdrawal.rs
@@ -311,14 +311,14 @@ pub async fn poll_info(
db: &PgPool,
ctx: &PaytoCtx,
currency: &Currency,
- channel: &NotificationChannel<Uuid, WithdrawalStatus>,
+ channel: &NotificationChannel<Uuid, Option<WithdrawalStatus>>,
params: &Status,
uuid: Uuid,
) -> sqlx::Result<Option<WithdrawalPublicInfo>> {
pooling(
¶ms.polling,
|| channel.subscribe(uuid),
- |init| *init != params.status,
+ |it| it.is_some_and(|v| v != params.status),
async ||
serialized!(
sqlx::query(
@@ -369,7 +369,7 @@ pub async fn poll_status(
db: &PgPool,
ctx: &PaytoCtx,
currency: &Currency,
- channel: &NotificationChannel<Uuid, WithdrawalStatus>,
+ channel: &NotificationChannel<Uuid, Option<WithdrawalStatus>>,
params: &Status,
uuid: Uuid,
wire: WireMethod,
@@ -378,7 +378,7 @@ pub async fn poll_status(
pooling(
¶ms.polling,
|| channel.subscribe(uuid),
- |init| *init != params.status,
+ |it| it.is_some_and(|v| v != params.status),
async ||
serialized!(
sqlx::query(
diff --git a/libeufin-bank/src/lib.rs b/libeufin-bank/src/lib.rs
@@ -484,9 +484,9 @@ pub async fn run(cfg: &Config, cmd: Cmd) -> anyhow::Result<()> {
let now = Zoned::now();
collect(
&db,
- &(now.clone() + cfg.gc_abort_after).timestamp(),
- &(now.clone() + cfg.gc_clean_after).timestamp(),
- &(now + cfg.gc_delete_after).timestamp(),
+ &(now.clone() - cfg.gc_abort_after).timestamp(),
+ &(now.clone() - cfg.gc_clean_after).timestamp(),
+ &(now - cfg.gc_delete_after).timestamp(),
)
.await?;
}
diff --git a/libeufin-nexus/src/db/exchange.rs b/libeufin-nexus/src/db/exchange.rs
@@ -72,7 +72,7 @@ pub async fn outgoing_history(
amount: r.try_get_amount("amount", currency)?,
debit_fee: r
.try_get_opt_amount("debit_fee", currency)?
- .filter(|it| it.is_zero()),
+ .filter(|it| !it.is_zero()),
credit_account: r.try_get_payto("credit_payto")?,
date: r.try_get_timestamp("execution_time")?.into(),
exchange_base_url: r.try_get_url("exchange_base_url")?,
@@ -118,7 +118,7 @@ pub async fn incoming_history(
|r: PgRow| {
let credit_fee = r
.try_get_opt_amount("credit_fee", currency)?
- .filter(|it| it.is_zero());
+ .filter(|it| !it.is_zero());
Ok(match r.try_get_parse("type")? {
IncomingType::reserve => IncomingBankTransaction::Reserve {
row_id: r.try_get_u64("incoming_transaction_id")?,
@@ -179,7 +179,7 @@ pub async fn revenue_history(
amount: r.try_get_amount("amount", currency)?,
credit_fee: r
.try_get_opt_amount("credit_fee", currency)?
- .filter(|it| it.is_zero()),
+ .filter(|it| !it.is_zero()),
debit_account: r.try_get_payto("debit_payto")?,
date: r.try_get_timestamp("execution_time")?.into(),
subject: r.try_get("subject")?,