Skip to content

Commit 9549e09

Browse files
committed
feat(wallet): add initializing wallet config
- move init subcommand to use existing walletopts - add --use-config to use saved config - add fetching required params before clap parsing
1 parent 5d9feb0 commit 9549e09

File tree

7 files changed

+287
-155
lines changed

7 files changed

+287
-155
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
88
- Removed MSRV and bumped Rust Edition to 2024
99
- Add `--pretty` top level flag for formatting commands output in a tabular format
1010
- Updated `bdk_wallet ` to `2.1.0`, `bdk_bitcoind_rpc` to `0.21.0`, `bdk_esplora` to `0.22.1`, `bdk_kyoto` to `0.13.1`
11-
- Add wallet configs initialization for initialiazing and saving wallet configs
11+
- Add wallet subcommand `init` to save wallet configs
12+
- Add an optional flag `--use-config` to use saved configs for a wallet
1213

1314
## [1.0.0]
1415

README.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -203,24 +203,24 @@ cargo run --pretty -n signet wallet -w {wallet_name} -d sqlite balance
203203
```
204204
This is available for wallet, key, repl and compile features. When ommitted, outputs default to `JSON`.
205205

206-
### Initializing Wallet Configurations with `bdk-cli wallet init`
206+
## Initializing Wallet Configurations with `init` Subcommand
207207

208-
The `bdk-cli wallet init` command simplifies wallet setup by saving configuration parameters to `config.toml` in the data directory (default `~/.bdk-bitcoin/config.toml`). This allows you to run subsequent `bdk-cli` wallet commands without repeatedly specifying configuration details, easing wallet operations.
208+
The `wallet init` sub-command simplifies wallet operations by saving configuration parameters to `config.toml` in the data directory (default `~/.bdk-bitcoin/config.toml`). This allows you to run subsequent `bdk-cli wallet` commands without repeatedly specifying configuration details, easing wallet operations.
209209

210210
To initialize a wallet configuration, use the following command structure:
211211

212212
```shell
213-
cargo run --features <list-of-features> -- -n <network> wallet init --wallet <wallet_name> --ext-descriptor <ext_descriptor> --int-descriptor <int_descriptor> --client-type <client_type> --url <server_url> [--database-type <database_type>] [--rpc-user <rpc_user>]
214-
[--rpc-password <rpc_password>]
213+
cargo run --features <list-of-features> -- -n <network> wallet --wallet <wallet_name> --ext-descriptor <ext_descriptor> --int-descriptor <int_descriptor> --client-type <client_type> --url <server_url> [--database-type <database_type>] [--rpc-user <rpc_user>]
214+
[--rpc-password <rpc_password>] init
215215
```
216216

217217
For example, to initialize a wallet named `my_wallet` with `electrum` as the backend on `signet` network:
218218

219219
```shell
220-
cargo run --features electrum -- -n signet wallet init -w my_wallet -e "tr(tprv8Z.../0/*)#dtdqk3dx" -i "tr(tprv8Z.../1/*)#ulgptya7" -d sqlite -c electrum -u "ssl://mempool.space:60602"
220+
cargo run --features electrum -- -n signet wallet -w my_wallet -e "tr(tprv8Z.../0/*)#dtdqk3dx" -i "tr(tprv8Z.../1/*)#ulgptya7" -d sqlite -c electrum -u "ssl://mempool.space:60602" init
221221
```
222222

223-
To overwrite an existing wallet configuration, use the `--force` flag at the end of the command.
223+
To overwrite an existing wallet configuration, use the `--force` flag after the `init` sub-command.
224224

225225
You can omit the following arguments to use their default values:
226226

@@ -235,12 +235,9 @@ After a wallet is initialized, you can then run `bdk-cli` wallet commands withou
235235
For example, with the wallet `my_wallet` initialized, generate a new address and sync the wallet as follow:
236236

237237
```shell
238-
cargo run wallet -w my_wallet new_address
238+
cargo run wallet -w my_wallet --use-config new_address
239239

240-
cargo run --features electrum wallet -w my_wallet sync
240+
cargo run --features electrum wallet -w my_wallet --use-config sync
241241
```
242242

243243
Note that each wallet has its own configuration, allowing multiple wallets with different configurations.
244-
245-
* Each wallet has its own configuration, allowing multiple wallets with different settings (e.g., different descriptors or backends).
246-
* You can override saved configuration values for a single command by specifying them explicitly (e.g., `--client-type esplora` or `--url https://mempool.space/signet/api`).

src/commands.rs

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ pub enum CliSubCommand {
117117
pub enum WalletSubCommand {
118118
/// Initialize a wallet configuration and save to `config.toml`.
119119
Init {
120-
#[command(flatten)]
121-
wallet_opts: WalletOpts,
122120
/// Overwrite existing wallet configuration if it exists.
123121
#[arg(long = "force", default_value_t = false)]
124122
force: bool,
@@ -184,15 +182,15 @@ pub struct WalletOpts {
184182
feature = "rpc",
185183
feature = "cbf"
186184
))]
187-
#[arg(env = "CLIENT_TYPE", short = 'c', long, value_enum)]
188-
pub client_type: Option<ClientType>,
185+
#[arg(env = "CLIENT_TYPE", short = 'c', long, value_enum, required=true)]
186+
pub client_type: ClientType,
189187
#[cfg(any(feature = "sqlite", feature = "redb"))]
190-
#[arg(env = "DATABASE_TYPE", short = 'd', long, value_enum)]
191-
pub database_type: Option<DatabaseType>,
188+
#[arg(env = "DATABASE_TYPE", short = 'd', long, value_enum, required=true)]
189+
pub database_type: DatabaseType,
192190
/// Sets the server url.
193191
#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))]
194-
#[arg(env = "SERVER_URL", short = 'u', long)]
195-
pub url: Option<String>,
192+
#[arg(env = "SERVER_URL", short = 'u', long, required=true)]
193+
pub url: String,
196194
/// Electrum batch size.
197195
#[cfg(feature = "electrum")]
198196
#[arg(env = "ELECTRUM_BATCH_SIZE", short = 'b', long, default_value = "10")]
@@ -215,7 +213,7 @@ pub struct WalletOpts {
215213
value_parser = parse_proxy_auth,
216214
default_value = "user:password",
217215
)]
218-
pub basic_auth: Option<(String, String)>,
216+
pub basic_auth: (String, String),
219217
#[cfg(feature = "rpc")]
220218
/// Sets an optional cookie authentication.
221219
#[arg(env = "COOKIE")]
@@ -226,56 +224,48 @@ pub struct WalletOpts {
226224
}
227225

228226
impl WalletOpts {
229-
/// Load configuration from TOML file and merge with CLI options
227+
/// Merges optional configuration values from config.toml into the current WalletOpts.
230228
pub fn load_config(&mut self, wallet_name: &str, datadir: &Path) -> Result<(), Error> {
231229
if let Some(config) = WalletConfig::load(datadir)? {
232230
if let Ok(config_opts) = config.get_wallet_opts(wallet_name) {
233-
self.wallet = self.wallet.take().or(config_opts.wallet);
234231
self.verbose = self.verbose || config_opts.verbose;
235-
self.ext_descriptor = self.ext_descriptor.take().or(config_opts.ext_descriptor);
236-
self.int_descriptor = self.int_descriptor.take().or(config_opts.int_descriptor);
237-
#[cfg(any(
238-
feature = "electrum",
239-
feature = "esplora",
240-
feature = "rpc",
241-
feature = "cbf"
242-
))]
243-
{
244-
self.client_type = self.client_type.clone().or(config_opts.client_type);
245-
}
246-
#[cfg(any(feature = "sqlite", feature = "redb"))]
247-
{
248-
// prioritizing the config.toml value for database type as it has a default value
249-
self.database_type = config_opts.database_type.or(self.database_type.clone());
250-
}
251-
#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))]
252-
{
253-
self.url = self.url.take().or(config_opts.url);
254-
}
255232
#[cfg(feature = "electrum")]
256233
{
257234
self.batch_size = if self.batch_size != 10 {
258-
config_opts.batch_size
259-
} else {
260235
self.batch_size
236+
} else {
237+
config_opts.batch_size
261238
};
262239
}
263240
#[cfg(feature = "esplora")]
264241
{
265242
self.parallel_requests = if self.parallel_requests != 5 {
266-
config_opts.parallel_requests
267-
} else {
268243
self.parallel_requests
244+
} else {
245+
config_opts.parallel_requests
269246
};
270247
}
271248
#[cfg(feature = "rpc")]
272249
{
273-
self.basic_auth = self.basic_auth.take().or(config_opts.basic_auth);
250+
self.basic_auth = if self.basic_auth != ("user".into(), "password".into()) {
251+
self.basic_auth.clone()
252+
} else {
253+
config_opts.basic_auth
254+
};
274255
self.cookie = self.cookie.take().or(config_opts.cookie);
275256
}
276257
#[cfg(feature = "cbf")]
277258
{
278-
self.compactfilter_opts = config_opts.compactfilter_opts;
259+
if self.compactfilter_opts.conn_count == 2
260+
&& config_opts.compactfilter_opts.conn_count != 2
261+
{
262+
self.compactfilter_opts.conn_count =
263+
config_opts.compactfilter_opts.conn_count;
264+
}
265+
if self.compactfilter_opts.skip_blocks.is_none() {
266+
self.compactfilter_opts.skip_blocks =
267+
config_opts.compactfilter_opts.skip_blocks;
268+
}
279269
}
280270
}
281271
}

src/config.rs

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ pub struct WalletConfig {
2323

2424
#[derive(Debug, Serialize, Deserialize)]
2525
pub struct WalletConfigInner {
26-
pub name: String,
26+
pub wallet: String,
2727
pub network: String,
2828
pub ext_descriptor: String,
29-
pub int_descriptor: String,
30-
#[cfg(feature = "sqlite")]
29+
pub int_descriptor: Option<String>,
30+
#[cfg(any(feature = "sqlite", feature = "redb"))]
3131
pub database_type: String,
3232
#[cfg(any(
3333
feature = "electrum",
@@ -39,9 +39,15 @@ pub struct WalletConfigInner {
3939
#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))]
4040
pub server_url: Option<String>,
4141
#[cfg(feature = "rpc")]
42-
pub rpc_user: String,
42+
pub rpc_user: Option<String>,
4343
#[cfg(feature = "rpc")]
44-
pub rpc_password: String,
44+
pub rpc_password: Option<String>,
45+
#[cfg(feature = "electrum")]
46+
pub batch_size: Option<usize>,
47+
#[cfg(feature = "esplora")]
48+
pub parallel_requests: Option<usize>,
49+
#[cfg(feature = "rpc")]
50+
pub cookie: Option<String>,
4551
}
4652

4753
impl WalletConfig {
@@ -85,21 +91,18 @@ impl WalletConfig {
8591
"regtest" => Network::Regtest,
8692
"signet" => Network::Signet,
8793
_ => {
88-
return Err(Error::Generic(format!(
89-
"Invalid network: {network}",
90-
network = wallet_config.network
91-
)));
94+
return Err(Error::Generic("Invalid network".to_string()));
9295
}
9396
};
9497

95-
#[cfg(feature = "sqlite")]
98+
#[cfg(any(feature = "sqlite", feature = "redb"))]
9699
let database_type = match wallet_config.database_type.as_str() {
100+
#[cfg(feature = "sqlite")]
97101
"sqlite" => DatabaseType::Sqlite,
102+
#[cfg(feature = "redb")]
103+
"redb" => DatabaseType::Redb,
98104
_ => {
99-
return Err(Error::Generic(format!(
100-
"Invalid database type: {database_type}",
101-
database_type = wallet_config.database_type
102-
)));
105+
return Err(Error::Generic("Invalid database type".to_string()));
103106
}
104107
};
105108

@@ -111,44 +114,52 @@ impl WalletConfig {
111114
))]
112115
let client_type = match wallet_config.client_type.as_deref() {
113116
#[cfg(feature = "electrum")]
114-
Some("electrum") => Some(ClientType::Electrum),
117+
Some("electrum") => ClientType::Electrum,
115118
#[cfg(feature = "esplora")]
116-
Some("esplora") => Some(ClientType::Esplora),
119+
Some("esplora") => ClientType::Esplora,
117120
#[cfg(feature = "rpc")]
118-
Some("rpc") => Some(ClientType::Rpc),
121+
Some("rpc") => ClientType::Rpc,
119122
#[cfg(feature = "cbf")]
120-
Some("cbf") => Some(ClientType::Cbf),
121-
Some(other) => return Err(Error::Generic(format!("Invalid client type: {other}"))),
122-
None => None,
123+
Some("cbf") => ClientType::Cbf,
124+
_ => return Err(Error::Generic(format!("Invalid client type"))),
123125
};
124126

125127
Ok(WalletOpts {
126-
wallet: Some(wallet_config.name.clone()),
128+
wallet: Some(wallet_config.wallet.clone()),
127129
verbose: false,
128130
ext_descriptor: Some(wallet_config.ext_descriptor.clone()),
129-
int_descriptor: Some(wallet_config.int_descriptor.clone()),
131+
int_descriptor: wallet_config.int_descriptor.clone(),
130132
#[cfg(any(
131133
feature = "electrum",
132134
feature = "esplora",
133135
feature = "rpc",
134136
feature = "cbf"
135137
))]
136138
client_type,
137-
#[cfg(feature = "sqlite")]
138-
database_type: Some(database_type),
139+
#[cfg(any(feature = "sqlite", feature = "redb"))]
140+
database_type,
139141
#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))]
140-
url: wallet_config.server_url.clone(),
142+
url: wallet_config
143+
.server_url
144+
.clone()
145+
.ok_or_else(|| Error::Generic(format!("Server url not found")))?,
141146
#[cfg(feature = "electrum")]
142147
batch_size: 10,
143148
#[cfg(feature = "esplora")]
144149
parallel_requests: 5,
145150
#[cfg(feature = "rpc")]
146-
basic_auth: Some((
147-
wallet_config.rpc_user.clone(),
148-
wallet_config.rpc_password.clone(),
149-
)),
151+
basic_auth: (
152+
wallet_config
153+
.rpc_user
154+
.clone()
155+
.unwrap_or_else(|| "user".into()),
156+
wallet_config
157+
.rpc_password
158+
.clone()
159+
.unwrap_or_else(|| "password".into()),
160+
),
150161
#[cfg(feature = "rpc")]
151-
cookie: None,
162+
cookie: wallet_config.cookie.clone(),
152163
#[cfg(feature = "cbf")]
153164
compactfilter_opts: crate::commands::CompactFilterOpts {
154165
conn_count: 2,

0 commit comments

Comments
 (0)