Skip to content

Commit

Permalink
Require only one blockchain client feature at a time
Browse files Browse the repository at this point in the history
  • Loading branch information
notmandatory committed Aug 5, 2021
1 parent f3cd70c commit 47e0962
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 73 deletions.
1 change: 0 additions & 1 deletion .github/workflows/cont_integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ jobs:
- esplora
- compiler
- compact_filters
- repl,electrum,esplora,compiler
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ compact_filters = ["bdk/compact_filters"]
[[bin]]
name = "bdk-cli"
path = "src/bdk_cli.rs"
required-features = ["repl", "electrum"]
required-features = ["repl"]

[package.metadata.docs.rs]
features = ["esplora", "compiler"]
features = ["compiler"]
110 changes: 65 additions & 45 deletions src/bdk_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,25 @@ use structopt::StructOpt;

#[cfg(feature = "compact_filters")]
use bdk::blockchain::compact_filters::{BitcoinPeerConfig, CompactFiltersBlockchainConfig};
#[cfg(feature = "electrum")]
use bdk::blockchain::electrum::ElectrumBlockchainConfig;
#[cfg(feature = "esplora")]
use bdk::blockchain::esplora::EsploraBlockchainConfig;
use bdk::blockchain::{
AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig,
};

#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
use bdk::blockchain::{AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain};

use bdk::database::BatchDatabase;
use bdk::sled;
use bdk::sled::Tree;
use bdk::Wallet;
use bdk::{bitcoin, Error};
use bdk_cli::WalletSubCommand;
use bdk_cli::{
CliOpts, CliSubCommand, KeySubCommand, OfflineWalletSubCommand, OnlineWalletSubCommand,
WalletOpts,
};
use bdk_cli::{CliOpts, CliSubCommand, KeySubCommand, OfflineWalletSubCommand, WalletOpts};

#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
use bdk_cli::OnlineWalletSubCommand;

use regex::Regex;

const REPL_LINE_SPLIT_REGEX: &str = r#""([^"]*)"|'([^']*)'|([\w\-]+)"#;
Expand All @@ -59,6 +63,7 @@ const REPL_LINE_SPLIT_REGEX: &str = r#""([^"]*)"|'([^']*)'|([\w\-]+)"#;
version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
pub enum ReplSubCommand {
#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
#[structopt(flatten)]
OnlineWalletSubCommand(OnlineWalletSubCommand),
#[structopt(flatten)]
Expand Down Expand Up @@ -96,6 +101,7 @@ fn open_database(wallet_opts: &WalletOpts) -> Tree {
tree
}

#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
fn new_online_wallet<D>(
network: Network,
wallet_opts: &WalletOpts,
Expand All @@ -104,29 +110,40 @@ fn new_online_wallet<D>(
where
D: BatchDatabase,
{
// Try to use Esplora config if "esplora" feature is enabled
#[cfg(feature = "esplora")]
let config_esplora: Option<AnyBlockchainConfig> = {
let esplora_concurrency = wallet_opts.esplora_opts.esplora_concurrency;
wallet_opts.esplora_opts.esplora.clone().map(|base_url| {
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url,
concurrency: Some(esplora_concurrency),
})
})
};
#[cfg(not(feature = "esplora"))]
let config_esplora = None;

let config_electrum = AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
#[cfg(all(
feature = "electrum",
any(feature = "esplora", feature = "compact_filters")
))]
compile_error!("Only one blockchain client feature can be enabled at a time. The 'electrum' feature can not be enabled with 'esplora' or 'compact_filters'.");

#[cfg(feature = "electrum")]
let config = AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
url: wallet_opts.electrum_opts.electrum.clone(),
socks5: wallet_opts.proxy_opts.proxy.clone(),
retry: wallet_opts.proxy_opts.retries,
timeout: wallet_opts.electrum_opts.timeout,
});

#[cfg(all(
feature = "esplora",
any(feature = "electrum", feature = "compact_filters")
))]
compile_error!("Only one blockchain client feature can be enabled at a time. The 'esplora' feature can not be enabled with 'electrum' or 'compact_filters'.");

#[cfg(feature = "esplora")]
let config = AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: wallet_opts.esplora_opts.server.clone(),
concurrency: Some(wallet_opts.esplora_opts.concurrency),
});

#[cfg(all(
feature = "compact_filters",
any(feature = "electrum", feature = "esplora")
))]
compile_error!("Only one blockchain client feature can be enabled at a time. The 'esplora' feature can not be enabled with 'electrum' or 'compact_filters'.");

#[cfg(feature = "compact_filters")]
let config_compact_filters: Option<AnyBlockchainConfig> = {
let config = {
let mut peers = vec![];
for addrs in wallet_opts.compactfilter_opts.address.clone() {
for _ in 0..wallet_opts.compactfilter_opts.conn_count {
Expand All @@ -138,26 +155,17 @@ where
}
}

Some(AnyBlockchainConfig::CompactFilters(
CompactFiltersBlockchainConfig {
peers,
network,
storage_dir: prepare_home_dir().into_os_string().into_string().unwrap(),
skip_blocks: Some(wallet_opts.compactfilter_opts.skip_blocks),
},
))
AnyBlockchainConfig::CompactFilters(CompactFiltersBlockchainConfig {
peers,
network,
storage_dir: prepare_home_dir().into_os_string().into_string().unwrap(),
skip_blocks: Some(wallet_opts.compactfilter_opts.skip_blocks),
})
};

#[cfg(not(feature = "compact_filters"))]
let config_compact_filters = None;

// Fall back to Electrum config if Esplora or Compact Filter config isn't provided
let config = config_esplora
.or(config_compact_filters)
.unwrap_or(config_electrum);

let descriptor = wallet_opts.descriptor.as_str();
let change_descriptor = wallet_opts.change_descriptor.as_deref();

let wallet = Wallet::new(
descriptor,
change_descriptor,
Expand Down Expand Up @@ -206,6 +214,7 @@ fn main() {

fn handle_command(cli_opts: CliOpts, network: Network) -> Result<String, Error> {
let result = match cli_opts.subcommand {
#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
CliSubCommand::Wallet {
wallet_opts,
subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand),
Expand Down Expand Up @@ -244,7 +253,16 @@ fn handle_command(cli_opts: CliOpts, network: Network) -> Result<String, Error>
}
CliSubCommand::Repl { wallet_opts } => {
let database = open_database(&wallet_opts);
let online_wallet = new_online_wallet(network, &wallet_opts, database)?;

#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
let wallet = new_online_wallet(network, &wallet_opts, database)?;

#[cfg(not(any(
feature = "electrum",
feature = "esplora",
feature = "compact_filters"
)))]
let wallet = new_offline_wallet(network, &wallet_opts, database)?;

let mut rl = Editor::<()>::new();

Expand Down Expand Up @@ -285,15 +303,17 @@ fn handle_command(cli_opts: CliOpts, network: Network) -> Result<String, Error>
let repl_subcommand = repl_subcommand.unwrap();

let result = match repl_subcommand {
#[cfg(any(
feature = "electrum",
feature = "esplora",
feature = "compact_filters"
))]
ReplSubCommand::OnlineWalletSubCommand(online_subcommand) => {
bdk_cli::handle_online_wallet_subcommand(
&online_wallet,
online_subcommand,
)
bdk_cli::handle_online_wallet_subcommand(&wallet, online_subcommand)
}
ReplSubCommand::OfflineWalletSubCommand(offline_subcommand) => {
bdk_cli::handle_offline_wallet_subcommand(
&online_wallet,
&wallet,
&wallet_opts,
offline_subcommand,
)
Expand Down
62 changes: 37 additions & 25 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ use bdk::{FeeRate, KeychainKind, Wallet};
/// # Example
///
/// ```
/// # #[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
/// # {
/// # use bdk::bitcoin::Network;
/// # use structopt::StructOpt;
/// # use bdk_cli::{CliOpts, WalletOpts, CliSubCommand, WalletSubCommand};
Expand Down Expand Up @@ -174,8 +176,8 @@ use bdk::{FeeRate, KeychainKind, Wallet};
/// },
/// #[cfg(feature = "esplora")]
/// esplora_opts: EsploraOpts {
/// esplora: None,
/// esplora_concurrency: 4,
/// server: "https://blockstream.info/api/".to_string(),
/// concurrency: 4,
/// },
/// #[cfg(feature = "compact_filters")]
/// compactfilter_opts: CompactFilterOpts{
Expand All @@ -197,6 +199,7 @@ use bdk::{FeeRate, KeychainKind, Wallet};
/// };
///
/// assert_eq!(expected_cli_opts, cli_opts);
/// # }
/// ```
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(name = "BDK CLI",
Expand Down Expand Up @@ -268,6 +271,7 @@ pub enum CliSubCommand {
/// client and network connection and an [`OfflineWalletSubCommand`] does not.
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub enum WalletSubCommand {
#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
#[structopt(flatten)]
OnlineWalletSubCommand(OnlineWalletSubCommand),
#[structopt(flatten)]
Expand Down Expand Up @@ -315,8 +319,8 @@ pub enum WalletSubCommand {
/// },
/// #[cfg(feature = "esplora")]
/// esplora_opts: EsploraOpts {
/// esplora: None,
/// esplora_concurrency: 4,
/// server: "https://blockstream.info/api/".to_string(),
/// concurrency: 4,
/// },
/// #[cfg(feature = "compact_filters")]
/// compactfilter_opts: CompactFilterOpts{
Expand Down Expand Up @@ -431,7 +435,7 @@ pub struct ElectrumOpts {
pub timeout: Option<u8>,
/// Sets the Electrum server to use
#[structopt(
name = "SERVER:PORT",
name = "ELECTRUM_URL",
short = "s",
long = "server",
default_value = "ssl://electrum.blockstream.info:60002"
Expand All @@ -446,15 +450,20 @@ pub struct ElectrumOpts {
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct EsploraOpts {
/// Use the esplora server if given as parameter
#[structopt(name = "ESPLORA_URL", short = "e", long = "esplora")]
pub esplora: Option<String>,
#[structopt(
name = "ESPLORA_URL",
short = "s",
long = "server",
default_value = "https://blockstream.info/api/"
)]
pub server: String,
/// Concurrency of requests made to the esplora server
#[structopt(
name = "ESPLORA_CONCURRENCY",
long = "esplora_concurrency",
long = "concurrency",
default_value = "4"
)]
pub esplora_concurrency: u8,
pub concurrency: u8,
}

// This is a workaround for `structopt` issue #333, #391, #418; see https://github.com/TeXitoi/structopt/issues/333#issuecomment-712265332
Expand Down Expand Up @@ -1064,6 +1073,7 @@ mod test {
#[cfg(feature = "esplora")]
use crate::EsploraOpts;
use crate::OfflineWalletSubCommand::{CreateTx, GetNewAddress};
#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
use crate::OnlineWalletSubCommand::{Broadcast, Sync};
#[cfg(any(feature = "compact_filters", feature = "electrum"))]
use crate::ProxyOpts;
Expand Down Expand Up @@ -1098,8 +1108,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: None,
esplora_concurrency: 4
server: "https://blockstream.info/api/".to_string(),
concurrency: 4
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand Down Expand Up @@ -1148,8 +1158,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: None,
esplora_concurrency: 4,
server: "https://blockstream.info/api/".to_string(),
concurrency: 4,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand Down Expand Up @@ -1177,8 +1187,8 @@ mod test {
let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet",
"--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)",
"--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)",
"--esplora", "https://blockstream.info/api/",
"--esplora_concurrency", "5",
"--server", "https://blockstream.info/api/",
"--concurrency", "5",
"get_new_address"];

let cli_opts = CliOpts::from_iter(&cli_args);
Expand All @@ -1198,8 +1208,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: Some("https://blockstream.info/api/".to_string()),
esplora_concurrency: 5,
server: "https://blockstream.info/api/".to_string(),
concurrency: 5,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand Down Expand Up @@ -1251,8 +1261,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: None,
esplora_concurrency: 4,
server: "https://blockstream.info/api/".to_string(),
concurrency: 4,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand All @@ -1274,6 +1284,7 @@ mod test {
assert_eq!(expected_cli_opts, cli_opts);
}

#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
#[test]
fn test_parse_wallet_sync() {
let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet",
Expand All @@ -1297,8 +1308,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: None,
esplora_concurrency: 4,
server: "https://blockstream.info/api/".to_string(),
concurrency: 4,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand Down Expand Up @@ -1363,8 +1374,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: None,
esplora_concurrency: 4,
server: "https://blockstream.info/api/".to_string(),
concurrency: 4,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand Down Expand Up @@ -1396,6 +1407,7 @@ mod test {
assert_eq!(expected_cli_opts, cli_opts);
}

#[cfg(any(feature = "electrum", feature = "esplora", feature = "compact_filters"))]
#[test]
fn test_parse_wallet_broadcast() {
let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet",
Expand All @@ -1420,8 +1432,8 @@ mod test {
},
#[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
esplora: None,
esplora_concurrency: 4,
server: "https://blockstream.info/api/".to_string(),
concurrency: 4,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
Expand Down

0 comments on commit 47e0962

Please sign in to comment.