Skip to content

Commit f3ee4ee

Browse files
committed
feat(init-wallet): rename init & add walletopts
- rename init to config and move walletopts as config options
1 parent f21729b commit f3ee4ee

File tree

4 files changed

+98
-153
lines changed

4 files changed

+98
-153
lines changed

src/commands.rs

Lines changed: 18 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,11 @@
1313
//! All subcommands are defined in the below enums.
1414
1515
#![allow(clippy::large_enum_variant)]
16-
use crate::config::WalletConfig;
17-
use crate::error::BDKCliError as Error;
1816
use bdk_wallet::bitcoin::{
1917
Address, Network, OutPoint, ScriptBuf,
2018
bip32::{DerivationPath, Xpriv},
2119
};
2220
use clap::{Args, Parser, Subcommand, ValueEnum, value_parser};
23-
use std::path::Path;
2421

2522
#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))]
2623
use crate::utils::parse_proxy_auth;
@@ -72,8 +69,10 @@ pub enum CliSubCommand {
7269
/// needs backend like `sync` and `broadcast`, compile the binary with specific backend feature
7370
/// and use the configuration options below to configure for that backend.
7471
Wallet {
75-
#[command(flatten)]
76-
wallet_opts: WalletOpts,
72+
/// Selects the wallet to use.
73+
#[arg(env = "WALLET_NAME", short = 'w', long = "wallet", required = true)]
74+
wallet: String,
75+
7776
#[command(subcommand)]
7877
subcommand: WalletSubCommand,
7978
},
@@ -106,6 +105,10 @@ pub enum CliSubCommand {
106105
/// REPL command loop can be used to make recurring callbacks to an already loaded wallet.
107106
/// This mode is useful for hands on live testing of wallet operations.
108107
Repl {
108+
/// Wallet name for this REPL session
109+
#[arg(env = "WALLET_NAME", short = 'w', long = "wallet", required = true)]
110+
wallet: String,
111+
109112
#[command(flatten)]
110113
wallet_opts: WalletOpts,
111114
},
@@ -129,11 +132,14 @@ pub enum CliSubCommand {
129132
/// Wallet operation subcommands.
130133
#[derive(Debug, Subcommand, Clone, PartialEq)]
131134
pub enum WalletSubCommand {
132-
/// Initialize a wallet configuration and save to `config.toml`.
133-
Init {
135+
/// Save wallet configuration to `config.toml`.
136+
Config {
134137
/// Overwrite existing wallet configuration if it exists.
135-
#[arg(long = "force", default_value_t = false)]
138+
#[arg(short = 'f', long = "force", default_value_t = false)]
136139
force: bool,
140+
141+
#[command(flatten)]
142+
wallet_opts: WalletOpts,
137143
},
138144
#[cfg(any(
139145
feature = "electrum",
@@ -179,14 +185,15 @@ pub enum ClientType {
179185
#[derive(Debug, Args, Clone, PartialEq, Eq)]
180186
pub struct WalletOpts {
181187
/// Selects the wallet to use.
182-
#[arg(env = "WALLET_NAME", short = 'w', long = "wallet")]
188+
#[arg(skip)]
183189
pub wallet: Option<String>,
190+
// #[arg(env = "WALLET_NAME", short = 'w', long = "wallet", required = true)]
184191
/// Adds verbosity, returns PSBT in JSON format alongside serialized, displays expanded objects.
185192
#[arg(env = "VERBOSE", short = 'v', long = "verbose")]
186193
pub verbose: bool,
187194
/// Sets the descriptor to use for the external addresses.
188-
#[arg(env = "EXT_DESCRIPTOR", short = 'e', long)]
189-
pub ext_descriptor: Option<String>,
195+
#[arg(env = "EXT_DESCRIPTOR", short = 'e', long, required = true)]
196+
pub ext_descriptor: String,
190197
/// Sets the descriptor to use for internal/change addresses.
191198
#[arg(env = "INT_DESCRIPTOR", short = 'i', long)]
192199
pub int_descriptor: Option<String>,
@@ -237,56 +244,6 @@ pub struct WalletOpts {
237244
pub compactfilter_opts: CompactFilterOpts,
238245
}
239246

240-
impl WalletOpts {
241-
/// Merges optional configuration values from config.toml into the current WalletOpts.
242-
pub fn load_config(&mut self, wallet_name: &str, datadir: &Path) -> Result<(), Error> {
243-
if let Some(config) = WalletConfig::load(datadir)? {
244-
if let Ok(config_opts) = config.get_wallet_opts(wallet_name) {
245-
self.verbose = self.verbose || config_opts.verbose;
246-
#[cfg(feature = "electrum")]
247-
{
248-
self.batch_size = if self.batch_size != 10 {
249-
self.batch_size
250-
} else {
251-
config_opts.batch_size
252-
};
253-
}
254-
#[cfg(feature = "esplora")]
255-
{
256-
self.parallel_requests = if self.parallel_requests != 5 {
257-
self.parallel_requests
258-
} else {
259-
config_opts.parallel_requests
260-
};
261-
}
262-
#[cfg(feature = "rpc")]
263-
{
264-
self.basic_auth = if self.basic_auth != ("user".into(), "password".into()) {
265-
self.basic_auth.clone()
266-
} else {
267-
config_opts.basic_auth
268-
};
269-
self.cookie = self.cookie.take().or(config_opts.cookie);
270-
}
271-
#[cfg(feature = "cbf")]
272-
{
273-
if self.compactfilter_opts.conn_count == 2
274-
&& config_opts.compactfilter_opts.conn_count != 2
275-
{
276-
self.compactfilter_opts.conn_count =
277-
config_opts.compactfilter_opts.conn_count;
278-
}
279-
if self.compactfilter_opts.skip_blocks.is_none() {
280-
self.compactfilter_opts.skip_blocks =
281-
config_opts.compactfilter_opts.skip_blocks;
282-
}
283-
}
284-
}
285-
}
286-
Ok(())
287-
}
288-
}
289-
290247
/// Options to configure a SOCKS5 proxy for a blockchain client connection.
291248
#[cfg(any(feature = "electrum", feature = "esplora"))]
292249
#[derive(Debug, Args, Clone, PartialEq, Eq)]

src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl WalletConfig {
127127
Ok(WalletOpts {
128128
wallet: Some(wallet_config.wallet.clone()),
129129
verbose: false,
130-
ext_descriptor: Some(wallet_config.ext_descriptor.clone()),
130+
ext_descriptor: wallet_config.ext_descriptor.clone(),
131131
int_descriptor: wallet_config.int_descriptor.clone(),
132132
#[cfg(any(
133133
feature = "electrum",

src/handlers.rs

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -742,33 +742,26 @@ pub(crate) async fn handle_online_wallet_subcommand(
742742
}
743743
}
744744

745-
/// Handle wallet init subcommand to create or update config.toml
746-
pub fn handle_init_subcommand(
745+
/// Handle wallet config subcommand to create or update config.toml
746+
pub fn handle_config_subcommand(
747747
datadir: &Path,
748748
network: Network,
749+
wallet: String,
749750
wallet_opts: &WalletOpts,
750751
force: bool,
751752
) -> Result<String, Error> {
752-
let wallet_name = wallet_opts
753-
.wallet
754-
.as_ref()
755-
.ok_or_else(|| Error::Generic("Wallet name is required".to_string()))?;
756-
757753
let mut config = WalletConfig::load(datadir)?.unwrap_or(WalletConfig {
758754
network,
759755
wallets: HashMap::new(),
760756
});
761757

762-
if config.wallets.contains_key(wallet_name) && !force {
758+
if config.wallets.contains_key(&wallet) && !force {
763759
return Err(Error::Generic(format!(
764-
"Wallet '{wallet_name}' already exists in config.toml. Use --force to overwrite."
760+
"Wallet '{wallet}' already exists in config.toml. Use --force to overwrite."
765761
)));
766762
}
767763

768-
let ext_descriptor = wallet_opts
769-
.ext_descriptor
770-
.clone()
771-
.ok_or_else(|| Error::Generic("External descriptor is required".to_string()))?;
764+
let ext_descriptor = wallet_opts.ext_descriptor.clone();
772765
let int_descriptor = wallet_opts.int_descriptor.clone();
773766
#[cfg(any(
774767
feature = "electrum",
@@ -805,7 +798,7 @@ pub fn handle_init_subcommand(
805798
};
806799

807800
let wallet_config = WalletConfigInner {
808-
wallet: wallet_name.to_string(),
801+
wallet: wallet.clone(),
809802
network: network.to_string(),
810803
ext_descriptor,
811804
int_descriptor,
@@ -833,13 +826,11 @@ pub fn handle_init_subcommand(
833826
};
834827

835828
config.network = network;
836-
config
837-
.wallets
838-
.insert(wallet_name.to_string(), wallet_config);
829+
config.wallets.insert(wallet.clone(), wallet_config);
839830
config.save(datadir)?;
840831

841832
Ok(serde_json::to_string_pretty(&json!({
842-
"message": format!("Wallet '{wallet_name}' initialized successfully in {:?}", datadir.join("config.toml"))
833+
"message": format!("Wallet '{wallet}' initialized successfully in {:?}", datadir.join("config.toml"))
843834
}))?)
844835
}
845836

@@ -1057,11 +1048,15 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
10571048
feature = "rpc"
10581049
))]
10591050
CliSubCommand::Wallet {
1060-
mut wallet_opts,
1051+
wallet,
10611052
subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand),
10621053
} => {
10631054
let home_dir = prepare_home_dir(cli_opts.datadir)?;
1064-
let database_path = prepare_wallet_db_dir(&home_dir, &mut wallet_opts)?;
1055+
1056+
let config = WalletConfig::load(&home_dir)?
1057+
.ok_or(Error::Generic("No config found".to_string()))?;
1058+
let wallet_opts = config.get_wallet_opts(&wallet)?;
1059+
let database_path = prepare_wallet_db_dir(&home_dir, &wallet)?;
10651060

10661061
#[cfg(any(feature = "sqlite", feature = "redb"))]
10671062
let result = {
@@ -1111,34 +1106,33 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
11111106
Ok(result)
11121107
}
11131108
CliSubCommand::Wallet {
1114-
mut wallet_opts,
1109+
wallet: wallet_name,
11151110
subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand),
11161111
} => {
11171112
let network = cli_opts.network;
11181113
let datadir = cli_opts.datadir.clone();
1114+
let home_dir = prepare_home_dir(datadir)?;
1115+
let config = WalletConfig::load(&home_dir)?.ok_or(Error::Generic(format!(
1116+
"No config found for wallet '{wallet_name}'"
1117+
)))?;
1118+
let wallet_opts = config.get_wallet_opts(&wallet_name)?;
11191119
#[cfg(any(feature = "sqlite", feature = "redb"))]
11201120
let result = {
1121-
let home_dir = prepare_home_dir(datadir)?;
11221121
let mut persister: Persister = match &wallet_opts.database_type {
11231122
#[cfg(feature = "sqlite")]
11241123
DatabaseType::Sqlite => {
1125-
let database_path = prepare_wallet_db_dir(&home_dir, &mut wallet_opts)?;
1124+
let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?;
11261125
let db_file = database_path.join("wallet.sqlite");
11271126
let connection = Connection::open(db_file)?;
11281127
log::debug!("Sqlite database opened successfully");
11291128
Persister::Connection(connection)
11301129
}
11311130
#[cfg(feature = "redb")]
11321131
DatabaseType::Redb => {
1133-
let wallet_name = &wallet_opts.wallet;
1134-
11351132
let db = Arc::new(bdk_redb::redb::Database::create(
11361133
home_dir.join("wallet.redb"),
11371134
)?);
1138-
let store = RedbStore::new(
1139-
db,
1140-
wallet_name.as_deref().unwrap_or("wallet").to_string(),
1141-
)?;
1135+
let store = RedbStore::new(db, wallet_name)?;
11421136
log::debug!("Redb database opened successfully");
11431137
Persister::RedbStore(store)
11441138
}
@@ -1168,12 +1162,12 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
11681162
Ok(result)
11691163
}
11701164
CliSubCommand::Wallet {
1171-
wallet_opts,
1172-
subcommand: WalletSubCommand::Init { force },
1165+
wallet,
1166+
subcommand: WalletSubCommand::Config { force, wallet_opts },
11731167
} => {
11741168
let network = cli_opts.network;
11751169
let home_dir = prepare_home_dir(cli_opts.datadir)?;
1176-
let result = handle_init_subcommand(&home_dir, network, &wallet_opts, force)?;
1170+
let result = handle_config_subcommand(&home_dir, network, wallet, &wallet_opts, force)?;
11771171
Ok(result)
11781172
}
11791173
CliSubCommand::Key {
@@ -1191,43 +1185,48 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
11911185
Ok(result)
11921186
}
11931187
#[cfg(feature = "repl")]
1194-
CliSubCommand::Repl { ref wallet_opts } => {
1188+
CliSubCommand::Repl {
1189+
wallet: wallet_name,
1190+
mut wallet_opts,
1191+
} => {
11951192
let network = cli_opts.network;
1193+
let home_dir = prepare_home_dir(cli_opts.datadir.clone())?;
1194+
wallet_opts.wallet = Some(wallet_name.clone());
1195+
1196+
let config = WalletConfig::load(&home_dir)?.ok_or(Error::Generic(format!(
1197+
"No config found for wallet {}",
1198+
wallet_name.clone()
1199+
)))?;
1200+
let loaded_wallet_opts = config.get_wallet_opts(&wallet_name)?;
1201+
11961202
#[cfg(any(feature = "sqlite", feature = "redb"))]
11971203
let (mut wallet, mut persister) = {
1198-
let home_dir = prepare_home_dir(cli_opts.datadir.clone())?;
1199-
1200-
let mut persister: Persister = match &wallet_opts.database_type {
1204+
let mut persister: Persister = match &loaded_wallet_opts.database_type {
12011205
#[cfg(feature = "sqlite")]
12021206
DatabaseType::Sqlite => {
1203-
let database_path =
1204-
prepare_wallet_db_dir(&home_dir, &mut wallet_opts.clone())?;
1207+
let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?;
12051208
let db_file = database_path.join("wallet.sqlite");
12061209
let connection = Connection::open(db_file)?;
12071210
log::debug!("Sqlite database opened successfully");
12081211
Persister::Connection(connection)
12091212
}
12101213
#[cfg(feature = "redb")]
12111214
DatabaseType::Redb => {
1212-
let wallet_name = &wallet_opts.wallet;
12131215
let db = Arc::new(bdk_redb::redb::Database::create(
12141216
home_dir.join("wallet.redb"),
12151217
)?);
1216-
let store = RedbStore::new(
1217-
db,
1218-
wallet_name.as_deref().unwrap_or("wallet").to_string(),
1219-
)?;
1218+
let store = RedbStore::new(db, wallet_name.clone())?;
12201219
log::debug!("Redb database opened successfully");
12211220
Persister::RedbStore(store)
12221221
}
12231222
};
1224-
let wallet = new_persisted_wallet(network, &mut persister, wallet_opts)?;
1223+
let wallet = new_persisted_wallet(network, &mut persister, &loaded_wallet_opts)?;
12251224
(wallet, persister)
12261225
};
12271226
#[cfg(not(any(feature = "sqlite", feature = "redb")))]
1228-
let mut wallet = new_wallet(network, &wallet_opts)?;
1227+
let mut wallet = new_wallet(network, &loaded_wallet_opts)?;
12291228
let home_dir = prepare_home_dir(cli_opts.datadir.clone())?;
1230-
let database_path = prepare_wallet_db_dir(&home_dir, &mut wallet_opts.clone())?;
1229+
let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?;
12311230
loop {
12321231
let line = readline()?;
12331232
let line = line.trim();
@@ -1238,7 +1237,8 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12381237
let result = respond(
12391238
network,
12401239
&mut wallet,
1241-
wallet_opts,
1240+
&wallet_name,
1241+
&mut wallet_opts.clone(),
12421242
line,
12431243
database_path.clone(),
12441244
&cli_opts,
@@ -1276,7 +1276,8 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12761276
async fn respond(
12771277
network: Network,
12781278
wallet: &mut Wallet,
1279-
wallet_opts: &WalletOpts,
1279+
wallet_name: &String,
1280+
wallet_opts: &mut WalletOpts,
12801281
line: &str,
12811282
_datadir: std::path::PathBuf,
12821283
cli_opts: &CliOpts,
@@ -1311,10 +1312,16 @@ async fn respond(
13111312
Some(value)
13121313
}
13131314
ReplSubCommand::Wallet {
1314-
subcommand: WalletSubCommand::Init { force },
1315+
subcommand: WalletSubCommand::Config { force, wallet_opts },
13151316
} => {
1316-
let value = handle_init_subcommand(&_datadir, network, wallet_opts, force)
1317-
.map_err(|e| e.to_string())?;
1317+
let value = handle_config_subcommand(
1318+
&_datadir,
1319+
network,
1320+
wallet_name.to_string(),
1321+
&wallet_opts,
1322+
force,
1323+
)
1324+
.map_err(|e| e.to_string())?;
13181325
Some(value)
13191326
}
13201327
ReplSubCommand::Key { subcommand } => {

0 commit comments

Comments
 (0)