depolymerization

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

commit 9eabce59a590696c5ca5c2a787c3867256348c4f
parent 6d2599c1b1ed11109b6a7c012167608eca00145e
Author: Antoine A <>
Date:   Thu,  3 Feb 2022 18:26:19 +0100

Remove Delayed transaction status

Diffstat:
MREADME.md | 31+++++++++++++------------------
Mbtc-wire/README.md | 61++++---------------------------------------------------------
Mbtc-wire/src/loops/worker.rs | 99++++++++++++++++++++++++++++++-------------------------------------------------
Mbtc-wire/src/main.rs | 7+++----
Mbtc-wire/src/metadata.rs | 8++++----
Dbtc-wire/src/status.rs | 69---------------------------------------------------------------------
Mcommon/src/lib.rs | 1+
Acommon/src/status.rs | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Meth-wire/src/loops/worker.rs | 46++++++++++++++++++----------------------------
Meth-wire/src/main.rs | 15++++++++-------
Deth-wire/src/status.rs | 69---------------------------------------------------------------------
Mtest/common.sh | 2+-
12 files changed, 162 insertions(+), 318 deletions(-)

diff --git a/README.md b/README.md @@ -40,25 +40,20 @@ HTTP_LIFETIME = 0 WIRE_LIFETIME = 0 ``` -Modules have specific configuration: - -- [wire-gateway](wire-gateway/README.md#Configuration) -- [btc-wire](btc-wire/README.md#Configuration) +### Stuck transaction -## Architecture +When a transaction is sent with a fee too small compared to other transaction, +it can take an unlimited amount of time for this transaction to be mined. It is +possible to replace this transaction with another transaction with more fees. +Depolymerizer can be configured to do this automatically: -### Linking Taler to blockchains - -``` - ┌─────┐ ┌────────────────┐ ┌──────────┐ - │Taler│◄─►│Depolymerization│◄─►│Blockchain│ - └─────┘ └────────────────┘ └──────────┘ +``` ini +[depolymerizer-___] +# Delay in seconds before bumping an unconfirmed transaction fee (0 mean never) +BUMP_DELAY = 0 ``` -### Depolymerization architecture +Modules have specific configuration: -``` - ┌────────────┐ ┌──────────┐ ┌────────┐ - │wire_gateway│◄─►│PostgreSQL│◄─►│###_wire│ - └────────────┘ └──────────┘ └────────┘ -``` -\ No newline at end of file +- [wire-gateway](wire-gateway/README.md#Configuration) +- [btc-wire](btc-wire/README.md#Configuration) +\ No newline at end of file diff --git a/btc-wire/README.md b/btc-wire/README.md @@ -22,7 +22,6 @@ The configuration is based on [taler.conf](https://docs.taler.net/manpages/taler DATA_DIR = CONFIRMATION = 6 BOUNCE_FEE = 1000 -BUMP_DELAY = ``` ### bitcoin.conf @@ -41,62 +40,9 @@ the RPC server, `txindex=1` and `maxtxfee` are mandatory. 7. Run wire-gateway `wire-gateway` 8. Run btc-wire `btc-wire` -## How it work ? - - -### Outgoing transaction lifecycle - -#### Transaction lifecycle - -``` - │ - ▼ - ┌─────────┐ - ┌─┤Requested├─┐ - │ └─────────┘ │ - ▼ ▼ - ┌────┐ ┌───────┐ - │Sent│◄────►│Delayed│ - └────┘ └───────┘ - - -> Requested API request -Requested -> Sent Sent in the blockchain -Requested -> Delayed Error while sending -Delayed -> Sent Sent in the blockchain -Send -> Delayed Conflicting transaction (reorg) -``` - -#### Bounce lifecycle - -``` - │ - ▼ - ┌─────────┐ - ┌─────┤Requested├────┐ - │ └────┬────┘ │ - ▼ ▼ ▼ - ┌────┐ ┌───────┐ ┌───────┐ - │Sent│◄─►│Delayed│ │Ignored│ - └────┘ └───────┘ └───────┘ - - -> Requested Transaction in wrong format -Requested -> Sent Sent in the blockchain -Requested -> Ignored Insufficient amount -Requested -> Delayed Error while sending -Delayed -> Sent Sent in the blockchain -Send -> Delayed Conflicting transaction (reorg) -``` +## Implementation details ### Stuck transaction -When a transaction is sent with a fee too small compared to other transaction, -it can take an unlimited amount of time for this transaction to be mined. It is -possible to replace this transaction with another transaction with more fees -using [BIP 125](https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki). -btc-wire can be configured to do this automatically: - -``` ini -[depolymerizer-bitcoin] -# Delay in seconds before bumping an unconfirmed transaction fee -BUMP_DELAY = 10 -``` +We resolve stuck transaction by always sending replaceable transaction +using [BIP 125](https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki). +\ No newline at end of file diff --git a/btc-wire/src/loops/worker.rs b/btc-wire/src/loops/worker.rs @@ -22,25 +22,25 @@ use std::{ use bitcoin::{hashes::Hash, Amount as BtcAmount, BlockHash, Txid}; use btc_wire::{ - rpc::{self, Rpc, Category, ErrorCode, TransactionFull}, + rpc::{self, Category, ErrorCode, Rpc, TransactionFull}, rpc_utils::sender_address, GetOpReturnErr, GetSegwitErr, }; -use postgres::{fallible_iterator::FallibleIterator, Client}; use common::{ api_common::base32, config::BtcConfig, log::log::{error, info, warn}, postgres, sql::{sql_array, sql_url}, + status::{BounceStatus, WithdrawStatus}, }; +use postgres::{fallible_iterator::FallibleIterator, Client}; use crate::{ fail_point::fail_point, metadata::OutMetadata, reconnect::{AutoReconnectRPC, AutoReconnectSql}, sql::{sql_addr, sql_btc_amount, sql_txid}, - status::{BounceStatus, TxStatus}, taler_util::{btc_payto_url, btc_to_taler}, WireState, }; @@ -108,16 +108,12 @@ pub fn worker( // As we are now in sync with the blockchain if a transaction is in requested or delayed state it have not been sent - // Send delayed transactions - while send(db, rpc, TxStatus::Delayed)? {} - // Send requested transactions - while send(db, rpc, TxStatus::Requested)? {} + // Send requested withdraws + while withdraw(db, rpc)? {} let bounce_fee = BtcAmount::from_sat(config.bounce_fee); - // Send delayed bounce - while bounce(db, rpc, BounceStatus::Delayed, &bounce_fee)? {} // Send requested bounce - while bounce(db, rpc, BounceStatus::Requested, &bounce_fee)? {} + while bounce(db, rpc, &bounce_fee)? {} Ok(()) })(); @@ -138,13 +134,12 @@ pub fn worker( } } -/// Send atransaction on the blockchain, return true if more transactions with the same status remains -fn send(db: &mut Client, rpc: &mut Rpc, status: TxStatus) -> LoopResult<bool> { - assert!(status == TxStatus::Delayed || status == TxStatus::Requested); +/// Send a withdraw transaction on the blockchain, return false if no more requested transaction are found +fn withdraw(db: &mut Client, rpc: &mut Rpc) -> LoopResult<bool> { // We rely on the advisory lock to ensure we are the only one sending transactions let row = db.query_opt( "SELECT id, amount, wtid, credit_acc, exchange_url FROM tx_out WHERE status=$1 ORDER BY _date LIMIT 1", - &[&(status as i16)], + &[&(WithdrawStatus::Requested as i16)], )?; if let Some(row) = &row { let id: i32 = row.get(0); @@ -152,50 +147,33 @@ fn send(db: &mut Client, rpc: &mut Rpc, status: TxStatus) -> LoopResult<bool> { let wtid: [u8; 32] = sql_array(row, 2); let addr = sql_addr(row, 3); let url = sql_url(row, 4); - let info = OutMetadata::Transaction { wtid, url }; - let metadata = info.encode(); + let metadata = OutMetadata::Withdraw { wtid, url }; - match rpc.send_op_return(&addr, &amount, &metadata, false, true) { - Ok(tx_id) => { - fail_point("(injected) fail send", 0.3)?; - db.execute( - "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3", - &[&(TxStatus::Sent as i16), &tx_id.as_ref(), &id], - )?; - let amount = btc_to_taler(&amount.to_signed().unwrap()); - info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr); - } - Err(e) => { - db.execute( - "UPDATE tx_out SET status=$1 WHERE id=$2", - &[&(TxStatus::Delayed as i16), &id], - )?; - Err(e)?; - } - } + let tx_id = rpc.send_op_return(&addr, &amount, &metadata.encode(), false, true)?; + fail_point("(injected) fail send", 0.3)?; + db.execute( + "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3", + &[&(WithdrawStatus::Sent as i16), &tx_id.as_ref(), &id], + )?; + let amount = btc_to_taler(&amount.to_signed().unwrap()); + info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr); } Ok(row.is_some()) } -/// Bounce a transaction on the blockchain, return true if more bounce with the same status remains -fn bounce( - db: &mut Client, - rpc: &mut Rpc, - status: BounceStatus, - fee: &BtcAmount, -) -> LoopResult<bool> { - assert!(status == BounceStatus::Delayed || status == BounceStatus::Requested); +/// Bounce a transaction on the blockchain, return false if nor more requested transaction are found +fn bounce(db: &mut Client, rpc: &mut Rpc, fee: &BtcAmount) -> LoopResult<bool> { // We rely on the advisory lock to ensure we are the only one sending transactions let row = db.query_opt( "SELECT id, bounced FROM bounce WHERE status=$1 ORDER BY _date LIMIT 1", - &[&(status as i16)], + &[&(BounceStatus::Requested as i16)], )?; if let Some(row) = &row { let id: i32 = row.get(0); let bounced: Txid = sql_txid(row, 1); - let info = OutMetadata::Bounce { bounced }; + let metadata = OutMetadata::Bounce { bounced }; - match rpc.bounce(&bounced, fee, &info.encode()) { + match rpc.bounce(&bounced, fee, &metadata.encode()) { Ok(it) => { fail_point("(injected) fail bounce", 0.3)?; db.execute( @@ -215,13 +193,7 @@ fn bounce( )?; info!("|| (ignore) {} because {}", &bounced, msg); } - e => { - db.execute( - "UPDATE bounce SET status=$1 WHERE id=$2", - &[&(BounceStatus::Delayed as i16), &id], - )?; - Err(e)?; - } + e => Err(e)?, }, } } @@ -404,7 +376,7 @@ fn sync_chain_outgoing( .map(|(full, bytes)| (full, OutMetadata::decode(&bytes))) { Ok((full, Ok(info))) => match info { - OutMetadata::Transaction { wtid, .. } => { + OutMetadata::Withdraw { wtid, .. } => { sync_chain_outgoing_send(id, &full, &wtid, rpc, db, confirmations, config)? } OutMetadata::Bounce { bounced } => { @@ -438,7 +410,7 @@ fn sync_chain_outgoing_send( // Handle conflicting tx let nb_row = db.execute( "UPDATE tx_out SET status=$1, txid=NULL where txid=$2", - &[&(TxStatus::Delayed as i16), &id.as_ref()], + &[&(WithdrawStatus::Requested as i16), &id.as_ref()], )?; if nb_row > 0 { warn!( @@ -459,11 +431,16 @@ fn sync_chain_outgoing_send( // If already in database, sync status let row_id: i32 = row.get(0); let status: i16 = row.get(1); - match TxStatus::try_from(status as u8).unwrap() { - TxStatus::Requested | TxStatus::Delayed => { + match WithdrawStatus::try_from(status as u8).unwrap() { + WithdrawStatus::Requested => { let nb_row = db.execute( "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3 AND status=$4", - &[&(TxStatus::Sent as i16), &id.as_ref(), &row_id, &status], + &[ + &(WithdrawStatus::Sent as i16), + &id.as_ref(), + &row_id, + &status, + ], )?; if nb_row > 0 { warn!( @@ -475,7 +452,7 @@ fn sync_chain_outgoing_send( ); } } - TxStatus::Sent => { + WithdrawStatus::Sent => { if let Some(txid) = full.replaces_txid { let stored_id = sql_txid(&row, 2); if txid == stored_id { @@ -501,7 +478,7 @@ fn sync_chain_outgoing_send( let date = SystemTime::UNIX_EPOCH + Duration::from_secs(full.time); let nb = db.execute( "INSERT INTO tx_out (_date, amount, wtid, debit_acc, credit_acc, exchange_url, status, txid, request_uid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (wtid) DO NOTHING", - &[&date, &amount.to_string(), &wtid.as_ref(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(credit_addr).as_ref(), &config.base_url.as_ref(), &(TxStatus::Sent as i16), &id.as_ref(), &None::<&[u8]>], + &[&date, &amount.to_string(), &wtid.as_ref(), &btc_payto_url(&debit_addr).as_ref(), &btc_payto_url(credit_addr).as_ref(), &config.base_url.as_ref(), &(WithdrawStatus::Sent as i16), &id.as_ref(), &None::<&[u8]>], )?; if nb > 0 { warn!( @@ -551,7 +528,7 @@ fn sync_chain_outgoing_bounce( // Handle conflicting tx let nb_row = db.execute( "UPDATE bounce SET status=$1, txid=NULL where txid=$2", - &[&(BounceStatus::Delayed as i16), &id.as_ref()], + &[&(BounceStatus::Requested as i16), &id.as_ref()], )?; if nb_row > 0 { warn!("|| (conflict) {} in {}", &bounced, &id); @@ -567,7 +544,7 @@ fn sync_chain_outgoing_bounce( let row_id: i32 = row.get(0); let status: i16 = row.get(1); match BounceStatus::try_from(status as u8).unwrap() { - BounceStatus::Requested | BounceStatus::Delayed => { + BounceStatus::Requested => { let nb_row = db.execute( "UPDATE bounce SET status=$1, txid=$2 WHERE id=$3 AND status=$4", &[&(BounceStatus::Sent as i16), &id.as_ref(), &row_id, &status], diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs @@ -19,21 +19,20 @@ use btc_wire::{ rpc::Rpc, rpc_utils::default_data_dir, }; -use reconnect::{AutoReconnectRPC, AutoReconnectSql}; -use std::{sync::atomic::AtomicU16, thread::JoinHandle}; use common::{ config::{load_btc_config, BtcConfig}, log::log::info, }; +use reconnect::{AutoReconnectRPC, AutoReconnectSql}; +use std::{sync::atomic::AtomicU16, thread::JoinHandle}; use crate::loops::{analysis::analysis, watcher::watcher, worker::worker}; mod fail_point; -mod metadata; mod loops; +mod metadata; mod reconnect; mod sql; -mod status; mod taler_util; pub struct WireState { diff --git a/btc-wire/src/metadata.rs b/btc-wire/src/metadata.rs @@ -33,7 +33,7 @@ pub enum DecodeErr { /// Encoded metadata for outgoing transaction #[derive(Debug, Clone, PartialEq, Eq)] pub enum OutMetadata { - Transaction { wtid: [u8; 32], url: Url }, + Withdraw { wtid: [u8; 32], url: Url }, Bounce { bounced: Txid }, } @@ -44,7 +44,7 @@ impl OutMetadata { pub fn encode(&self) -> Vec<u8> { let mut buffer = Vec::new(); match self { - OutMetadata::Transaction { wtid, url } => { + OutMetadata::Withdraw { wtid, url } => { buffer.push(if url.scheme() == "http" { 1 } else { 0 }); buffer.extend_from_slice(wtid); let parts = format!("{}{}", url.domain().unwrap_or(""), url.path()); @@ -75,7 +75,7 @@ impl OutMetadata { uri_pack::unpack_uri(&bytes[33..])?, ); let url = Url::parse(&packed).unwrap(); - Ok(OutMetadata::Transaction { + Ok(OutMetadata::Withdraw { wtid: bytes[1..33].try_into().unwrap(), url, }) @@ -106,7 +106,7 @@ mod test { for url in urls { let wtid = rand_slice(); let url = Url::parse(url).unwrap(); - let info = OutMetadata::Transaction { wtid, url }; + let info = OutMetadata::Withdraw { wtid, url }; let encoded = info.encode(); let decoded = OutMetadata::decode(&encoded).unwrap(); assert_eq!(decoded, info); diff --git a/btc-wire/src/status.rs b/btc-wire/src/status.rs @@ -1,69 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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/> -*/ -//! Transactions status in database - -/// Outgoing transaction status -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TxStatus { - /// Client have ask for a transaction (default) - Requested = 0, - /// The wire failed to send this transaction and will try later - Delayed = 1, - /// Transaction have been announced to the bitcoin network - Sent = 2, -} - -impl TryFrom<u8> for TxStatus { - type Error = (); - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - x if x == TxStatus::Requested as u8 => Ok(TxStatus::Requested), - x if x == TxStatus::Sent as u8 => Ok(TxStatus::Sent), - x if x == TxStatus::Delayed as u8 => Ok(TxStatus::Delayed), - _ => Err(()), - } - } -} - -/// Bounce transaction status -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BounceStatus { - /// Bounce have been requested (default) - Requested = 0, - /// The wire failed to send this bounce and will try later - Delayed = 1, - /// Bounce will not be sent (e.g: bounce amount smaller than bounce fee) - Ignored = 2, - /// Bounce have been announced to the bitcoin network - Sent = 3, -} - -impl TryFrom<u8> for BounceStatus { - type Error = (); - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - x if x == BounceStatus::Requested as u8 => Ok(BounceStatus::Requested), - x if x == BounceStatus::Sent as u8 => Ok(BounceStatus::Sent), - x if x == BounceStatus::Delayed as u8 => Ok(BounceStatus::Delayed), - x if x == BounceStatus::Ignored as u8 => Ok(BounceStatus::Ignored), - _ => Err(()), - } - } -} diff --git a/common/src/lib.rs b/common/src/lib.rs @@ -24,6 +24,7 @@ pub mod config; pub mod error_codes; pub mod log; pub mod sql; +pub mod status; /// Secure random slice generator using getrandom pub fn rand_slice<const N: usize>() -> [u8; N] { diff --git a/common/src/status.rs b/common/src/status.rs @@ -0,0 +1,72 @@ +/* + This file is part of TALER + Copyright (C) 2022 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/> +*/ +//! Transactions status in database + +/// Withdraw transaction status +/// +/// -> Requested API request +/// Requested -> Sent Announced to the bitcoin network +/// Sent -> Requested Conflicting transaction (reorg) +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WithdrawStatus { + /// Withdraw have been requested (default) + Requested = 0, + /// Withdraw have been announced to the bitcoin network + Sent = 1, +} + +impl TryFrom<u8> for WithdrawStatus { + type Error = (); + + fn try_from(v: u8) -> Result<Self, Self::Error> { + match v { + x if x == WithdrawStatus::Requested as u8 => Ok(WithdrawStatus::Requested), + x if x == WithdrawStatus::Sent as u8 => Ok(WithdrawStatus::Sent), + _ => Err(()), + } + } +} + +/// Bounce transaction status +/// +/// -> Requested Deposit in wrong format +/// Requested -> Ignored Insufficient found +/// Requested -> Sent Announced to the bitcoin network +/// Sent -> Requested Conflicting transaction (reorg) +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BounceStatus { + /// Bounce have been requested (default) + Requested = 0, + /// Bounce will never be sent (e.g: bounce amount smaller than bounce fee) + Ignored = 1, + /// Bounce have been announced to the bitcoin network + Sent = 2, +} + +impl TryFrom<u8> for BounceStatus { + type Error = (); + + fn try_from(v: u8) -> Result<Self, Self::Error> { + match v { + x if x == BounceStatus::Requested as u8 => Ok(BounceStatus::Requested), + x if x == BounceStatus::Sent as u8 => Ok(BounceStatus::Sent), + x if x == BounceStatus::Ignored as u8 => Ok(BounceStatus::Ignored), + _ => Err(()), + } + } +} diff --git a/eth-wire/src/loops/worker.rs b/eth-wire/src/loops/worker.rs @@ -15,22 +15,22 @@ */ use std::time::SystemTime; -use eth_wire::{ - metadata::InMetadata, - rpc::Rpc, - taler_util::{eth_payto_url, eth_to_taler}, - BlockState, -}; use common::{ api_common::base32, log::log::{error, info}, postgres::{fallible_iterator::FallibleIterator, Client}, sql::{sql_array, sql_url}, + status::WithdrawStatus, +}; +use eth_wire::{ + metadata::InMetadata, + rpc::Rpc, + taler_util::{eth_payto_url, eth_to_taler}, + BlockState, }; use crate::{ sql::{sql_addr, sql_eth_amount}, - status::TxStatus, LoopResult, WireState, }; @@ -54,7 +54,7 @@ pub fn worker(mut rpc: Rpc, mut db: Client, state: &WireState) { sync_chain(&mut rpc, &mut db, state)?; - while send(&mut db, &mut rpc, state)? {} + while withdraw(&mut db, &mut rpc, state)? {} Ok(()) })(); @@ -115,12 +115,12 @@ fn sync_chain(rpc: &mut Rpc, db: &mut Client, state: &WireState) -> LoopResult<b Ok(true) } -/// Send a transaction on the blockchain, return true if more transactions with the same status remains -fn send(db: &mut Client, rpc: &mut Rpc, state: &WireState) -> LoopResult<bool> { +/// Send a withdraw transaction on the blockchain, return false if no more requested transaction are found +fn withdraw(db: &mut Client, rpc: &mut Rpc, state: &WireState) -> LoopResult<bool> { // We rely on the advisory lock to ensure we are the only one sending transactions let row = db.query_opt( "SELECT id, amount, wtid, credit_acc, exchange_url FROM tx_out WHERE status=$1 ORDER BY _date LIMIT 1", -&[&(TxStatus::Requested as i16)], +&[&(WithdrawStatus::Requested as i16)], )?; if let Some(row) = &row { let id: i32 = row.get(0); @@ -128,23 +128,13 @@ fn send(db: &mut Client, rpc: &mut Rpc, state: &WireState) -> LoopResult<bool> { let wtid: [u8; 32] = sql_array(row, 2); let addr = sql_addr(row, 3); let url = sql_url(row, 4); - match rpc.withdraw(state.address, addr, amount, wtid, url) { - Ok(tx_id) => { - db.execute( - "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3", - &[&(TxStatus::Sent as i16), &tx_id.as_ref(), &id], - )?; - let amount = eth_to_taler(&amount); - info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr); - } - Err(e) => { - db.execute( - "UPDATE tx_out SET status=$1 WHERE id=$2", - &[&(TxStatus::Delayed as i16), &id], - )?; - Err(e)?; - } - } + let tx_id = rpc.withdraw(state.address, addr, amount, wtid, url)?; + db.execute( + "UPDATE tx_out SET status=$1, txid=$2 WHERE id=$3", + &[&(WithdrawStatus::Sent as i16), &tx_id.as_ref(), &id], + )?; + let amount = eth_to_taler(&amount); + info!(">> {} {} in {} to {}", amount, base32(&wtid), tx_id, addr); } Ok(row.is_some()) } diff --git a/eth-wire/src/main.rs b/eth-wire/src/main.rs @@ -16,20 +16,19 @@ use std::sync::atomic::AtomicU16; +use common::{ + config::{load_eth_config, EthConfig}, + postgres::{self, Client, NoTls}, +}; use eth_wire::{ rpc::{self, Rpc}, taler_util::eth_payto_addr, }; use ethereum_types::H160; use loops::{watcher::watcher, worker::worker}; -use common::{ - config::{load_eth_config, EthConfig}, - postgres::{self, Client, NoTls}, -}; -mod status; -mod sql; mod loops; +mod sql; pub struct WireState { confirmation: AtomicU16, @@ -74,7 +73,9 @@ fn main() { ) .unwrap(); - rpc_worker.unlock_account(&state.address, "password").unwrap(); + rpc_worker + .unlock_account(&state.address, "password") + .unwrap(); let rpc_watcher = Rpc::new( state diff --git a/eth-wire/src/status.rs b/eth-wire/src/status.rs @@ -1,69 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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/> -*/ -//! Transactions status in database - -/// Outgoing transaction status -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TxStatus { - /// Client have ask for a transaction (default) - Requested = 0, - /// The wire failed to send this transaction and will try later - Delayed = 1, - /// Transaction have been announced to the bitcoin network - Sent = 2, -} - -impl TryFrom<u8> for TxStatus { - type Error = (); - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - x if x == TxStatus::Requested as u8 => Ok(TxStatus::Requested), - x if x == TxStatus::Sent as u8 => Ok(TxStatus::Sent), - x if x == TxStatus::Delayed as u8 => Ok(TxStatus::Delayed), - _ => Err(()), - } - } -} - -/// Bounce transaction status -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BounceStatus { - /// Bounce have been requested (default) - Requested = 0, - /// The wire failed to send this bounce and will try later - Delayed = 1, - /// Bounce will not be sent (e.g: bounce amount smaller than bounce fee) - Ignored = 2, - /// Bounce have been announced to the bitcoin network - Sent = 3, -} - -impl TryFrom<u8> for BounceStatus { - type Error = (); - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - x if x == BounceStatus::Requested as u8 => Ok(BounceStatus::Requested), - x if x == BounceStatus::Sent as u8 => Ok(BounceStatus::Sent), - x if x == BounceStatus::Delayed as u8 => Ok(BounceStatus::Delayed), - x if x == BounceStatus::Ignored as u8 => Ok(BounceStatus::Ignored), - _ => Err(()), - } - } -} diff --git a/test/common.sh b/test/common.sh @@ -10,7 +10,7 @@ function cleanup() { for n in `jobs -p`; do kill $n &> /dev/null || true done - rm -rf $DIR &> /dev/null + rm -rf $DIR &> /dev/null || true wait }