commit e3ac7f84d0c8e134c67d93a401c271dd89f1ac6f
parent fa6b17a6f311fef35c92b03643356cf1522e2bc4
Author: Antoine A <>
Date: Thu, 23 Dec 2021 10:50:50 +0100
Many small improvements and fix
Diffstat:
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", ¶ms)
+ }
+
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?;