depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit 5a4e56976d3fb7a88819aa7b9e290c0c8f3aaff9
parent 2ce7dab90e029a1c01d13e0730b220fd2bf5c7a3
Author: Antoine A <>
Date:   Thu,  1 Oct 2026 14:21:02 +0200

common: sync taler-rust

Diffstat:
MCargo.lock | 22++++++++++++----------
MCargo.toml | 1+
Mdepolymerizer-bitcoin/Cargo.toml | 1+
Mdepolymerizer-bitcoin/src/api.rs | 52++++++++++++++++++++++++----------------------------
Mdepolymerizer-bitcoin/src/bin/btc-harness.rs | 3++-
Adepolymerizer-bitcoin/src/constants.rs | 19+++++++++++++++++++
Mdepolymerizer-bitcoin/src/db.rs | 105+++++++++++++++++++++++++++++++++++--------------------------------------------
Mdepolymerizer-bitcoin/src/lib.rs | 5++---
Mdepolymerizer-bitcoin/src/main.rs | 2+-
9 files changed, 108 insertions(+), 102 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -809,6 +809,7 @@ dependencies = [ "taler-api", "taler-build", "taler-common", + "taler-macros", "taler-test-utils", "thiserror", "tokio", @@ -993,7 +994,7 @@ dependencies = [ [[package]] name = "failure-injection" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#3f6a0fd79c5c9f294536810a2edb2f5419acf326" +source = "git+git://git.taler.net/taler-rust.git/#7bfaf2c5e3f7ee89c29cc9175da77ee7cb582b18" dependencies = [ "thiserror", ] @@ -1595,9 +1596,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb" dependencies = [ "spin", ] @@ -2921,7 +2922,7 @@ dependencies = [ [[package]] name = "taler-api" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#3f6a0fd79c5c9f294536810a2edb2f5419acf326" +source = "git+git://git.taler.net/taler-rust.git/#7bfaf2c5e3f7ee89c29cc9175da77ee7cb582b18" dependencies = [ "aws-lc-rs", "axum", @@ -2939,6 +2940,7 @@ dependencies = [ "serde_urlencoded", "sqlx", "taler-common", + "taler-macros", "thiserror", "tokio", "tower-http", @@ -2950,12 +2952,12 @@ dependencies = [ [[package]] name = "taler-build" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#3f6a0fd79c5c9f294536810a2edb2f5419acf326" +source = "git+git://git.taler.net/taler-rust.git/#7bfaf2c5e3f7ee89c29cc9175da77ee7cb582b18" [[package]] name = "taler-common" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#3f6a0fd79c5c9f294536810a2edb2f5419acf326" +source = "git+git://git.taler.net/taler-rust.git/#7bfaf2c5e3f7ee89c29cc9175da77ee7cb582b18" dependencies = [ "anyhow", "aws-lc-rs", @@ -2985,7 +2987,7 @@ dependencies = [ [[package]] name = "taler-macros" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#3f6a0fd79c5c9f294536810a2edb2f5419acf326" +source = "git+git://git.taler.net/taler-rust.git/#7bfaf2c5e3f7ee89c29cc9175da77ee7cb582b18" dependencies = [ "proc-macro2", "quote", @@ -2995,7 +2997,7 @@ dependencies = [ [[package]] name = "taler-test-utils" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#3f6a0fd79c5c9f294536810a2edb2f5419acf326" +source = "git+git://git.taler.net/taler-rust.git/#7bfaf2c5e3f7ee89c29cc9175da77ee7cb582b18" dependencies = [ "aws-lc-rs", "axum", @@ -3793,9 +3795,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml @@ -29,6 +29,7 @@ url = { version = "2.2", features = ["serde"] } taler-common = { git = "git://git.taler.net/taler-rust.git/" } taler-api = { git = "git://git.taler.net/taler-rust.git/" } taler-build = { git = "git://git.taler.net/taler-rust.git/" } +taler-macros = { git = "git://git.taler.net/taler-rust.git/" } taler-test-utils = { git = "git://git.taler.net/taler-rust.git/" } failure-injection = { git = "git://git.taler.net/taler-rust.git/" } depolymerizer-common = { path = "depolymerizer-common" } diff --git a/depolymerizer-bitcoin/Cargo.toml b/depolymerizer-bitcoin/Cargo.toml @@ -22,6 +22,7 @@ anyhow.workspace = true taler-api.workspace = true taler-build.workspace = true taler-common.workspace = true +taler-macros.workspace = true failure-injection.workspace = true sqlx.workspace = true tokio.workspace = true diff --git a/depolymerizer-bitcoin/src/api.rs b/depolymerizer-bitcoin/src/api.rs @@ -415,8 +415,8 @@ pub mod test { db::IncomingType, types::amount::{Currency, amount}, }; + use taler_macros::db_test; use taler_test_utils::{ - db::db_test_setup, routine::{ Status, admin_add_incoming_routine, out_history_routine, registration_routine, revenue_routine, transfer_routine, @@ -426,7 +426,6 @@ pub mod test { }; use crate::{ - CONFIG_SOURCE, api::ServerState, db::{TxOut, TxOutKind, sync_out, test::rand_tx_id}, payto::FullBtcPayto, @@ -453,27 +452,24 @@ pub mod test { .unwrap() }); - async fn setup() -> (Router, PgPool) { - let (_, pool) = db_test_setup(CONFIG_SOURCE).await; + async fn setup(db: &PgPool) -> Router { let api = ServerState::start( - pool.clone(), + db.clone(), EXCHANGE.clone(), Currency::from_str("BTC").unwrap(), ) .await; - let server = Router::new() + Router::new() .wire_gateway(api.clone(), AuthMethod::None) .prepared_transfer(api.clone()) .revenue(api.clone(), AuthMethod::None) .observability(api, AuthMethod::None) - .finalize(); - - (server, pool) + .finalize() } - #[tokio::test] - async fn config() { - let (server, _) = setup().await; + #[db_test] + async fn config(db: PgPool) { + let server = setup(&db).await; server .get("/taler-wire-gateway/config") .await @@ -493,9 +489,9 @@ pub mod test { server.get("/taler-observability/metrics").await.assert_ok(); } - #[tokio::test] - async fn transfer() { - let (server, _) = setup().await; + #[db_test] + async fn transfer(db: PgPool) { + let server = setup(&db).await; transfer_routine( &server.prefix("/taler-wire-gateway"), TransferState::pending, @@ -504,9 +500,9 @@ pub mod test { .await; } - #[tokio::test] - async fn outgoing_history() { - let (server, db) = setup().await; + #[db_test] + async fn outgoing_history(db: PgPool) { + let server = setup(&db).await; out_history_routine( &server.prefix("/taler-wire-gateway"), tasks!({ @@ -535,9 +531,9 @@ pub mod test { .await; } - #[tokio::test] - async fn admin_add_incoming() { - let (server, _) = setup().await; + #[db_test] + async fn admin_add_incoming(db: PgPool) { + let server = setup(&db).await; admin_add_incoming_routine( &server.prefix("/taler-wire-gateway"), &server.prefix("/taler-prepared-transfer"), @@ -547,9 +543,9 @@ pub mod test { .await; } - #[tokio::test] - async fn revenue() { - let (server, _) = &setup().await; + #[db_test] + async fn revenue(db: PgPool) { + let server = setup(&db).await; revenue_routine( &server.prefix("/taler-wire-gateway"), &server.prefix("/taler-revenue"), @@ -592,16 +588,16 @@ pub mod test { .unwrap() } - #[tokio::test] - async fn registration() { - let (server, pool) = setup().await; + #[db_test] + async fn registration(db: PgPool) { + let server = setup(&db).await; registration_routine( &server.prefix("/taler-wire-gateway"), &server.prefix("/taler-prepared-transfer"), &CLIENT.as_uri(), &EXCHANGE.as_uri(), &UNKNOWN.as_uri(), - || check_in(&pool), + || check_in(&db), ) .await; } diff --git a/depolymerizer-bitcoin/src/bin/btc-harness.rs b/depolymerizer-bitcoin/src/bin/btc-harness.rs @@ -26,8 +26,9 @@ use std::{ use bitcoin::{Address, Amount, Txid, address::NetworkUnchecked, hashes::Hash}; use clap::Parser as _; use depolymerizer_bitcoin::{ - CONFIG_SOURCE, RpcApiExtended as _, + RpcApiExtended as _, config::{RpcAuth, RpcCfg, WorkerCfg}, + constants::CONFIG_SOURCE, db::{self, dbinit, incoming_history, pool}, loops::{LoopError, LoopResult, analysis::analysis, worker::worker_step}, payto::{BtcWallet, FullBtcPayto}, diff --git a/depolymerizer-bitcoin/src/constants.rs b/depolymerizer-bitcoin/src/constants.rs @@ -0,0 +1,19 @@ +/* + This file is part of TALER + Copyright (C) 2022-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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ + +use taler_common::config::parser::ConfigSource; + +pub const CONFIG_SOURCE: ConfigSource = ConfigSource::simple("depolymerizer-bitcoin"); diff --git a/depolymerizer-bitcoin/src/db.rs b/depolymerizer-bitcoin/src/db.rs @@ -883,7 +883,7 @@ pub mod test { hashes::{Hash as _, sha256d::Hash}, }; use jiff::Span; - use sqlx::{PgPool, Postgres, pool::PoolConnection, postgres::PgRow}; + use sqlx::{PgConnection, PgPool, postgres::PgRow}; use taler_api::{db::TypeHelper as _, notification::dummy_listen, subject::IncomingKey}; use taler_common::{ api::{EddsaPublicKey, HashCode, ShortHashCode, params::History, wire::TransferRequest}, @@ -893,9 +893,9 @@ pub mod test { utils::now_sql_stable_ts, }, }; + use taler_macros::db_test; use crate::{ - CONFIG_SOURCE, api::test::CLIENT, db::{ AddIncomingResult, ProblematicTx, SyncOutResult, SyncOutState, TransferResult, TxOut, @@ -908,14 +908,8 @@ pub mod test { pub const CURR: Currency = Currency::TEST; - async fn setup() -> (PoolConnection<Postgres>, PgPool) { - taler_test_utils::db::db_test_setup(CONFIG_SOURCE).await - } - - #[tokio::test] - async fn kv() { - let (mut db, pool) = setup().await; - + #[db_test] + async fn kv(mut db: PgConnection, pool: PgPool) { // Empty status update_status(&mut db, false).await.unwrap(); update_status(&mut db, true).await.unwrap(); @@ -953,15 +947,14 @@ pub mod test { .assume_checked() }); - #[tokio::test] - async fn tx_in() { - let (mut db, pool) = setup().await; + #[db_test] + async fn tx_in(mut db: PgConnection, pool: PgPool) { let amount = amount("KUDOS:10"); let mut routine = async |first: &Option<IncomingKey>, second: &Option<IncomingKey>| { let id = sqlx::query("SELECT count(*) + 1 FROM tx_in") .try_map(|r: PgRow| r.try_get_u64(0)) - .fetch_one(&mut *db) + .fetch_one(&mut db) .await .unwrap(); let now = now_sql_stable_ts(); @@ -1056,10 +1049,8 @@ pub mod test { ); } - #[tokio::test] - async fn tx_in_admin() { - let (_, pool) = setup().await; - + #[db_test] + async fn tx_in_admin(pool: PgPool) { let amount = amount("KUDOS:10"); // Empty db @@ -1119,9 +1110,8 @@ pub mod test { ); } - #[tokio::test] - async fn sync_out_simple() { - let (_, pool) = setup().await; + #[db_test] + async fn sync_out_simple(pool: PgPool) { let amount = amount("KUDOS:10"); let now = now_sql_stable_ts(); @@ -1199,9 +1189,8 @@ pub mod test { ); } - #[tokio::test] - async fn sync_out_talerable() { - let (mut db, poll) = setup().await; + #[db_test] + async fn sync_out_talerable(mut db: PgConnection, pool: PgPool) { let amount = amount("KUDOS:10"); let now = now_sql_stable_ts(); @@ -1214,7 +1203,7 @@ pub mod test { metadata: None, credit_account: CLIENT.as_uri(), }; - let id = match transfer(&poll, &CLIENT, &t).await.unwrap() { + let id = match transfer(&pool, &CLIENT, &t).await.unwrap() { TransferResult::Success(res) => res.row_id, _ => unreachable!(), }; @@ -1235,23 +1224,23 @@ pub mod test { url, metadata: None, }; - transfer_sent(&mut *db, *id, &out.id).await.unwrap(); + transfer_sent(&mut db, *id, &out.id).await.unwrap(); assert_eq!( - sync_out(&poll, &out, &kind, false).await.unwrap(), + sync_out(&pool, &out, &kind, false).await.unwrap(), SyncOutResult { id: None, state: SyncOutState::None } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::New } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::None @@ -1274,24 +1263,24 @@ pub mod test { url, metadata: None, }; - transfer_sent(&mut *db, *id, &tx_id).await.unwrap(); - transfer_conflict(&mut *db, &conflict_id).await.unwrap(); + transfer_sent(&mut db, *id, &tx_id).await.unwrap(); + transfer_conflict(&mut db, &conflict_id).await.unwrap(); assert_eq!( - sync_out(&poll, &out, &kind, false).await.unwrap(), + sync_out(&pool, &out, &kind, false).await.unwrap(), SyncOutResult { id: None, state: SyncOutState::None } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::New } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::None @@ -1314,24 +1303,24 @@ pub mod test { url, metadata: None, }; - transfer_sent(&mut *db, *id, &tx_id).await.unwrap(); + transfer_sent(&mut db, *id, &tx_id).await.unwrap(); transfer_bumpfee(&mut db, &bump_id, wtid).await.unwrap(); assert_eq!( - sync_out(&poll, &out, &kind, false).await.unwrap(), + sync_out(&pool, &out, &kind, false).await.unwrap(), SyncOutResult { id: None, state: SyncOutState::None } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::New } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::None @@ -1353,14 +1342,14 @@ pub mod test { metadata: None, }; assert_eq!( - sync_out(&poll, &out, &kind, false).await.unwrap(), + sync_out(&pool, &out, &kind, false).await.unwrap(), SyncOutResult { id: None, state: SyncOutState::Recovered } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::New @@ -1382,14 +1371,14 @@ pub mod test { metadata: None, }; assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::Recovered } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::None @@ -1411,14 +1400,14 @@ pub mod test { metadata: None, }; assert_eq!( - sync_out(&poll, &out, &kind, false).await.unwrap(), + sync_out(&pool, &out, &kind, false).await.unwrap(), SyncOutResult { id: None, state: SyncOutState::Recovered } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::New @@ -1440,14 +1429,14 @@ pub mod test { metadata: None, }; assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::Recovered } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::None @@ -1469,16 +1458,16 @@ pub mod test { url, metadata: None, }; - transfer_sent(&mut *db, *id, &txid).await.unwrap(); + transfer_sent(&mut db, *id, &txid).await.unwrap(); assert_eq!( - sync_out(&poll, &out, &kind, false).await.unwrap(), + sync_out(&pool, &out, &kind, false).await.unwrap(), SyncOutResult { id: None, state: SyncOutState::Replaced } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::New @@ -1500,16 +1489,16 @@ pub mod test { url, metadata: None, }; - transfer_sent(&mut *db, *id, &txid).await.unwrap(); + transfer_sent(&mut db, *id, &txid).await.unwrap(); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::Replaced } ); assert_eq!( - sync_out(&poll, &out, &kind, true).await.unwrap(), + sync_out(&pool, &out, &kind, true).await.unwrap(), SyncOutResult { id: Some(*id), state: SyncOutState::None @@ -1518,12 +1507,11 @@ pub mod test { // Failure let (id, _, _) = &prepare_transfer().await; - transfer_ignored(&mut *db, *id, "Oh nooooo").await.unwrap(); + transfer_ignored(&mut db, *id, "Oh nooooo").await.unwrap(); } - #[tokio::test] - async fn bounces() { - let (_, db) = setup().await; + #[db_test] + async fn bounces(db: PgPool) { let amount = amount("KUDOS:10"); let now = now_sql_stable_ts(); @@ -1609,9 +1597,8 @@ pub mod test { assert_eq!(pending_bounce(&db).await.unwrap(), None); } - #[tokio::test] - async fn reorgs() { - let (_, pool) = setup().await; + #[db_test] + async fn reorgs(pool: PgPool) { let amount = amount("KUDOS:10"); let now = now_sql_stable_ts(); diff --git a/depolymerizer-bitcoin/src/lib.rs b/depolymerizer-bitcoin/src/lib.rs @@ -19,13 +19,14 @@ use bitcoin::{Address, Amount, Network, Txid, hashes::hex::FromHex}; use rpc::{Category, Transaction}; use rpc_utils::segwit_min_amount; use segwit::{decode_segwit_msg, encode_segwit_key}; -use taler_common::{api::EddsaPublicKey, config::parser::ConfigSource}; +use taler_common::api::EddsaPublicKey; use crate::{rpc::RpcApi, segwit::DecodeSegWitErr}; pub mod api; pub mod cli; pub mod config; +pub mod constants; pub mod db; pub mod loops; pub mod payto; @@ -36,8 +37,6 @@ pub mod setup; pub mod sql; pub mod taler_utils; -pub const CONFIG_SOURCE: ConfigSource = ConfigSource::simple("depolymerizer-bitcoin"); - #[derive(Debug, thiserror::Error)] pub enum GetOpReturnErr { #[error("Missing opreturn")] diff --git a/depolymerizer-bitcoin/src/main.rs b/depolymerizer-bitcoin/src/main.rs @@ -16,8 +16,8 @@ use clap::Parser as _; use depolymerizer_bitcoin::{ - CONFIG_SOURCE, cli::{Args, run}, + constants::CONFIG_SOURCE, }; use taler_common::taler_main;