depolymerization

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

commit e3ac7f84d0c8e134c67d93a401c271dd89f1ac6f
parent fa6b17a6f311fef35c92b03643356cf1522e2bc4
Author: Antoine A <>
Date:   Thu, 23 Dec 2021 10:50:50 +0100

Many small improvements and fix

Diffstat:
MCargo.lock | 69++++++++++++++++++++++++++-------------------------------------------
Mbtc-wire/src/bin/btc-wire-cli.rs | 13+++++++++----
Mbtc-wire/src/bin/test.rs | 8++++----
Mbtc-wire/src/main.rs | 119++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mbtc-wire/src/rpc.rs | 102+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mscript/setup.sh | 4++--
Mscript/test_btc_stress.sh | 3++-
Mtaler-config/src/lib.rs | 6++++++
Mtest.conf | 5+++--
Mwire-gateway/db/schema.sql | 19+++++++++++++------
Mwire-gateway/src/main.rs | 4++--
11 files changed, 223 insertions(+), 129 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -460,9 +460,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" dependencies = [ "instant", ] @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" dependencies = [ "futures-channel", "futures-core", @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" dependencies = [ "futures-core", "futures-sink", @@ -524,15 +524,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" [[package]] name = "futures-executor" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" dependencies = [ "futures-core", "futures-task", @@ -541,18 +541,16 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" [[package]] name = "futures-macro" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" dependencies = [ - "autocfg", - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -560,23 +558,22 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" [[package]] name = "futures-task" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" [[package]] name = "futures-util" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" dependencies = [ - "autocfg", "futures-channel", "futures-core", "futures-io", @@ -586,8 +583,6 @@ dependencies = [ "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] @@ -895,9 +890,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", @@ -1070,18 +1065,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] name = "proc-macro2" version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1328,9 +1311,9 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" [[package]] name = "serde" -version = "1.0.131" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" +checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008" dependencies = [ "serde_derive", ] @@ -1347,9 +1330,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.131" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" +checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276" dependencies = [ "proc-macro2", "quote", diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use bitcoin::{Address, Amount, Network}; use btc_wire::{ - rpc::BtcRpc, + rpc::{BtcRpc, Error, ErrorCode}, rpc_utils::{default_data_dir, rpc_dir}, test::rand_key, }; @@ -76,7 +76,7 @@ impl App { pub fn start(data_dir: Option<PathBuf>) -> Self { let data_dir = data_dir.unwrap_or_else(default_data_dir); let network = Network::Regtest; - let client = BtcRpc::common(&data_dir, network); + let client = BtcRpc::common(&data_dir, network).unwrap(); Self { data_dir, @@ -87,8 +87,13 @@ impl App { pub fn auto_wallet(&self, name: &str) -> (BtcRpc, Address) { // Auto load - self.client.load_wallet(name).ok(); - let wallet = BtcRpc::wallet(&self.data_dir, self.network, name); + if let Err(err) = self.client.load_wallet(name) { + match err { + Error::RPC { code, .. } if code == ErrorCode::RpcWalletAlreadyLoaded => {} + e => Err(e).unwrap(), + } + } + let wallet = BtcRpc::wallet(&self.data_dir, self.network, name).unwrap(); let addr = wallet .get_new_address() .expect(&format!("Failed to get wallet address {}", name)); diff --git a/btc-wire/src/bin/test.rs b/btc-wire/src/bin/test.rs @@ -39,7 +39,7 @@ pub fn main() { let wallets = [CLIENT, WIRE, RESERVE]; - let rpc = BtcRpc::common(&data_dir, network); + let rpc = BtcRpc::common(&data_dir, network).unwrap(); if !existing_wallets.contains(CLIENT) || !existing_wallets.contains(WIRE) || !existing_wallets.contains(RESERVE) @@ -64,9 +64,9 @@ pub fn main() { } // Client initialization - let client_rpc = BtcRpc::wallet(&data_dir, network, CLIENT); - let wire_rpc = BtcRpc::wallet(&data_dir, network, WIRE); - let reserve_rpc = BtcRpc::wallet(&data_dir, network, RESERVE); + let client_rpc = BtcRpc::wallet(&data_dir, network, CLIENT).unwrap(); + let wire_rpc = BtcRpc::wallet(&data_dir, network, WIRE).unwrap(); + let reserve_rpc = BtcRpc::wallet(&data_dir, network, RESERVE).unwrap(); let client_addr = client_rpc.get_new_address().unwrap(); let wire_addr = wire_rpc.get_new_address().unwrap(); let reserve_addr = reserve_rpc.get_new_address().unwrap(); diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs @@ -3,13 +3,13 @@ use btc_wire::{ rpc::{BtcRpc, Category}, rpc_utils::{default_data_dir, sender_address}, segwit::DecodeSegWitErr, - GetOpReturnErr, GetSegwitErr, + BounceErr, GetOpReturnErr, GetSegwitErr, }; use postgres::{fallible_iterator::FallibleIterator, Client, NoTls}; use rand::{rngs::OsRng, RngCore}; use std::{ collections::HashMap, - path::PathBuf, + path::{Path, PathBuf}, str::FromStr, time::{Duration, SystemTime}, }; @@ -124,13 +124,66 @@ mod test { } } -struct AutoReloadDb { +struct AutoReconnectRPC { + delay: Duration, + data_dir: PathBuf, + wallet: String, + client: BtcRpc, + network: Network, +} + +impl AutoReconnectRPC { + pub fn new( + data_dir: impl Into<PathBuf>, + network: Network, + wallet: impl Into<String>, + delay: Duration, + ) -> Self { + let wallet: String = wallet.into(); + let data_dir: PathBuf = data_dir.into(); + Self { + client: Self::connect(&data_dir, network, &wallet, delay), + data_dir, + wallet, + delay, + network, + } + } + + /// Connect a new client, loop on error + fn connect(data_dir: &Path, network: Network, wallet: &str, delay: Duration) -> BtcRpc { + loop { + match BtcRpc::wallet(data_dir, network, wallet) { + Ok(new) => match new.net_info() { + Ok(_) => return new, + Err(err) => { + error!("connect: DB - {}", err); + std::thread::sleep(delay); + }, + }, + Err(err) => { + error!("connect: DB - {}", err); + std::thread::sleep(delay); + } + } + } + } + + pub fn client(&mut self) -> &mut BtcRpc { + if self.client.net_info().is_err() { + self.client = Self::connect(&self.data_dir, self.network, &self.wallet, self.delay); + } + &mut self.client + } +} + +struct AutoReconnectSql { delay: Duration, config: String, client: Client, } -impl AutoReloadDb { +impl AutoReconnectSql { pub fn new(config: impl Into<String>, delay: Duration) -> Self { let config: String = config.into(); Self { @@ -146,7 +199,7 @@ impl AutoReloadDb { match Client::connect(config, NoTls) { Ok(new) => return new, Err(err) => { - error!("connect: DB - {}", err); + error!("connect: RPC - {}", err); std::thread::sleep(delay); } } @@ -168,7 +221,7 @@ fn last_hash(db: &mut Client) -> Result<Option<BlockHash>, postgres::Error> { } /// Listen for new proposed transactions and announce them on the bitcoin network -fn sender(rpc: BtcRpc, mut db: AutoReloadDb, _config: &Config) { +fn sender(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, _config: &Config) { // List all transactions with the given status fn list_status(db: &mut Client, status: Status) -> Result<Vec<i32>, postgres::Error> { let mut iter = @@ -243,6 +296,7 @@ fn sender(rpc: BtcRpc, mut db: AutoReloadDb, _config: &Config) { let mut failed = false; loop { + let rpc = rpc.client(); let db = db.client(); let result: Result<(), Box<dyn std::error::Error>> = (|| { // Listen to all channels @@ -327,8 +381,10 @@ fn sender(rpc: BtcRpc, mut db: AutoReloadDb, _config: &Config) { } /// Listen for mined block and index confirmed transactions into the database -fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { +fn watcher(mut rpc: AutoReconnectRPC, mut db: AutoReconnectSql, config: &Config) { + let mut init = false; loop { + let rpc = rpc.client(); let db = db.client(); let result: Result<(), Box<dyn std::error::Error>> = (|| { @@ -419,7 +475,32 @@ fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { GetSegwitErr::Decode( DecodeSegWitErr::MissingSegWitAddress | DecodeSegWitErr::NoMagicIdMatch, - ) => {} + ) => { + // Skip debounce while getting the database in sync to prevent double spent + if !init && false { + let nb = db.execute("INSERT INTO bounce (bounced) VALUES ($1) ON CONFLICT (bounced) DO NOTHING", &[&id.as_ref()])?; + // Check if already bounced + if nb > 0 && false { + // We do not handle failures, bouncing is done in a best effort manner + match rpc + .bounce(&id, &BtcAmount::from_sat(config.bounce_fee)) + { + Ok(it) => { + info!("bounce {} in {}", &id, &it); + db.execute( + "UPDATE bounce SET txid = $1 WHERE bounced = $2", + &[&it.as_ref(), &id.as_ref()], + )?; + } + Err(err) => match err { + BounceErr::AmountLessThanFee => { /* Ignore */ } + BounceErr::NotAReceiveTransaction + | BounceErr::RPC(_) => Err(err)?, + }, + } + } + } + } err => warn!("receive: {} {}", id, err), }, }, @@ -458,6 +539,8 @@ fn watcher(rpc: BtcRpc, mut db: AutoReloadDb, config: &Config) { })(); if let Err(e) = result { error!("watcher: DB - {}", e); + } else { + init = true; } info!("watcher: Wait for block"); @@ -487,13 +570,23 @@ fn main() { std::process::exit(1); } }; - let rpc = BtcRpc::common(&data_dir, network); + let rpc = BtcRpc::common(&data_dir, network).unwrap(); rpc.load_wallet(&config.btc_wallet).ok(); - let rpc_watcher = BtcRpc::wallet(&data_dir, network, &config.btc_wallet); - let rpc_sender = BtcRpc::wallet(&data_dir, network, &config.btc_wallet); + let rpc_watcher = AutoReconnectRPC::new( + &data_dir, + network, + &config.btc_wallet, + Duration::from_secs(5), + ); + let rpc_sender = AutoReconnectRPC::new( + &data_dir, + network, + &config.btc_wallet, + Duration::from_secs(5), + ); - let db_watcher = AutoReloadDb::new(&config.db_url, Duration::from_secs(5)); - let db_sender = AutoReloadDb::new(&config.db_url, Duration::from_secs(5)); + let db_watcher = AutoReconnectSql::new(&config.db_url, Duration::from_secs(5)); + let db_sender = AutoReconnectSql::new(&config.db_url, Duration::from_secs(5)); let join = std::thread::spawn(move || sender(rpc_sender, db_sender, config)); watcher(rpc_watcher, db_watcher, config); join.join().unwrap(); diff --git a/btc-wire/src/rpc.rs b/btc-wire/src/rpc.rs @@ -3,6 +3,7 @@ use bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, Network, SignedAmo use serde_json::{json, Value}; use std::{ fmt::Debug, + io, path::Path, sync::atomic::{AtomicU64, Ordering}, time::Duration, @@ -37,7 +38,7 @@ pub enum Error { Transport(#[from] ureq::Transport), #[error("{code:?} - {msg}")] RPC { code: ErrorCode, msg: String }, - #[error(transparent)] + #[error("JSON: {0}")] Json(#[from] serde_json::Error), } @@ -51,40 +52,33 @@ pub struct BtcRpc { } impl BtcRpc { - pub fn common(data_dir: &Path, network: Network) -> Self { - let path = data_dir.join(rpc_dir(network)).join(".cookie"); - - let cookie = std::fs::read(path).unwrap(); - let agent = ureq::builder() - .redirects(0) - .timeout_connect(Duration::from_secs(5)) - .timeout_write(Duration::from_secs(5)) - .timeout_read(Duration::from_secs(5)) - .build(); - Self { - path: rpc_url(network), - agent, - id: AtomicU64::new(0), - cookie: format!("Basic {}", base64::encode(&cookie)), - } + pub fn common(data_dir: &Path, network: Network) -> io::Result<Self> { + Self::new(data_dir, rpc_url(network), network) } - pub fn wallet(data_dir: &Path, network: Network, wallet: &str) -> Self { - let path = data_dir.join(rpc_dir(network)).join(".cookie"); + pub fn wallet(data_dir: &Path, network: Network, wallet: &str) -> io::Result<Self> { + Self::new( + data_dir, + format!("{}/wallet/{}", rpc_url(network), wallet), + network, + ) + } - let cookie = std::fs::read(path).unwrap(); + fn new(data_dir: &Path, path: String, network: Network) -> io::Result<Self> { + let cookie_path = data_dir.join(rpc_dir(network)).join(".cookie"); + let cookie = std::fs::read(cookie_path)?; let agent = ureq::builder() .redirects(0) .timeout_connect(Duration::from_secs(5)) .timeout_write(Duration::from_secs(5)) .timeout_read(Duration::from_secs(5)) .build(); - Self { - path: format!("{}/wallet/{}", rpc_url(network), wallet), + Ok(Self { + path, agent, id: AtomicU64::new(0), cookie: format!("Basic {}", base64::encode(&cookie)), - } + }) } fn call<T>(&self, method: &str, params: &impl serde::Serialize) -> Result<T> @@ -123,6 +117,11 @@ impl BtcRpc { } } + pub fn net_info(&self) -> Result<Empty> { + let params: [(); 0] = []; + self.call("getnetworkinfo", &params) + } + pub fn load_wallet(&self, name: &str) -> Result<Wallet> { self.call("loadwallet", &[name]) } @@ -139,7 +138,7 @@ impl BtcRpc { self.call("generatetoaddress", &(nb, address)) } - pub fn wait_for_new_block(&self, timeout: u64) -> Result<()> { + pub fn wait_for_new_block(&self, timeout: u64) -> Result<Empty> { self.call("waitfornewblock", &[timeout]) } @@ -173,34 +172,30 @@ impl BtcRpc { outputs: impl IntoIterator<Item = (&'b Address, &'c Amount)>, data: Option<&[u8]>, ) -> Result<Txid> { - let hex: String = self - .call( - "createrawtransaction", - &[ - Value::Array( - inputs - .into_iter() - .enumerate() - .map(|(i, id)| json!({"txid": id.to_string(), "vout": i})) - .collect(), - ), - Value::Array({ - let mut vec: Vec<Value> = outputs - .into_iter() - .map(|(addr, amount)| json!({&addr.to_string(): amount.as_btc()})) - .collect(); - if let Some(data) = data { - vec.push(json!({ "data".to_string(): data.to_hex() })); - } - vec - }), - ], - ) - .unwrap(); - let funded: HexWrapper = self.call("fundrawtransaction", &[hex]).unwrap(); - let signed: HexWrapper = self - .call("signrawtransactionwithwallet", &[&funded.hex]) - .unwrap(); + let hex: String = self.call( + "createrawtransaction", + &[ + Value::Array( + inputs + .into_iter() + .enumerate() + .map(|(i, id)| json!({"txid": id.to_string(), "vout": i})) + .collect(), + ), + Value::Array({ + let mut vec: Vec<Value> = outputs + .into_iter() + .map(|(addr, amount)| json!({&addr.to_string(): amount.as_btc()})) + .collect(); + if let Some(data) = data { + vec.push(json!({ "data".to_string(): data.to_hex() })); + } + vec + }), + ], + )?; + let funded: HexWrapper = self.call("fundrawtransaction", &[hex])?; + let signed: HexWrapper = self.call("signrawtransactionwithwallet", &[&funded.hex])?; self.call("sendrawtransaction", &[&signed.hex]) } @@ -317,6 +312,9 @@ pub struct HexWrapper { pub hex: String, } +#[derive(Debug, serde::Deserialize)] +pub struct Empty {} + /// Bitcoin RPC error codes <https://github.com/bitcoin/bitcoin/blob/master/src/rpc/protocol.h> #[derive(Debug, Clone, Copy, PartialEq, Eq, serde_repr::Deserialize_repr)] #[repr(i32)] diff --git a/script/setup.sh b/script/setup.sh @@ -19,14 +19,14 @@ function reset_db() { function init_btc() { BTC_DIR=$(mktemp -d) BTC_CLI="bitcoin-cli -regtest -datadir=$BTC_DIR" - bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &> /dev/null & + bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &> btc.log & $BTC_CLI -rpcwait getnetworkinfo > /dev/null } # Start a bitcoind regest server in a previously created temprorary directory and load wallets function restart_btc() { BTC_CLI="bitcoin-cli -regtest -datadir=$BTC_DIR" - bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &> /dev/null & + bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &>> btc.log & $BTC_CLI -rpcwait getnetworkinfo > /dev/null for wallet in wire client reserve; do $BTC_CLI loadwallet $wallet > /dev/null diff --git a/script/test_btc_stress.sh b/script/test_btc_stress.sh @@ -48,7 +48,7 @@ for n in `$SEQ`; do done sleep 3 # Give time for btc_wire watcher to process next_btc # Confirm all transactions -sleep 3 # Give time for btc_wire watcher to process +sleep 5 # Give time for btc_wire watcher to process echo " OK" echo -n "Requesting exchange incoming transaction list:" @@ -67,6 +67,7 @@ for n in `$SEQ`; do -b $BANK_ENDPOINT \ -C payto://bitcoin/$CLIENT \ -a BTC:0.0000$n > /dev/null + mine_btc done sleep 10 # Give time for btc_wire sender to process next_btc # Mine transactions diff --git a/taler-config/src/lib.rs b/taler-config/src/lib.rs @@ -10,6 +10,7 @@ pub struct Config { pub confirmation: u8, pub btc_wallet: String, pub btc_chain: String, + pub bounce_fee: u64, } impl Config { @@ -37,6 +38,11 @@ impl Config { .expect("Config CONFIRMATION is not a number"), btc_wallet: self_conf.get("BTC_WALLET").unwrap_or("wire").to_string(), btc_chain: self_conf.get("BTC_CHAIN").unwrap_or("regtest").to_string(), + bounce_fee: self_conf + .get("BOUNCE_FEE") + .unwrap_or("0") + .parse() + .expect("Config BOUNCE_FEE is not a number"), } } } diff --git a/test.conf b/test.conf @@ -9,4 +9,5 @@ PAYTO = payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj CONFIRMATION = 1 BTC_WALLET = wire BTC_DATA_DIR = ~/.bitcoin -BTC_CHAIN = regtest -\ No newline at end of file +BTC_CHAIN = regtest +BOUNCE_FEE = 0 +\ No newline at end of file diff --git a/wire-gateway/db/schema.sql b/wire-gateway/db/schema.sql @@ -1,4 +1,4 @@ -DROP TABLE IF EXISTS state, tx_in, tx_out; +DROP TABLE IF EXISTS state, tx_in, tx_out, bounce; -- Key value state CREATE TABLE state ( @@ -9,7 +9,7 @@ CREATE TABLE state ( -- Incoming transactions CREATE TABLE tx_in ( id SERIAL PRIMARY KEY, - _date TIMESTAMP NOT NULL, + _date TIMESTAMP NOT NULL DEFAULT now(), amount TEXT NOT NULL, reserve_pub BYTEA NOT NULL UNIQUE, debit_acc TEXT NOT NULL, @@ -19,13 +19,20 @@ CREATE TABLE tx_in ( -- Outgoing transactions CREATE TABLE tx_out ( id SERIAL PRIMARY KEY, - _date TIMESTAMP NOT NULL, + _date TIMESTAMP NOT NULL DEFAULT now(), amount TEXT NOT NULL, wtid BYTEA NOT NULL UNIQUE, debit_acc TEXT NOT NULL, credit_acc TEXT NOT NULL, exchange_url TEXT NOT NULL, - status SMALLINT NOT NULL, + status SMALLINT NOT NULL DEFAULT 0, txid BYTEA UNIQUE, request_uid BYTEA NOT NULL UNIQUE -); -\ No newline at end of file +); + +-- Bounced transaction +CREATE TABLE bounce ( + bounced BYTEA UNIQUE NOT NULL, + txid BYTEA UNIQUE, + _date TIMESTAMP NOT NULL DEFAULT now() +) +\ No newline at end of file diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs @@ -229,8 +229,8 @@ async fn router( let timestamp = Timestamp::now(); let tx = db.transaction().await?; - let row = tx.query_one("INSERT INTO tx_out (_date, amount, wtid, debit_acc, credit_acc, exchange_url, status, request_uid) VALUES (now(), $1, $2, $3, $4, $5, $6, $7) RETURNING id", &[ - &request.amount.to_string(), &request.wtid.as_ref(), &state.config.payto.as_ref(), &request.credit_account.as_ref(), &request.exchange_base_url.as_ref(), &0i16, &request.request_uid.as_ref() + let row = tx.query_one("INSERT INTO tx_out (amount, wtid, debit_acc, credit_acc, exchange_url, request_uid) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", &[ + &request.amount.to_string(), &request.wtid.as_ref(), &state.config.payto.as_ref(), &request.credit_account.as_ref(), &request.exchange_base_url.as_ref(), &request.request_uid.as_ref() ]).await?; tx.execute("NOTIFY new_tx", &[]).await?; tx.commit().await?;