commit e66401b06e243ae00d152ac6715efe81ec398d01
parent e43225e2e68bc9b5d32755e77165d1ec2502bbc8
Author: Antoine A <>
Date: Sat, 22 Feb 2025 12:48:03 +0100
magnet-bank: differentiate tx registration and valuation
Diffstat:
8 files changed, 196 insertions(+), 176 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1508,9 +1508,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
+checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
dependencies = [
"adler2",
]
@@ -1895,9 +1895,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.8"
+version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
+checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
dependencies = [
"bitflags",
]
@@ -1996,9 +1996,9 @@ dependencies = [
[[package]]
name = "ring"
-version = "0.17.9"
+version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24"
+checksum = "d34b5020fcdea098ef7d95e9f89ec15952123a4a039badd09fabebe9e963e839"
dependencies = [
"cc",
"cfg-if",
diff --git a/common/taler-common/src/types/timestamp.rs b/common/taler-common/src/types/timestamp.rs
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-use std::fmt::Display;
+use std::{fmt::Display, ops::Add};
use jiff::{civil::Time, tz::TimeZone};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeStruct}; // codespell:ignore
@@ -130,3 +130,14 @@ impl From<jiff::civil::Date> for Timestamp {
.into()
}
}
+
+impl Add<jiff::Span> for Timestamp {
+ type Output = Self;
+
+ fn add(self, rhs: jiff::Span) -> Self::Output {
+ match self {
+ Timestamp::Never => Self::never(),
+ Timestamp::Time(timestamp) => Self::Time(timestamp + rhs),
+ }
+ }
+}
diff --git a/database-versioning/magnet-bank-0001.sql b/database-versioning/magnet-bank-0001.sql
@@ -28,7 +28,8 @@ CREATE TABLE tx_in(
subject TEXT NOT NULL,
debit_account TEXT NOT NULL,
debit_name TEXT NOT NULL,
- created INT8 NOT NULL
+ valued_at INT8 NOT NULL,
+ registered_at INT8 NOT NULL
);
COMMENT ON TABLE tx_in IS 'Incoming transactions';
@@ -39,7 +40,8 @@ CREATE TABLE tx_out(
subject TEXT NOT NULL,
credit_account TEXT NOT NULL,
credit_name TEXT NOT NULL,
- created INT8 NOT NULL
+ valued_at INT8 NOT NULL,
+ registered_at INT8 NOT NULL
);
COMMENT ON TABLE tx_in IS 'Outgoing transactions';
@@ -91,7 +93,7 @@ CREATE TABLE initiated(
last_submitted INT8,
submission_counter INT2 NOT NULL DEFAULT 0,
tx_out_id INT8 UNIQUE REFERENCES tx_out(tx_out_id) ON DELETE CASCADE,
- created INT8 NOT NULL
+ initiated_at INT8 NOT NULL
);
COMMENT ON TABLE tx_in IS 'Initiated outgoing transactions';
diff --git a/database-versioning/magnet-bank-procedures.sql b/database-versioning/magnet-bank-procedures.sql
@@ -44,21 +44,22 @@ CREATE FUNCTION register_tx_in(
IN in_subject TEXT,
IN in_debit_account TEXT,
IN in_debit_name TEXT,
- IN in_timestamp INT8,
+ IN in_valued_at INT8,
IN in_type incoming_type,
IN in_metadata BYTEA,
+ IN in_now INT8,
-- Error status
OUT out_reserve_pub_reuse BOOLEAN,
-- Success return
OUT out_tx_row_id INT8,
- OUT out_timestamp INT8,
+ OUT out_valued_at INT8,
OUT out_new BOOLEAN
)
LANGUAGE plpgsql AS $$
BEGIN
-- Check for idempotence
-SELECT tx_in_id, created
-INTO out_tx_row_id, out_timestamp
+SELECT tx_in_id, valued_at
+INTO out_tx_row_id, out_valued_at
FROM tx_in
WHERE (in_code IS NOT NULL AND magnet_code = in_code) -- Magnet transaction
OR (in_code IS NULL AND amount = in_amount AND debit_account = in_debit_account AND subject = in_subject); -- Admin transaction
@@ -76,23 +77,25 @@ IF out_reserve_pub_reuse THEN
END IF;
-- Insert new incoming transaction
+out_valued_at = in_valued_at;
INSERT INTO tx_in (
magnet_code,
amount,
subject,
debit_account,
debit_name,
- created
+ valued_at,
+ registered_at
) VALUES (
in_code,
in_amount,
in_subject,
in_debit_account,
in_debit_name,
- in_timestamp
+ in_valued_at,
+ in_now
)
-RETURNING tx_in_id, created
-INTO out_tx_row_id, out_timestamp;
+RETURNING tx_in_id INTO out_tx_row_id;
-- Notify new incoming transaction registration
PERFORM pg_notify('tx_in', out_tx_row_id || '');
IF in_type IS NOT NULL THEN
@@ -118,19 +121,18 @@ CREATE FUNCTION register_tx_out(
IN in_subject TEXT,
IN in_credit_account TEXT,
IN in_credit_name TEXT,
- IN in_timestamp INT8,
+ IN in_valued_at INT8,
IN in_wtid BYTEA,
IN in_origin_exchange_url TEXT,
+ IN in_now INT8,
-- Success return
OUT out_tx_row_id INT8,
- OUT out_timestamp INT8,
OUT out_new BOOLEAN
)
LANGUAGE plpgsql AS $$
BEGIN
-- Check for idempotence
-SELECT tx_out_id, created
-INTO out_tx_row_id, out_timestamp
+SELECT tx_out_id INTO out_tx_row_id
FROM tx_out WHERE magnet_code = in_code;
out_new = NOT found;
@@ -142,17 +144,18 @@ IF out_new THEN
subject,
credit_account,
credit_name,
- created
+ valued_at,
+ registered_at
) VALUES (
in_code,
in_amount,
in_subject,
in_credit_account,
in_credit_name,
- in_timestamp
+ in_valued_at,
+ in_now
)
- RETURNING tx_out_id, created
- INTO out_tx_row_id, out_timestamp;
+ RETURNING tx_out_id INTO out_tx_row_id;
-- Notify new outgoing transaction registration
PERFORM pg_notify('tx_out', out_tx_row_id || '');
@@ -182,13 +185,13 @@ CREATE FUNCTION taler_transfer(
IN in_exchange_base_url TEXT,
IN in_credit_account TEXT,
IN in_credit_name TEXT,
- IN in_timestamp INT8,
+ IN in_now INT8,
-- Error return
OUT out_request_uid_reuse BOOLEAN,
OUT out_wtid_reuse BOOLEAN,
-- Success return
- OUT out_tx_row_id INT8,
- OUT out_timestamp INT8
+ OUT out_initiated_row_id INT8,
+ OUT out_initiated_at INT8
)
LANGUAGE plpgsql AS $$
BEGIN
@@ -197,8 +200,8 @@ SELECT (amount != in_amount
OR credit_account != in_credit_account
OR exchange_base_url != in_exchange_base_url
OR wtid != in_wtid)
- ,transfer.initiated_id, created
-INTO out_request_uid_reuse, out_tx_row_id, out_timestamp
+ ,initiated_id, initiated_at
+INTO out_request_uid_reuse, out_initiated_row_id, out_initiated_at
FROM transfer JOIN initiated USING (initiated_id)
WHERE request_uid = in_request_uid;
IF FOUND THEN
@@ -212,20 +215,21 @@ IF out_wtid_reuse THEN
RETURN;
END IF;
-- Insert an initiated outgoing transaction
+out_initiated_at = in_now;
INSERT INTO initiated (
amount,
subject,
credit_account,
credit_name,
- created
+ initiated_at
) VALUES (
in_amount,
in_subject,
in_credit_account,
in_credit_name,
- in_timestamp
-) RETURNING initiated_id, created
-INTO out_tx_row_id, out_timestamp;
+ in_now
+) RETURNING initiated_id
+INTO out_initiated_row_id;
-- Insert a transfer operation
INSERT INTO transfer (
initiated_id,
@@ -233,12 +237,12 @@ INSERT INTO transfer (
wtid,
exchange_base_url
) VALUES (
- out_tx_row_id,
+ out_initiated_row_id,
in_request_uid,
in_wtid,
in_exchange_base_url
);
-PERFORM pg_notify('transfer', out_tx_row_id || '');
+PERFORM pg_notify('transfer', out_initiated_row_id || '');
END $$;
CREATE FUNCTION initiated_status_update(
@@ -274,27 +278,26 @@ CREATE FUNCTION register_bounce_tx_in(
IN in_subject TEXT,
IN in_debit_account TEXT,
IN in_debit_name TEXT,
+ IN in_valued_at INT8,
IN in_bounce_amount taler_amount,
IN in_reason TEXT,
- IN in_timestamp INT8,
+ IN in_now INT8,
-- Success return
OUT out_tx_row_id INT8,
- OUT out_tx_timestamp INT8,
OUT out_tx_new BOOLEAN,
OUT out_bounce_row_id INT8,
- OUT out_bounce_timestamp INT8,
OUT out_bounce_new BOOLEAN
)
LANGUAGE plpgsql AS $$
BEGIN
-- Register incoming transaction idempotently
-SELECT register_tx_in.out_tx_row_id, register_tx_in.out_timestamp, register_tx_in.out_new
-INTO out_tx_row_id, out_tx_timestamp, out_tx_new
-FROM register_tx_in(in_code, in_amount, in_subject, in_debit_account, in_debit_name, in_timestamp, NULL, NULL);
+SELECT register_tx_in.out_tx_row_id, register_tx_in.out_new
+INTO out_tx_row_id, out_tx_new
+FROM register_tx_in(in_code, in_amount, in_subject, in_debit_account, in_debit_name, in_valued_at, NULL, NULL, in_now);
-- Check if already bounce
-SELECT initiated_id, created
- INTO out_bounce_row_id, out_bounce_timestamp
+SELECT initiated_id
+ INTO out_bounce_row_id
FROM bounced JOIN initiated USING (initiated_id)
WHERE tx_in_id = out_tx_row_id;
out_bounce_new=NOT FOUND;
@@ -306,15 +309,15 @@ IF out_bounce_new THEN
subject,
credit_account,
credit_name,
- created
+ initiated_at
) VALUES (
in_amount,
'bounce: ' || in_code,
in_debit_account,
in_debit_name,
- in_timestamp
+ in_now
)
- RETURNING initiated_id, created INTO out_bounce_row_id, out_bounce_timestamp;
+ RETURNING initiated_id INTO out_bounce_row_id;
-- Register the bounce
INSERT INTO bounced (
tx_in_id,
diff --git a/taler-magnet-bank/src/adapter.rs b/taler-magnet-bank/src/adapter.rs
@@ -88,8 +88,8 @@ impl WireGateway for MagnetApi {
let creditor = FullHuPayto::try_from(&req.credit_account)?;
let result = db::make_transfer(&self.pool, &req, &creditor, &Timestamp::now()).await?;
match result {
- db::TransferResult::Success { id, timestamp } => Ok(TransferResponse {
- timestamp,
+ db::TransferResult::Success { id, initiated_at } => Ok(TransferResponse {
+ timestamp: initiated_at,
row_id: SafeU64::try_from(id).unwrap(),
}),
db::TransferResult::RequestUidReuse => Err(failure(
@@ -149,17 +149,17 @@ impl WireGateway for MagnetApi {
amount: req.amount,
subject: format!("Admin incoming {}", req.reserve_pub),
debtor,
- timestamp: Timestamp::now(),
metadata: IncomingSubject::Reserve(req.reserve_pub),
},
+ &Timestamp::now(),
)
.await?;
match res {
AddIncomingResult::Success {
- row_id, timestamp, ..
+ row_id, valued_at, ..
} => Ok(AddIncomingResponse {
row_id: safe_u64(row_id),
- timestamp,
+ timestamp: valued_at,
}),
AddIncomingResult::ReservePubReuse => Err(failure(
ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
@@ -176,17 +176,17 @@ impl WireGateway for MagnetApi {
amount: req.amount,
subject: format!("Admin incoming KYC:{}", req.account_pub),
debtor,
- timestamp: Timestamp::now(),
metadata: IncomingSubject::Kyc(req.account_pub),
},
+ &Timestamp::now(),
)
.await?;
match res {
AddIncomingResult::Success {
- row_id, timestamp, ..
+ row_id, valued_at, ..
} => Ok(AddKycauthResponse {
row_id: safe_u64(row_id),
- timestamp,
+ timestamp: valued_at,
}),
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
@@ -63,7 +63,7 @@ pub struct TxIn {
pub amount: Amount,
pub subject: String,
pub debtor: FullHuPayto,
- pub timestamp: Timestamp,
+ pub value_date: Timestamp,
}
impl Display for TxIn {
@@ -73,9 +73,9 @@ impl Display for TxIn {
amount,
subject,
debtor,
- timestamp,
+ value_date,
} = self;
- write!(f, "{timestamp} {amount} {code} {debtor} '{subject}'")
+ write!(f, "{value_date} {amount} {code} {debtor} '{subject}'")
}
}
@@ -85,7 +85,7 @@ pub struct TxOut {
pub amount: Amount,
pub subject: String,
pub creditor: FullHuPayto,
- pub timestamp: Timestamp,
+ pub value_date: Timestamp,
}
impl Display for TxOut {
@@ -95,9 +95,9 @@ impl Display for TxOut {
amount,
subject,
creditor,
- timestamp,
+ value_date,
} = self;
- write!(f, "{timestamp} {amount} {code} {creditor} '{subject}'")
+ write!(f, "{value_date} {amount} {code} {creditor} '{subject}'")
}
}
@@ -106,7 +106,6 @@ pub struct TxInAdmin {
pub amount: Amount,
pub subject: String,
pub debtor: FullHuPayto,
- pub timestamp: Timestamp,
pub metadata: IncomingSubject,
}
@@ -114,7 +113,6 @@ pub struct TxInAdmin {
pub struct AddOutgoingResult {
pub new: bool,
pub row_id: u64,
- pub timestamp: Timestamp,
}
#[derive(Debug, PartialEq, Eq)]
@@ -122,7 +120,7 @@ pub enum AddIncomingResult {
Success {
new: bool,
row_id: u64,
- timestamp: Timestamp,
+ valued_at: Timestamp,
},
ReservePubReuse,
}
@@ -145,18 +143,22 @@ impl Display for Initiated {
}
}
-pub async fn register_tx_in_admin(db: &PgPool, tx: &TxInAdmin) -> sqlx::Result<AddIncomingResult> {
+pub async fn register_tx_in_admin(
+ db: &PgPool,
+ tx: &TxInAdmin,
+ now: &Timestamp,
+) -> sqlx::Result<AddIncomingResult> {
sqlx::query(
"
- SELECT out_reserve_pub_reuse, out_new, out_tx_row_id, out_timestamp
- FROM register_tx_in(NULL, ($1, $2)::taler_amount, $3, $4, $5, $6, $7, $8)
+ SELECT out_reserve_pub_reuse, out_tx_row_id, out_valued_at, out_new
+ FROM register_tx_in(NULL, ($1, $2)::taler_amount, $3, $4, $5, $6, $7, $8, $6)
",
)
.bind_amount(&tx.amount)
.bind(&tx.subject)
.bind(tx.debtor.iban())
.bind(&tx.debtor.name)
- .bind_timestamp(&tx.timestamp)
+ .bind_timestamp(now)
.bind(tx.metadata.ty())
.bind(tx.metadata.key())
.try_map(|r: PgRow| {
@@ -164,9 +166,9 @@ pub async fn register_tx_in_admin(db: &PgPool, tx: &TxInAdmin) -> sqlx::Result<A
AddIncomingResult::ReservePubReuse
} else {
AddIncomingResult::Success {
- new: r.try_get(1)?,
- row_id: r.try_get_u64(2)?,
- timestamp: r.try_get_timestamp(3)?,
+ row_id: r.try_get_u64(1)?,
+ valued_at: r.try_get_timestamp(2)?,
+ new: r.try_get(3)?,
}
})
})
@@ -178,11 +180,12 @@ pub async fn register_tx_in(
db: &mut PgConnection,
tx: &TxIn,
subject: &Option<IncomingSubject>,
+ now: &Timestamp,
) -> sqlx::Result<AddIncomingResult> {
sqlx::query(
"
- SELECT out_reserve_pub_reuse, out_new, out_tx_row_id, out_timestamp
- FROM register_tx_in($1, ($2, $3)::taler_amount, $4, $5, $6, $7, $8, $9)
+ SELECT out_reserve_pub_reuse, out_tx_row_id, out_valued_at, out_new
+ FROM register_tx_in($1, ($2, $3)::taler_amount, $4, $5, $6, $7, $8, $9, $10)
",
)
.bind(tx.code as i64)
@@ -190,17 +193,18 @@ pub async fn register_tx_in(
.bind(&tx.subject)
.bind(tx.debtor.iban())
.bind(&tx.debtor.name)
- .bind_timestamp(&tx.timestamp)
+ .bind_timestamp(&tx.value_date)
.bind(subject.as_ref().map(|it| it.ty()))
.bind(subject.as_ref().map(|it| it.key()))
+ .bind_timestamp(now)
.try_map(|r: PgRow| {
Ok(if r.try_get(0)? {
AddIncomingResult::ReservePubReuse
} else {
AddIncomingResult::Success {
- new: r.try_get(1)?,
- row_id: r.try_get_u64(2)?,
- timestamp: r.try_get_timestamp(3)?,
+ row_id: r.try_get_u64(1)?,
+ valued_at: r.try_get_timestamp(2)?,
+ new: r.try_get(3)?,
}
})
})
@@ -212,11 +216,12 @@ pub async fn register_tx_out(
db: &mut PgConnection,
tx: &TxOut,
subject: &Option<OutgoingSubject>,
+ now: &Timestamp,
) -> sqlx::Result<AddOutgoingResult> {
sqlx::query(
"
- SELECT out_new, out_tx_row_id, out_timestamp
- FROM register_tx_out($1, ($2, $3)::taler_amount, $4, $5, $6, $7, $8, $9)
+ SELECT out_new, out_tx_row_id
+ FROM register_tx_out($1, ($2, $3)::taler_amount, $4, $5, $6, $7, $8, $9, $10)
",
)
.bind(tx.code as i64)
@@ -224,14 +229,14 @@ pub async fn register_tx_out(
.bind(&tx.subject)
.bind(tx.creditor.iban())
.bind(&tx.creditor.name)
- .bind_timestamp(&tx.timestamp)
+ .bind_timestamp(&tx.value_date)
.bind(subject.as_ref().map(|it| it.0.as_ref()))
.bind(subject.as_ref().map(|it| it.1.as_str()))
+ .bind_timestamp(now)
.try_map(|r: PgRow| {
Ok(AddOutgoingResult {
new: r.try_get(0)?,
row_id: r.try_get_u64(1)?,
- timestamp: r.try_get_timestamp(2)?,
})
})
.fetch_one(db)
@@ -240,7 +245,7 @@ pub async fn register_tx_out(
#[derive(Debug, PartialEq, Eq)]
pub enum TransferResult {
- Success { id: u64, timestamp: Timestamp },
+ Success { id: u64, initiated_at: Timestamp },
RequestUidReuse,
WtidReuse,
}
@@ -249,12 +254,12 @@ pub async fn make_transfer<'a>(
db: impl PgExecutor<'a>,
req: &TransferRequest,
creditor: &FullHuPayto,
- timestamp: &Timestamp,
+ now: &Timestamp,
) -> sqlx::Result<TransferResult> {
let subject = format!("{} {}", req.wtid, req.exchange_base_url);
sqlx::query(
"
- SELECT out_request_uid_reuse, out_wtid_reuse, out_tx_row_id, out_timestamp
+ SELECT out_request_uid_reuse, out_wtid_reuse, out_initiated_row_id, out_initiated_at
FROM taler_transfer($1, $2, $3, ($4, $5)::taler_amount, $6, $7, $8, $9)
",
)
@@ -265,7 +270,7 @@ pub async fn make_transfer<'a>(
.bind(req.exchange_base_url.as_str())
.bind(creditor.iban())
.bind(&creditor.name)
- .bind_timestamp(timestamp)
+ .bind_timestamp(now)
.try_map(|r: PgRow| {
Ok(if r.try_get(0)? {
TransferResult::RequestUidReuse
@@ -274,7 +279,7 @@ pub async fn make_transfer<'a>(
} else {
TransferResult::Success {
id: r.try_get_u64(2)?,
- timestamp: r.try_get_timestamp(3)?,
+ initiated_at: r.try_get_timestamp(3)?,
}
})
})
@@ -285,10 +290,8 @@ pub async fn make_transfer<'a>(
#[derive(Debug, PartialEq, Eq)]
pub struct BounceResult {
pub tx_id: u64,
- pub tx_timestamp: Timestamp,
pub tx_new: bool,
pub bounce_id: u64,
- pub bounce_timestamp: Timestamp,
pub bounce_new: bool,
}
@@ -297,12 +300,12 @@ pub async fn register_bounce_tx_in(
tx: &TxIn,
amount: &Amount,
reason: &str,
- timestamp: &Timestamp,
+ now: &Timestamp,
) -> sqlx::Result<BounceResult> {
sqlx::query(
"
- SELECT out_tx_row_id, out_tx_timestamp, out_tx_new, out_bounce_row_id, out_bounce_timestamp, out_bounce_new
- FROM register_bounce_tx_in($1, ($2, $3)::taler_amount, $4, $5, $6, ($7, $8)::taler_amount, $9, $10)
+ SELECT out_tx_row_id, out_tx_new, out_bounce_row_id, out_bounce_new
+ FROM register_bounce_tx_in($1, ($2, $3)::taler_amount, $4, $5, $6, $7, ($8, $9)::taler_amount, $10, $11)
",
)
.bind(tx.code as i64)
@@ -310,17 +313,16 @@ pub async fn register_bounce_tx_in(
.bind(&tx.subject)
.bind(tx.debtor.iban())
.bind(&tx.debtor.name)
+ .bind_timestamp(&tx.value_date)
.bind_amount(amount)
.bind(reason)
- .bind_timestamp(timestamp)
+ .bind_timestamp(now)
.try_map(|r: PgRow| {
Ok(BounceResult {
tx_id: r.try_get_u64(0)?,
- tx_timestamp: r.try_get_timestamp(1)?,
- tx_new: r.try_get(2)?,
- bounce_id: r.try_get_u64(3)?,
- bounce_timestamp:r.try_get_timestamp(4)?,
- bounce_new: r.try_get(5)?,
+ tx_new: r.try_get(1)?,
+ bounce_id: r.try_get_u64(2)?,
+ bounce_new: r.try_get(3)?,
})
})
.fetch_one(db)
@@ -346,7 +348,7 @@ pub async fn transfer_page<'a>(
(amount).frac as amount_frac,
credit_account,
credit_name,
- created
+ initiated_at
FROM transfer
JOIN initiated USING (initiated_id)
WHERE
@@ -389,7 +391,7 @@ pub async fn outgoing_history(
(amount).frac as amount_frac,
credit_account,
credit_name,
- created,
+ valued_at,
exchange_base_url,
wtid
FROM taler_out
@@ -432,7 +434,7 @@ pub async fn incoming_history(
(amount).frac as amount_frac,
debit_account,
debit_name,
- created,
+ valued_at,
metadata
FROM taler_in
JOIN tx_in USING (tx_in_id)
@@ -480,7 +482,7 @@ pub async fn revenue_history(
"
SELECT
tx_in_id,
- created,
+ valued_at,
(amount).val as amount_val,
(amount).frac as amount_frac,
debit_account,
@@ -520,7 +522,7 @@ pub async fn transfer_by_id<'a>(
wtid,
credit_account,
credit_name,
- created
+ initiated_at
FROM transfer
JOIN initiated USING (initiated_id)
WHERE initiated_id = $1
@@ -612,6 +614,7 @@ pub async fn initiated_submit_failure<'a>(
#[cfg(test)]
mod test {
+ use jiff::Span;
use sqlx::{PgConnection, PgPool, postgres::PgRow};
use taler_api::{
db::TypeHelper,
@@ -662,6 +665,8 @@ mod test {
.fetch_one(&mut *db)
.await
.unwrap();
+ let now = Timestamp::now_stable();
+ let later = now + Span::new().hours(4);
let tx = TxIn {
code: code,
amount: amount("EUR:10"),
@@ -669,17 +674,17 @@ mod test {
debtor: magnet_payto(
"payto://iban/HU30162000031000163100000000?receiver-name=name",
),
- timestamp: Timestamp::now_stable(),
+ value_date: now,
};
// Insert
assert_eq!(
- register_tx_in(db, &tx, &first)
+ register_tx_in(db, &tx, &first, &now)
.await
.expect("register tx in"),
AddIncomingResult::Success {
new: true,
row_id: id,
- timestamp: tx.timestamp
+ valued_at: now
}
);
// Idempotent
@@ -687,17 +692,18 @@ mod test {
register_tx_in(
db,
&TxIn {
- timestamp: Timestamp::now(),
+ value_date: later,
..tx.clone()
},
- &first
+ &first,
+ &now
)
.await
.expect("register tx in"),
AddIncomingResult::Success {
new: false,
row_id: id,
- timestamp: tx.timestamp
+ valued_at: now
}
);
// Many
@@ -706,16 +712,18 @@ mod test {
db,
&TxIn {
code: code + 1,
+ value_date: later,
..tx
},
- &second
+ &second,
+ &now
)
.await
.expect("register tx in"),
AddIncomingResult::Success {
new: true,
row_id: id + 1,
- timestamp: tx.timestamp
+ valued_at: later
}
);
}
@@ -782,39 +790,34 @@ mod test {
Vec::new()
);
+ let now = Timestamp::now_stable();
+ let later = now + Span::new().hours(3);
let tx = TxInAdmin {
amount: amount("EUR:10"),
subject: "subject".to_owned(),
debtor: magnet_payto("payto://iban/HU30162000031000163100000000?receiver-name=name"),
- timestamp: Timestamp::now_stable(),
metadata: IncomingSubject::Reserve(EddsaPublicKey::rand()),
};
// Insert
assert_eq!(
- register_tx_in_admin(&pool, &tx)
+ register_tx_in_admin(&pool, &tx, &now)
.await
.expect("register tx in"),
AddIncomingResult::Success {
new: true,
row_id: 1,
- timestamp: tx.timestamp
+ valued_at: now
}
);
// Idempotent
assert_eq!(
- register_tx_in_admin(
- &pool,
- &TxInAdmin {
- timestamp: Timestamp::now(),
- ..tx.clone()
- }
- )
- .await
- .expect("register tx in"),
+ register_tx_in_admin(&pool, &tx, &later)
+ .await
+ .expect("register tx in"),
AddIncomingResult::Success {
new: false,
row_id: 1,
- timestamp: tx.timestamp
+ valued_at: now
}
);
// Many
@@ -825,14 +828,15 @@ mod test {
subject: "Other".to_owned(),
metadata: IncomingSubject::Reserve(EddsaPublicKey::rand()),
..tx.clone()
- }
+ },
+ &later
)
.await
.expect("register tx in"),
AddIncomingResult::Success {
new: true,
row_id: 2,
- timestamp: tx.timestamp
+ valued_at: later
}
);
@@ -861,6 +865,8 @@ mod test {
.fetch_one(&mut *db)
.await
.unwrap();
+ let now = Timestamp::now_stable();
+ let later = now + Span::new().hours(4);
let tx = TxOut {
code,
amount: amount("EUR:10"),
@@ -868,17 +874,16 @@ mod test {
creditor: magnet_payto(
"payto://iban/HU30162000031000163100000000?receiver-name=name",
),
- timestamp: Timestamp::now_stable(),
+ value_date: now,
};
// Insert
assert_eq!(
- register_tx_out(db, &tx, &first)
+ register_tx_out(db, &tx, &first, &now)
.await
.expect("register tx out"),
AddOutgoingResult {
new: true,
row_id: id,
- timestamp: tx.timestamp
}
);
// Idempotent
@@ -886,17 +891,17 @@ mod test {
register_tx_out(
db,
&TxOut {
- timestamp: Timestamp::now(),
+ value_date: later,
..tx.clone()
},
- &first
+ &first,
+ &now
)
.await
.expect("register tx out"),
AddOutgoingResult {
new: false,
row_id: id,
- timestamp: tx.timestamp
}
);
// Many
@@ -905,16 +910,17 @@ mod test {
db,
&TxOut {
code: code + 1,
+ value_date: later,
..tx.clone()
},
- &second
+ &second,
+ &now
)
.await
.expect("register tx out"),
AddOutgoingResult {
new: true,
row_id: id + 1,
- timestamp: tx.timestamp
}
);
}
@@ -975,25 +981,26 @@ mod test {
credit_account: payto("payto://iban/HU02162000031000164800000000?receiver-name=name"),
};
let payto = magnet_payto("payto://iban/HU30162000031000163100000000?receiver-name=name");
- let timestamp = Timestamp::now_stable();
+ let now = Timestamp::now_stable();
+ let later = now + Span::new().hours(2);
// Insert
assert_eq!(
- make_transfer(&mut db, &req, &payto, ×tamp)
+ make_transfer(&mut db, &req, &payto, &now)
.await
.expect("transfer"),
TransferResult::Success {
id: 1,
- timestamp: timestamp
+ initiated_at: now
}
);
// Idempotent
assert_eq!(
- make_transfer(&mut db, &req, &payto, &Timestamp::now())
+ make_transfer(&mut db, &req, &payto, &later)
.await
.expect("transfer"),
TransferResult::Success {
id: 1,
- timestamp: timestamp
+ initiated_at: now
}
);
// Request UID reuse
@@ -1005,7 +1012,7 @@ mod test {
..req.clone()
},
&payto,
- &Timestamp::now()
+ &now
)
.await
.expect("transfer"),
@@ -1020,7 +1027,7 @@ mod test {
..req.clone()
},
&payto,
- &Timestamp::now()
+ &now
)
.await
.expect("transfer"),
@@ -1036,13 +1043,13 @@ mod test {
..req
},
&payto,
- ×tamp
+ &later
)
.await
.expect("transfer"),
TransferResult::Success {
id: 2,
- timestamp: timestamp
+ initiated_at: later
}
);
@@ -1065,15 +1072,10 @@ mod test {
let amount = amount("HUF:10");
let payto = magnet_payto("payto://iban/HU30162000031000163100000000?receiver-name=name");
- let timestamp = Timestamp::now_stable();
+ let now = Timestamp::now_stable();
// Empty db
- assert!(
- db::pending_batch(&mut db, ×tamp)
- .await
- .unwrap()
- .is_empty()
- );
+ assert!(db::pending_batch(&mut db, &now).await.unwrap().is_empty());
// Insert
assert_eq!(
@@ -1084,16 +1086,17 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- timestamp
+ value_date: now
},
- &None
+ &None,
+ &now
)
.await
.expect("register tx in"),
AddIncomingResult::Success {
new: true,
row_id: 1,
- timestamp: timestamp
+ valued_at: now
}
);
@@ -1106,20 +1109,18 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- timestamp
+ value_date: now
},
&amount,
"good reason",
- ×tamp
+ &now
)
.await
.expect("bounce"),
BounceResult {
tx_id: 2,
- tx_timestamp: timestamp,
tx_new: true,
bounce_id: 1,
- bounce_timestamp: timestamp,
bounce_new: true
}
);
@@ -1132,20 +1133,18 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- timestamp
+ value_date: now
},
&amount,
"good reason",
- ×tamp
+ &now
)
.await
.expect("bounce"),
BounceResult {
tx_id: 2,
- tx_timestamp: timestamp,
tx_new: false,
bounce_id: 1,
- bounce_timestamp: timestamp,
bounce_new: false
}
);
@@ -1159,20 +1158,18 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- timestamp
+ value_date: now
},
&amount,
"good reason",
- ×tamp
+ &now
)
.await
.expect("bounce"),
BounceResult {
tx_id: 1,
- tx_timestamp: timestamp,
tx_new: false,
bounce_id: 2,
- bounce_timestamp: timestamp,
bounce_new: true
}
);
@@ -1185,27 +1182,25 @@ mod test {
amount: amount.clone(),
subject: "subject".to_owned(),
debtor: payto.clone(),
- timestamp
+ value_date: now
},
&amount,
"good reason",
- ×tamp
+ &now
)
.await
.expect("bounce"),
BounceResult {
tx_id: 1,
- tx_timestamp: timestamp,
tx_new: false,
bounce_id: 2,
- bounce_timestamp: timestamp,
bounce_new: false
}
);
// Batch
assert_eq!(
- db::pending_batch(&mut db, ×tamp).await.unwrap(),
+ db::pending_batch(&mut db, &now).await.unwrap(),
&[
Initiated {
id: 1,
diff --git a/taler-magnet-bank/src/worker.rs b/taler-magnet-bank/src/worker.rs
@@ -90,8 +90,13 @@ impl Worker<'_> {
match parse_incoming_unstructured(&tx_in.subject) {
Ok(None) => bounce(self.db, "missing public key").await?,
Ok(Some(subject)) => {
- let res =
- db::register_tx_in(self.db, &tx_in, &Some(subject)).await?;
+ let res = db::register_tx_in(
+ self.db,
+ &tx_in,
+ &Some(subject),
+ &Timestamp::now(),
+ )
+ .await?;
match res {
AddIncomingResult::Success { new, .. } => {
if new {
@@ -110,7 +115,9 @@ impl Worker<'_> {
}
Tx::Out(tx_out) => {
let subject = subject::parse_outgoing(&tx_out.subject);
- let res = db::register_tx_out(self.db, &tx_out, &subject.ok()).await?;
+ let res =
+ db::register_tx_out(self.db, &tx_out, &subject.ok(), &Timestamp::now())
+ .await?;
// TODO log recovered & log malformed
if res.new {
info!("outgoing {tx_out}");
@@ -232,7 +239,7 @@ pub fn extract_tx_info(tx: Transaction) -> Tx {
amount,
subject: tx.subject,
debtor: counter_account,
- timestamp: Timestamp::from(tx.value_date),
+ value_date: Timestamp::from(tx.value_date),
})
} else {
Tx::Out(TxOut {
@@ -240,7 +247,7 @@ pub fn extract_tx_info(tx: Transaction) -> Tx {
amount,
subject: tx.subject,
creditor: counter_account,
- timestamp: Timestamp::from(tx.value_date),
+ value_date: Timestamp::from(tx.value_date),
})
}
}
diff --git a/taler-magnet-bank/tests/api.rs b/taler-magnet-bank/tests/api.rs
@@ -75,6 +75,7 @@ async fn outgoing_history() {
let acquire = pool.acquire();
async move {
let mut conn = acquire.await.unwrap();
+ let now = Timestamp::now();
db::register_tx_out(
&mut *conn,
&db::TxOut {
@@ -84,12 +85,13 @@ async fn outgoing_history() {
creditor: magnet_payto(
"payto://iban/HU30162000031000163100000000?receiver-name=name",
),
- timestamp: Timestamp::now_stable(),
+ value_date: now,
},
&Some(OutgoingSubject(
ShortHashCode::rand(),
url("https://exchange.test"),
)),
+ &now,
)
.await
.unwrap();