depolymerization

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

commit 502ed283fd45af1f430e94a90f9a57e5d7621ade
parent e9c7a7907d5588da15b0faebfaeddb667872e639
Author: Antoine A <>
Date:   Wed, 11 Dec 2024 19:47:05 +0100

update utils

Diffstat:
MCargo.lock | 43+++++++++++++++++++------------------------
Mbtc-wire/src/loops/worker.rs | 4++--
Mbtc-wire/src/taler_utils.rs | 6+++---
Mcommon/src/config.rs | 12++++++++++--
Mcommon/src/sql.rs | 8+++++++-
Meth-wire/src/lib.rs | 4++--
Meth-wire/src/loops/worker.rs | 4++--
Meth-wire/src/sql.rs | 4++--
Meth-wire/src/taler_util.rs | 13++++++++-----
Minstrumentation/src/gateway.rs | 31++++++++++++++++---------------
Minstrumentation/src/utils.rs | 9++++++++-
Mwire-gateway/src/main.rs | 32++++++++++++++++----------------
12 files changed, 95 insertions(+), 75 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -166,7 +166,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower", "tower-layer", @@ -189,7 +189,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -945,13 +945,13 @@ dependencies = [ [[package]] name = "flexi_logger" -version = "0.29.6" +version = "0.29.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26948e37cfcb1f2c2cd38e0602d3a8ab6b9472c0c6eff4516fc8def9a3124d7" +checksum = "4613c3fa90ebf91dff72ff383a9324329c017819711bda86142be81368ac6fad" dependencies = [ "chrono", "log", - "thiserror 1.0.69", + "thiserror 2.0.6", ] [[package]] @@ -2149,9 +2149,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags", ] @@ -2279,9 +2279,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "log", "once_cell", @@ -2367,18 +2367,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -2845,12 +2845,6 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" @@ -2869,7 +2863,7 @@ dependencies = [ [[package]] name = "taler-api" version = "0.1.0" -source = "git+https://git.taler.net/taler-rust.git/#90d4608cf2f4a73cf13cb252e8892a27fa090e62" +source = "git+https://git.taler.net/taler-rust.git/#fcc09117ad3a761486922279845b3abec0efd024" dependencies = [ "axum", "dashmap", @@ -2890,13 +2884,14 @@ dependencies = [ [[package]] name = "taler-common" version = "0.1.0" -source = "git+https://git.taler.net/taler-rust.git/#90d4608cf2f4a73cf13cb252e8892a27fa090e62" +source = "git+https://git.taler.net/taler-rust.git/#fcc09117ad3a761486922279845b3abec0efd024" dependencies = [ "base32", "fastrand", "rand", "serde", "serde_json", + "serde_urlencoded", "serde_with", "sqlx", "thiserror 2.0.6", @@ -3136,14 +3131,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", diff --git a/btc-wire/src/loops/worker.rs b/btc-wire/src/loops/worker.rs @@ -335,7 +335,7 @@ fn sync_chain_incoming_confirmed( let credit_addr = full.details[0].address.clone().unwrap().assume_checked(); let amount = btc_to_taler(&full.amount, state.currency); let nb = db.execute("INSERT INTO tx_in (received, amount, reserve_pub, debit_acc, credit_acc) VALUES ($1, ($2, $3)::taler_amount, $4, $5, $6) ON CONFLICT (reserve_pub) DO NOTHING ", &[ - &((full.time * 1000000) as i64), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &reserve_pub.as_slice(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(&credit_addr).as_ref() + &((full.time * 1000000) as i64), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &reserve_pub.as_slice(), &btc_payto_url(&debit_addr).raw(), &btc_payto_url(&credit_addr).raw() ])?; if nb > 0 { info!( @@ -439,7 +439,7 @@ fn sync_chain_debit( let debit_addr = sender_address(rpc, full)?; let nb = db.execute( "INSERT INTO tx_out (created, amount, wtid, debit_acc, credit_acc, exchange_url, status, txid, request_uid) VALUES ($1, ($2, $3)::taler_amount, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (wtid) DO NOTHING", - &[&((full.time*1000000) as i64), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &wtid.as_slice(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(&credit_addr).as_ref(), &state.base_url.as_ref(), &(DebitStatus::Sent as i16), &id.as_byte_array().as_slice(), &None::<&[u8]>], + &[&((full.time*1000000) as i64), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &wtid.as_slice(), &btc_payto_url(&debit_addr).raw(), &btc_payto_url(&credit_addr).raw(), &state.base_url.as_ref(), &(DebitStatus::Sent as i16), &id.as_byte_array().as_slice(), &None::<&[u8]>], )?; if nb > 0 { warn!( diff --git a/btc-wire/src/taler_utils.rs b/btc-wire/src/taler_utils.rs @@ -16,14 +16,14 @@ //! Utils function to convert taler API types to bitcoin API types use bitcoin::{Address, Amount as BtcAmount, SignedAmount}; -use common::taler_common::amount::Amount; +use common::taler_common::{amount::Amount, payto::Payto}; use common::url::Url; use common::{currency::CurrencyBtc, taler_common::amount::FRAC_BASE}; use std::str::FromStr; /// Generate a payto uri from a btc address -pub fn btc_payto_url(addr: &Address) -> Url { - Url::from_str(&format!("payto://bitcoin/{}", addr)).unwrap() +pub fn btc_payto_url(addr: &Address) -> Payto { + Payto::from_str(&format!("payto://bitcoin/{}", addr)).unwrap() } /// Extract a btc address from a payto uri diff --git a/common/src/config.rs b/common/src/config.rs @@ -20,6 +20,7 @@ use std::{ process::Command, str::FromStr, }; +use taler_common::payto::Payto; use url::Url; use crate::{ @@ -98,8 +99,8 @@ impl TalerConfig { /* ----- Wire Gateway ----- */ - pub fn payto(&self) -> Url { - required(self.section(), "PAYTO", url) + pub fn payto(&self) -> Payto { + required(self.section(), "PAYTO", payto) } pub fn port(&self) -> u16 { @@ -200,6 +201,13 @@ pub fn url(properties: &Properties, name: &str) -> Option<Url> { }) } +pub fn payto(properties: &Properties, name: &str) -> Option<Payto> { + properties.get(name).map(|s| { + s.parse() + .or_fail(|e| format!("config {}={} is not a valid payto: {}", name, s, e)) + }) +} + pub fn postgres(properties: &Properties, name: &str) -> Option<postgres::Config> { properties.get(name).map(|s| { postgres::Config::from_str(s) diff --git a/common/src/sql.rs b/common/src/sql.rs @@ -17,7 +17,7 @@ use std::str::FromStr; use postgres::Row; -use taler_common::{amount::Amount, api_common::SafeU64}; +use taler_common::{amount::Amount, api_common::SafeU64, payto::Payto}; use url::Url; use crate::log::OrFail; @@ -28,6 +28,12 @@ pub fn sql_url(row: &Row, idx: usize) -> Url { Url::from_str(str).or_fail(|_| format!("Database invariant: expected an url got {}", str)) } +/// Payto from sql +pub fn sql_payto(row: &Row, idx: usize) -> Payto { + let str: &str = row.get(idx); + Payto::from_str(str).or_fail(|_| format!("Database invariant: expected a payto got {}", str)) +} + /// Ethereum amount from sql pub fn sql_amount(row: &Row, idx: usize, currency: &str) -> Amount { let val: i64 = row.get(idx); diff --git a/eth-wire/src/lib.rs b/eth-wire/src/lib.rs @@ -26,7 +26,7 @@ use common::{ log::{fail, OrFail}, metadata::{InMetadata, OutMetadata}, postgres, - taler_common::amount::Amount, + taler_common::{amount::Amount, payto::Payto}, url::Url, }; use ethereum_types::{Address, H160, H256, U256, U64}; @@ -232,7 +232,7 @@ pub struct WireState { pub lifetime: Option<u32>, pub bump_delay: Option<u32>, pub base_url: Url, - pub payto: Url, + pub payto: Payto, pub db_config: postgres::Config, pub currency: CurrencyEth, } diff --git a/eth-wire/src/loops/worker.rs b/eth-wire/src/loops/worker.rs @@ -279,7 +279,7 @@ fn sync_chain_incoming_confirmed( let amount = eth_to_taler(&tx.value, state.currency); let credit_addr = tx.from.expect("Not coinbase"); let nb = db.execute("INSERT INTO tx_in (received, amount, reserve_pub, debit_acc, credit_acc) VALUES ($1, ($2, $3)::taler_amount, $4, $5, $6) ON CONFLICT (reserve_pub) DO NOTHING ", &[ - &Timestamp::now().as_sql_micros(), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &reserve_pub.as_ref(), &eth_payto_url(&credit_addr).as_ref(), &state.payto.as_ref() + &Timestamp::now().as_sql_micros(), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &reserve_pub.as_ref(), &eth_payto_url(&credit_addr).raw(), &state.payto.raw() ])?; if nb > 0 { info!( @@ -353,7 +353,7 @@ fn sync_chain_outgoing(tx: &SyncTransaction, db: &mut Client, state: &WireState) // Else add to database let nb = db.execute( "INSERT INTO tx_out (created, amount, wtid, debit_acc, credit_acc, exchange_url, status, txid, request_uid) VALUES ($1, ($2, $3)::taler_amount, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (wtid) DO NOTHING", - &[&Timestamp::now().as_sql_micros(), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &wtid.as_ref(), &eth_payto_url(&state.address).as_ref(), &eth_payto_url(&credit_addr).as_ref(), &state.base_url.as_ref(), &(DebitStatus::Sent as i16), &tx.hash.as_ref(), &None::<&[u8]>], + &[&Timestamp::now().as_sql_micros(), &(amount.decimal.val as i64), &(amount.decimal.frac as i32), &wtid.as_ref(), &eth_payto_url(&state.address).raw(), &eth_payto_url(&credit_addr).raw(), &state.base_url.as_ref(), &(DebitStatus::Sent as i16), &tx.hash.as_ref(), &None::<&[u8]>], )?; if nb > 0 { warn!( diff --git a/eth-wire/src/sql.rs b/eth-wire/src/sql.rs @@ -17,7 +17,7 @@ use common::{ currency::CurrencyEth, log::OrFail, postgres::Row, - sql::{sql_amount, sql_array, sql_url}, + sql::{sql_amount, sql_array, sql_payto}, }; use eth_wire::taler_util::{eth_payto_addr, taler_to_eth}; use ethereum_types::{H160, H256, U256}; @@ -35,7 +35,7 @@ pub fn sql_eth_amount(row: &Row, idx: usize, currency: CurrencyEth) -> U256 { /// Ethereum address from sql pub fn sql_addr(row: &Row, idx: usize) -> H160 { - let url = sql_url(row, idx); + let url = sql_payto(row, idx); eth_payto_addr(&url).or_fail(|_| { format!( "Database invariant: expected an ethereum payto url got {}", diff --git a/eth-wire/src/taler_util.rs b/eth-wire/src/taler_util.rs @@ -17,8 +17,10 @@ use std::str::FromStr; use common::{ currency::CurrencyEth, - taler_common::amount::{Amount, FRAC_BASE}, - url::Url, + taler_common::{ + amount::{Amount, FRAC_BASE}, + payto::Payto, + }, }; use ethereum_types::{Address, U256}; @@ -26,8 +28,8 @@ pub const WEI: u64 = 1_000_000_000_000_000_000; pub const TRUNC: u64 = WEI / FRAC_BASE as u64; /// Generate a payto uri from an eth address -pub fn eth_payto_url(addr: &Address) -> Url { - Url::from_str(&format!( +pub fn eth_payto_url(addr: &Address) -> Payto { + Payto::from_str(&format!( "payto://ethereum/{}", hex::encode(addr.as_bytes()) )) @@ -35,7 +37,8 @@ pub fn eth_payto_url(addr: &Address) -> Url { } /// Extract an eth address from a payto uri -pub fn eth_payto_addr(url: &Url) -> Result<Address, String> { +pub fn eth_payto_addr(payto: &Payto) -> Result<Address, String> { + let url = payto.as_ref(); if url.domain() != Some("ethereum") { return Err(format!( "Expected domain 'ethereum' got '{}'", diff --git a/instrumentation/src/gateway.rs b/instrumentation/src/gateway.rs @@ -18,8 +18,13 @@ use std::str::FromStr; use btc_wire::taler_utils::btc_payto_url; use common::{ - rand_slice, taler_common::amount::Amount as TalerAmount, taler_common::api_common::Base32, - taler_common::api_wire::TransferRequest, url::Url, + rand_slice, + taler_common::{ + amount::Amount as TalerAmount, + api_common::Base32, + api_wire::TransferRequest, + payto::{payto, Payto}, + }, }; use libdeflater::{CompressionLvl, Compressor}; use ureq::Response; @@ -29,10 +34,10 @@ use crate::{ utils::{cmd_out, cmd_redirect_ok, gateway_error, TestCtx}, }; -fn client_transfer(gateway_url: &str, payto_url: &str, amount: &str) -> String { +fn client_transfer(gateway_url: &str, payto_url: &Payto, amount: &str) -> String { cmd_out( "taler-exchange-wire-gateway-client", - &["-b", gateway_url, "-C", payto_url, "-a", amount], + &["-b", gateway_url, "-C", payto_url.raw(), "-a", amount], ) } @@ -63,7 +68,7 @@ pub fn api(ctx: TestCtx) { "-b", &ctx.gateway_url, "-D", - btc_payto_url(&ctx.client_addr).as_ref(), + btc_payto_url(&ctx.client_addr).raw(), "-a", &amount, ], @@ -84,11 +89,7 @@ pub fn api(ctx: TestCtx) { let mut amounts = Vec::new(); for n in 1..10 { let amount = format!("{}:0.0000{}", ctx.taler_conf.currency.to_str(), n); - client_transfer( - &ctx.gateway_url, - btc_payto_url(&ctx.client_addr).as_ref(), - &amount, - ); + client_transfer(&ctx.gateway_url, &btc_payto_url(&ctx.client_addr), &amount); amounts.push(amount); } @@ -111,7 +112,7 @@ pub fn api(ctx: TestCtx) { } let amount = &format!("{}:0.00042", ctx.taler_conf.currency.to_str()); - let payto = btc_payto_url(&ctx.client_addr).to_string(); + let btc_payto = btc_payto_url(&ctx.client_addr); ctx.step("Request format"); { @@ -124,12 +125,12 @@ pub fn api(ctx: TestCtx) { "payto://bitcoin/42$CLIENT", ] { let url = url.replace("$CLIENT", &ctx.client_addr.to_string()); - let result = client_transfer(&ctx.gateway_url, &url, amount); + let result = client_transfer(&ctx.gateway_url, &payto(url), amount); assert!(result.contains("(400/24)")); } // Bad transaction amount - let result = client_transfer(&ctx.gateway_url, &payto, "ATC:0.00042"); + let result = client_transfer(&ctx.gateway_url, &btc_payto, "ATC:0.00042"); assert!(result.contains("(400/30)")); // Bad history delta @@ -152,7 +153,7 @@ pub fn api(ctx: TestCtx) { amount: TalerAmount::from_str(amount).unwrap(), exchange_base_url: ctx.taler_conf.base_url(), wtid: Base32::from(rand_slice()), - credit_account: Url::from_str(&payto).unwrap(), + credit_account: btc_payto, }; // Same assert_eq!( @@ -230,7 +231,7 @@ pub fn auth(ctx: TestCtx) { "-s", "exchange-accountcredentials-admin", "-C", - btc_payto_url(&ctx.client_addr).as_ref(), + btc_payto_url(&ctx.client_addr).raw(), "-a", &format!("{}:0.00042", ctx.taler_conf.currency.to_str()), ], diff --git a/instrumentation/src/utils.rs b/instrumentation/src/utils.rs @@ -34,6 +34,7 @@ use common::{ amount::Amount, api_common::Base32, api_wire::{IncomingBankTransaction, IncomingHistory, OutgoingHistory, TransferRequest}, + payto::Payto, }, url::Url, }; @@ -108,7 +109,13 @@ pub fn check_gateway_up(base_url: &str) -> bool { ureq::get(&format!("{}config", base_url)).call().is_ok() } -pub fn transfer(base_url: &str, wtid: &[u8; 32], url: &Url, credit_account: Url, amount: &Amount) { +pub fn transfer( + base_url: &str, + wtid: &[u8; 32], + url: &Url, + credit_account: Payto, + amount: &Amount, +) { ureq::post(&format!("{}transfer", base_url)) .send_json(TransferRequest { request_uid: Base32::from(rand_slice()), diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs @@ -27,7 +27,6 @@ use common::{ fail, log::{error, info}, }, - url::Url, }; use listenfd::ListenFd; use sqlx::Row; @@ -56,12 +55,13 @@ use taler_common::{ TransferList, TransferRequest, TransferResponse, TransferState, TransferStatus, }, error_code::ErrorCode, + payto::Payto, }; use tokio::time::sleep; struct ServerState { pool: PgPool, - payto: Url, + payto: Payto, currency: Currency, status: AtomicBool, } @@ -90,7 +90,7 @@ impl WireGatewayImpl for ServerState { amount: r.try_get_amount_i(0, self.currency())?, exchange_base_url: r.try_get_url(2)?, wtid: r.try_get_base32(3)?, - credit_account: r.try_get_url(4)?, + credit_account: r.try_get_payto(4)?, }; if prev == req { // Idempotence @@ -114,8 +114,8 @@ impl WireGatewayImpl for ServerState { .bind_timestamp(&Timestamp::now()) .bind_amount(&req.amount) .bind(req.wtid.as_slice()) - .bind(self.payto.as_str()) - .bind(req.credit_account.as_str()) + .bind((&self.payto).raw()) + .bind(req.credit_account.raw()) .bind(req.exchange_base_url.as_str()) .bind(req.request_uid.as_slice()) .fetch_one(&mut *tx).await?; @@ -149,7 +149,7 @@ impl WireGatewayImpl for ServerState { date: r.try_get_timestamp(1)?, amount: r.try_get_amount_i(2, self.currency())?, wtid: r.try_get_base32(4)?, - credit_account: r.try_get_url(5)?, + credit_account: r.try_get_payto(5)?, exchange_base_url: r.try_get_url(6)?, }) }).await?; @@ -175,7 +175,7 @@ impl WireGatewayImpl for ServerState { date: r.try_get_timestamp(1)?, amount: r.try_get_amount_i(2, self.currency())?, reserve_pub: r.try_get_base32(4)?, - debit_account: r.try_get_url(5)?, + debit_account: r.try_get_payto(5)?, }) }, ) @@ -195,7 +195,7 @@ impl WireGatewayImpl for ServerState { .bind_timestamp(&Timestamp::now()) .bind_amount(&req.amount) .bind(req.reserve_pub.as_slice()) - .bind(req.debit_account.as_str()) + .bind(req.debit_account.raw()) .bind("payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj") .fetch_one(&self.pool).await?; Ok(AddIncomingResponse { @@ -296,18 +296,18 @@ async fn main() { info!("wire-gateway stopped"); } -/// Check if an url if a valid payto url for the configured currency -fn check_payto(url: &Url, currency: Currency) -> bool { +/// Check if a payto is valid the configured currency +fn check_payto(payto: &Payto, currency: Currency) -> bool { match currency { - Currency::ETH(_) => check_pay_to_eth(url), - Currency::BTC(_) => check_pay_to_btc(url), + Currency::ETH(_) => check_pay_to_eth(payto), + Currency::BTC(_) => check_pay_to_btc(payto), } } /// Check if an url is a valid bitcoin payto url -fn check_pay_to_btc(url: &Url) -> bool { +fn check_pay_to_btc(payto: &Payto) -> bool { + let url = payto.as_ref(); url.domain() == Some("bitcoin") - && url.scheme() == "payto" && url.username() == "" && url.password().is_none() && url.query().is_none() @@ -316,9 +316,9 @@ fn check_pay_to_btc(url: &Url) -> bool { } /// Check if an url is a valid ethereum payto url -fn check_pay_to_eth(url: &Url) -> bool { +fn check_pay_to_eth(payto: &Payto) -> bool { + let url = payto.as_ref(); url.domain() == Some("ethereum") - && url.scheme() == "payto" && url.username() == "" && url.password().is_none() && url.query().is_none()