commit 8090f9ccf69dd88aafde6aaf6b01dc74a8bd4b4a
parent 5f43dd660186d631fcd05e505ee8fd2a5393a113
Author: Antoine A <>
Date: Mon, 27 Dec 2021 20:21:27 +0100
Parse bitcoin config file
Diffstat:
13 files changed, 144 insertions(+), 141 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -159,6 +159,7 @@ dependencies = [
"owo-colors",
"postgres",
"rand",
+ "rust-ini",
"serde",
"serde_json",
"serde_repr",
diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml
@@ -40,6 +40,7 @@ base64 = "0.13.0"
taler-api = { path = "../taler-api" }
taler-config = { path = "../taler-config" }
taler-log = { path = "../taler-log" }
+rust-ini = "0.17.0"
[dev-dependencies]
# statistics-driven micro-benchmarks
diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs
@@ -2,8 +2,9 @@ use std::path::PathBuf;
use bitcoin::{Address, Amount, Network};
use btc_wire::{
+ config::BitcoinConfig,
rpc::{BtcRpc, Error, ErrorCode},
- rpc_utils::{default_data_dir, rpc_dir},
+ rpc_utils::default_data_dir,
test::rand_key,
};
@@ -22,8 +23,6 @@ struct Args {
enum Cmd {
Transfer(TransferCmd),
NextBlock(NextBlockCmd),
- Init(InitCmd),
- Reset(ResetCmd),
}
#[derive(argh::FromArgs)]
@@ -56,33 +55,18 @@ struct NextBlockCmd {
to: String,
}
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "init")]
-/// Init test state
-struct InitCmd {}
-
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "reset")]
-/// Reset regtest
-struct ResetCmd {}
-
struct App {
- data_dir: PathBuf,
- network: Network,
+ config: BitcoinConfig,
client: BtcRpc,
}
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).unwrap();
+ let config = BitcoinConfig::load(data_dir).unwrap();
+ let client = BtcRpc::common(&config).unwrap();
- Self {
- data_dir,
- network,
- client,
- }
+ Self { config, client }
}
pub fn auto_wallet(&self, name: &str) -> (BtcRpc, Address) {
@@ -93,7 +77,7 @@ impl App {
e => Err(e).unwrap(),
}
}
- let wallet = BtcRpc::wallet(&self.data_dir, self.network, name).unwrap();
+ let wallet = BtcRpc::wallet(&self.config, name).unwrap();
let addr = wallet
.get_new_address()
.expect(&format!("Failed to get wallet address {}", name));
@@ -101,7 +85,7 @@ impl App {
}
pub fn next_block(&self, wallet: &str) {
- match self.network {
+ match self.config.network {
Network::Regtest => {
// Manually mine a block
let (_, addr) = self.auto_wallet(wallet);
@@ -113,31 +97,11 @@ impl App {
}
}
}
-
- pub fn init(&self) {
- for wallet in ["wire", "client", "reserve"] {
- self.client.create_wallet(wallet).ok();
- }
- if self.network == Network::Regtest {
- let (client, client_addr) = self.auto_wallet("client");
- client.generate(101, &client_addr).unwrap();
- }
- }
}
fn main() {
let args: Args = argh::from_env();
match args.cmd {
- Cmd::Init(_) => App::start(args.datadir).init(),
- Cmd::Reset(_) => {
- let path = args
- .datadir
- .unwrap_or_else(default_data_dir)
- .join(rpc_dir(Network::Regtest));
- if path.exists() {
- std::fs::remove_dir_all(path).unwrap();
- }
- }
Cmd::Transfer(TransferCmd {
key: _key,
from,
diff --git a/btc-wire/src/bin/test.rs b/btc-wire/src/bin/test.rs
@@ -3,8 +3,9 @@ use std::{collections::HashSet, iter::repeat_with, panic::AssertUnwindSafe};
use bitcoin::{Address, Amount, Network, Txid};
use btc_wire::{
+ config::BitcoinConfig,
rpc::{self, BtcRpc, Category},
- rpc_utils::{default_data_dir, rpc_dir, CLIENT, WIRE},
+ rpc_utils::{default_data_dir, CLIENT, WIRE},
test::rand_key,
BounceErr,
};
@@ -16,9 +17,9 @@ const RESERVE: &str = "reserve";
pub fn main() {
let test_amount = Amount::from_sat(1500);
let data_dir = default_data_dir();
+ let config = BitcoinConfig::load(&data_dir).unwrap();
// Network check
- let network = Network::Regtest;
- match network {
+ match config.network {
Network::Bitcoin => {
panic!("Do not run tests on the mainnet, you are going to loose money")
}
@@ -30,16 +31,15 @@ pub fn main() {
// Wallet check
{
- let existing_wallets: HashSet<String> =
- std::fs::read_dir(data_dir.join(rpc_dir(network)).join("wallets"))
- .unwrap()
- .filter_map(|it| it.ok())
- .map(|it| it.file_name().to_string_lossy().to_string())
- .collect();
+ let existing_wallets: HashSet<String> = std::fs::read_dir(config.dir.join("wallets"))
+ .unwrap()
+ .filter_map(|it| it.ok())
+ .map(|it| it.file_name().to_string_lossy().to_string())
+ .collect();
let wallets = [CLIENT, WIRE, RESERVE];
- let rpc = BtcRpc::common(&data_dir, network).unwrap();
+ let rpc = BtcRpc::common(&config).unwrap();
if !existing_wallets.contains(CLIENT)
|| !existing_wallets.contains(WIRE)
|| !existing_wallets.contains(RESERVE)
@@ -64,15 +64,15 @@ pub fn main() {
}
// Client initialization
- 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_rpc = BtcRpc::wallet(&config, CLIENT).unwrap();
+ let wire_rpc = BtcRpc::wallet(&config, WIRE).unwrap();
+ let reserve_rpc = BtcRpc::wallet(&config, 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();
let next_block = || {
- match network {
+ match config.network {
Network::Regtest => {
// Manually mine a block
reserve_rpc.generate(1, &reserve_addr).unwrap();
@@ -120,9 +120,9 @@ pub fn main() {
)
.yellow()
);
- match network {
+ match config.network {
Network::Bitcoin | Network::Testnet | Network::Signet => {
- if network == Network::Bitcoin {
+ if config.network == Network::Bitcoin {
println!("Send coins to this address: {}", client_addr);
} else {
println!("Request coins from a faucet such as https://bitcoinfaucet.uo1.net/send.php to this address: {}", client_addr);
@@ -296,9 +296,10 @@ pub fn main() {
/// Check a specific transaction exist in a wallet historic
fn tx_exist(rpc: &BtcRpc, id: &Txid, min_confirmation: i32, detail: Category) -> rpc::Result<bool> {
let result = rpc.list_since_block(None, 1, false).unwrap();
- let found = result.transactions.into_iter().any(|tx| {
- tx.category == detail && tx.confirmations >= min_confirmation && tx.txid == *id
- });
+ let found = result
+ .transactions
+ .into_iter()
+ .any(|tx| tx.category == detail && tx.confirmations >= min_confirmation && tx.txid == *id);
Ok(found)
}
diff --git a/btc-wire/src/config.rs b/btc-wire/src/config.rs
@@ -0,0 +1,77 @@
+use std::{
+ net::SocketAddr,
+ path::{Path, PathBuf},
+ process::exit,
+ str::FromStr,
+};
+
+use bitcoin::Network;
+use taler_log::log::error;
+
+fn rpc_dir(network: Network) -> &'static str {
+ match network {
+ Network::Bitcoin => "main",
+ Network::Testnet => "testnet3",
+ Network::Regtest => "regtest",
+ Network::Signet => "signet",
+ }
+}
+
+fn rpc_port(network: Network) -> u16 {
+ match network {
+ Network::Bitcoin => 8332,
+ Network::Testnet => 18332,
+ Network::Regtest => 18443,
+ Network::Signet => 38333,
+ }
+}
+
+#[derive(Clone)]
+pub struct BitcoinConfig {
+ pub network: Network,
+ pub dir: PathBuf,
+ pub addr: SocketAddr,
+}
+
+impl BitcoinConfig {
+ pub fn load(data_dir: impl AsRef<Path>) -> Result<Self, ini::Error> {
+ let conf = ini::Ini::load_from_file(data_dir.as_ref().join("bitcoin.conf"))?;
+
+ let section = conf.general_section();
+
+ if !section.contains_key("txindex") {
+ error!("btc_wire require a bitcoin core node running with 'txindex' option");
+ exit(1);
+ }
+
+ let network = if let Some("1") = section.get("testnet") {
+ Network::Testnet
+ } else if let Some("1") = section.get("signet") {
+ Network::Signet
+ } else if let Some("1") = section.get("regtest") {
+ Network::Regtest
+ } else {
+ Network::Bitcoin
+ };
+
+ let port = if let Some(addr) = section.get("rpcport") {
+ addr.parse()
+ .expect("bitcoin config value 'rpcport' is not a valid port number")
+ } else {
+ rpc_port(network)
+ };
+
+ let addr = if let Some(addr) = section.get("rpcbind") {
+ SocketAddr::from_str(addr)
+ .expect("bitcoin config value 'rpcbind' is not a valid socket address")
+ } else {
+ ([127, 0, 0, 1], port).into()
+ };
+
+ Ok(Self {
+ network,
+ addr,
+ dir: data_dir.as_ref().join(rpc_dir(network)),
+ })
+ }
+}
diff --git a/btc-wire/src/lib.rs b/btc-wire/src/lib.rs
@@ -9,6 +9,7 @@ pub mod rpc;
pub mod rpc_utils;
pub mod segwit;
pub mod test;
+pub mod config;
#[derive(Debug, thiserror::Error)]
pub enum BounceErr {
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
@@ -1,5 +1,6 @@
-use bitcoin::{hashes::Hash, Address, Amount as BtcAmount, BlockHash, Network, SignedAmount, Txid};
+use bitcoin::{hashes::Hash, Address, Amount as BtcAmount, BlockHash, SignedAmount, Txid};
use btc_wire::{
+ config::BitcoinConfig,
rpc::{BtcRpc, Category},
rpc_utils::{default_data_dir, sender_address},
segwit::DecodeSegWitErr,
@@ -9,7 +10,7 @@ use postgres::{fallible_iterator::FallibleIterator, Client, NoTls};
use rand::{rngs::OsRng, RngCore};
use std::{
collections::HashMap,
- path::{Path, PathBuf},
+ path::PathBuf,
str::FromStr,
time::{Duration, SystemTime},
};
@@ -123,34 +124,26 @@ mod test {
struct AutoReconnectRPC {
delay: Duration,
- data_dir: PathBuf,
+ config: BitcoinConfig,
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 {
+ pub fn new(config: BitcoinConfig, 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,
+ client: Self::connect(&config, &wallet, delay),
wallet,
delay,
- network,
+ config,
}
}
/// Connect a new client, loop on error
- fn connect(data_dir: &Path, network: Network, wallet: &str, delay: Duration) -> BtcRpc {
+ fn connect(config: &BitcoinConfig, wallet: &str, delay: Duration) -> BtcRpc {
loop {
- match BtcRpc::wallet(data_dir, network, wallet) {
+ match BtcRpc::wallet(config, wallet) {
Ok(new) => match new.net_info() {
Ok(_) => return new,
Err(err) => {
@@ -168,7 +161,7 @@ impl AutoReconnectRPC {
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);
+ self.client = Self::connect(&self.config, &self.wallet, self.delay);
}
&mut self.client
}
@@ -509,29 +502,15 @@ fn main() {
.unwrap_or_else(default_data_dir);
let config = taler_config::Config::from_path("test.conf");
let config: &'static Config = Box::leak(Box::new(config));
- let network = match config.btc_chain.as_str() {
- "main" => Network::Bitcoin,
- "test" => Network::Testnet,
- "regtest" => Network::Regtest,
- chain => {
- error!("Unsupported chain {}", chain);
- std::process::exit(1);
- }
- };
- let rpc = BtcRpc::common(&data_dir, network).unwrap();
+ let btc_config = BitcoinConfig::load(&data_dir).unwrap();
+ let rpc = BtcRpc::common(&btc_config).unwrap();
rpc.load_wallet(&config.btc_wallet).ok();
let rpc_listener = AutoReconnectRPC::new(
- &data_dir,
- network,
- &config.btc_wallet,
- Duration::from_secs(5),
- );
- let rpc_worker = AutoReconnectRPC::new(
- &data_dir,
- network,
+ btc_config.clone(),
&config.btc_wallet,
Duration::from_secs(5),
);
+ let rpc_worker = AutoReconnectRPC::new(btc_config, &config.btc_wallet, Duration::from_secs(5));
let db_listener = AutoReconnectSql::new(&config.db_url, Duration::from_secs(5));
let db_worker = AutoReconnectSql::new(&config.db_url, Duration::from_secs(5));
diff --git a/btc-wire/src/rpc.rs b/btc-wire/src/rpc.rs
@@ -1,15 +1,15 @@
-use crate::rpc_utils::{rpc_dir, rpc_url};
-use bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, Network, SignedAmount, Txid};
+use bitcoin::{hashes::hex::ToHex, Address, Amount, BlockHash, SignedAmount, Txid};
use serde_json::{json, Value};
use std::{
fmt::Debug,
io::{self, BufRead, BufReader, ErrorKind, Write},
net::{SocketAddr, TcpStream},
- path::Path,
sync::atomic::{AtomicU64, Ordering},
time::{Duration, Instant},
};
+use crate::config::BitcoinConfig;
+
// This is a very simple RPC client designed only for a specific bitcoincore version
// and to use on an secure localhost connection to a trusted node
@@ -55,24 +55,24 @@ pub struct BtcRpc {
}
impl BtcRpc {
- pub fn common(data_dir: &Path, network: Network) -> io::Result<Self> {
- Self::new(data_dir, rpc_url(network), "/".into(), network)
+ pub fn common(config: &BitcoinConfig) -> io::Result<Self> {
+ Self::new(config, None)
}
- pub fn wallet(data_dir: &Path, network: Network, wallet: &str) -> io::Result<Self> {
- Self::new(
- data_dir,
- rpc_url(network),
- format!("/wallet/{}", wallet),
- network,
- )
+ pub fn wallet(config: &BitcoinConfig, wallet: &str) -> io::Result<Self> {
+ Self::new(config, Some(wallet))
}
- fn new(data_dir: &Path, addr: SocketAddr, path: String, network: Network) -> io::Result<Self> {
- let cookie_path = data_dir.join(rpc_dir(network)).join(".cookie");
+ fn new(config: &BitcoinConfig, wallet: Option<&str>) -> io::Result<Self> {
+ let path = if let Some(wallet) = wallet {
+ format!("/wallet/{}", wallet)
+ } else {
+ String::from("/")
+ };
+ let cookie_path = config.dir.join(".cookie");
let cookie = std::fs::read(cookie_path)?;
Ok(Self {
- addr,
+ addr: config.addr,
path,
id: AtomicU64::new(0),
cookie: format!("Basic {}", base64::encode(&cookie)),
diff --git a/btc-wire/src/rpc_utils.rs b/btc-wire/src/rpc_utils.rs
@@ -1,31 +1,12 @@
-use std::{net::SocketAddr, path::PathBuf, str::FromStr};
+use std::{path::PathBuf, str::FromStr};
-use bitcoin::{Address, Amount, Network};
+use bitcoin::{Address, Amount};
use crate::rpc::{self, BtcRpc, TransactionFull};
pub const CLIENT: &str = "client";
pub const WIRE: &str = "wire";
-pub fn rpc_dir(network: Network) -> &'static str {
- match network {
- Network::Bitcoin => "main",
- Network::Testnet => "testnet3",
- Network::Regtest => "regtest",
- _ => unreachable!("Unsupported network"),
- }
-}
-
-pub fn rpc_url(network: Network) -> SocketAddr {
- let port = match network {
- Network::Bitcoin => 8332,
- Network::Testnet => 18332,
- Network::Regtest => 18443,
- _ => unreachable!("Unsupported network"),
- };
- ([127, 0, 0, 1], port).into()
-}
-
pub fn default_data_dir() -> PathBuf {
// https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md#:~:text=By%20default%2C%20the%20configuration%20file,file%3E%20option%20in%20the%20bitcoin.
if cfg!(target_os = "windows") {
diff --git a/script/setup.sh b/script/setup.sh
@@ -18,15 +18,18 @@ function reset_db() {
# Start a bitcoind regtest server in a temporary directory
function init_btc() {
BTC_DIR=$(mktemp -d)
+ echo "regtest=1" > $BTC_DIR/bitcoin.conf
+ echo "txindex=1" >> $BTC_DIR/bitcoin.conf
+ echo "fallbackfee=0.00000001" >> $BTC_DIR/bitcoin.conf
BTC_CLI="bitcoin-cli -regtest -datadir=$BTC_DIR"
- bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &> btc.log &
+ bitcoind -datadir=$BTC_DIR &> btc.log &
$BTC_CLI -rpcwait getnetworkinfo > /dev/null
}
# Start a bitcoind regest server in a previously created temporary directory and load wallets
function restart_btc() {
BTC_CLI="bitcoin-cli -regtest -datadir=$BTC_DIR"
- bitcoind -datadir=$BTC_DIR -txindex -regtest -fallbackfee=0.00000001 &>> btc.log &
+ bitcoind -datadir=$BTC_DIR -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/taler-config/Cargo.toml b/taler-config/Cargo.toml
@@ -3,8 +3,6 @@ name = "taler-config"
version = "0.1.0"
edition = "2021"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
# Url format
url = { version = "2.2.2", features = ["serde"] }
diff --git a/taler-config/src/lib.rs b/taler-config/src/lib.rs
@@ -9,7 +9,6 @@ pub struct Config {
pub payto: Url,
pub confirmation: u8,
pub btc_wallet: String,
- pub btc_chain: String,
pub bounce_fee: u64,
}
@@ -37,7 +36,6 @@ impl Config {
.parse()
.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")
diff --git a/test.conf b/test.conf
@@ -9,5 +9,4 @@ PAYTO = payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj
CONFIRMATION = 1
BTC_WALLET = wire
BTC_DATA_DIR = ~/.bitcoin
-BTC_CHAIN = regtest
BOUNCE_FEE = 0
\ No newline at end of file