@@ -859,6 +859,35 @@ pub fn handle_config_subcommand(
859859 wallet_opts : & WalletOpts ,
860860 force : bool ,
861861) -> Result < String , Error > {
862+ if network == Network :: Bitcoin {
863+ eprintln ! (
864+ "WARNING: You are configuring a wallet for Bitcoin MAINNET.\n \
865+ This software is experimental and not recommended for use with real funds.\n \
866+ Consider using a testnet for testing purposes. \n "
867+ ) ;
868+ }
869+
870+ let ext_descriptor = wallet_opts. ext_descriptor . clone ( ) ;
871+ let int_descriptor = wallet_opts. int_descriptor . clone ( ) ;
872+
873+ if ext_descriptor. contains ( "xprv" ) || ext_descriptor. contains ( "tprv" ) {
874+ eprintln ! (
875+ "WARNING: Your external descriptor contains PRIVATE KEYS.\n \
876+ Private keys will be saved in PLAINTEXT in the config file.\n \
877+ This is a security risk. Consider using public descriptors instead."
878+ ) ;
879+ }
880+
881+ if let Some ( ref internal_desc) = int_descriptor {
882+ if internal_desc. contains ( "xprv" ) || internal_desc. contains ( "tprv" ) {
883+ eprintln ! (
884+ "WARNING: Your internal descriptor contains PRIVATE KEYS.\n \
885+ Private keys will be saved in PLAINTEXT in the config file.\n \
886+ This is a security risk. Consider using public descriptors instead."
887+ ) ;
888+ }
889+ }
890+
862891 let mut config = WalletConfig :: load ( datadir) ?. unwrap_or ( WalletConfig {
863892 network,
864893 wallets : HashMap :: new ( ) ,
@@ -870,8 +899,6 @@ pub fn handle_config_subcommand(
870899 ) ) ) ;
871900 }
872901
873- let ext_descriptor = wallet_opts. ext_descriptor . clone ( ) ;
874- let int_descriptor = wallet_opts. int_descriptor . clone ( ) ;
875902 #[ cfg( any(
876903 feature = "electrum" ,
877904 feature = "esplora" ,
@@ -1143,9 +1170,141 @@ pub(crate) fn handle_compile_subcommand(
11431170 }
11441171}
11451172
1173+ /// Handle wallets command to show all saved wallet configurations
1174+ pub fn handle_wallets_subcommand ( datadir : & Path , pretty : bool ) -> Result < String , Error > {
1175+ let load_config = WalletConfig :: load ( datadir) ?;
1176+
1177+ let config = match load_config {
1178+ Some ( c) if !c. wallets . is_empty ( ) => c,
1179+ _ => {
1180+ return Ok ( if pretty {
1181+ "No wallet configurations found." . to_string ( )
1182+ } else {
1183+ serde_json:: to_string_pretty ( & json ! ( {
1184+ "wallets" : [ ]
1185+ } ) ) ?
1186+ } ) ;
1187+ }
1188+ } ;
1189+
1190+ if pretty {
1191+ let mut rows: Vec < Vec < CellStruct > > = vec ! [ ] ;
1192+
1193+ for ( name, wallet_config) in config. wallets . iter ( ) {
1194+ let mut row = vec ! [ name. cell( ) , wallet_config. network. clone( ) . cell( ) ] ;
1195+
1196+ #[ cfg( any( feature = "sqlite" , feature = "redb" ) ) ]
1197+ row. push ( wallet_config. database_type . clone ( ) . cell ( ) ) ;
1198+
1199+ #[ cfg( any(
1200+ feature = "electrum" ,
1201+ feature = "esplora" ,
1202+ feature = "rpc" ,
1203+ feature = "cbf"
1204+ ) ) ]
1205+ {
1206+ let client_str = wallet_config. client_type . as_deref ( ) . unwrap_or ( "N/A" ) ;
1207+ row. push ( client_str. cell ( ) ) ;
1208+ }
1209+
1210+ #[ cfg( any( feature = "electrum" , feature = "esplora" , feature = "rpc" ) ) ]
1211+ {
1212+ let url_str = wallet_config. server_url . as_deref ( ) . unwrap_or ( "N/A" ) ;
1213+ let display_url = if url_str. len ( ) > 20 {
1214+ shorten ( url_str, 15 , 10 )
1215+ } else {
1216+ url_str. to_string ( )
1217+ } ;
1218+ row. push ( display_url. cell ( ) ) ;
1219+ }
1220+
1221+ let ext_desc_display = if wallet_config. ext_descriptor . len ( ) > 40 {
1222+ shorten ( & wallet_config. ext_descriptor , 20 , 15 )
1223+ } else {
1224+ wallet_config. ext_descriptor . clone ( )
1225+ } ;
1226+ row. push ( ext_desc_display. cell ( ) ) ;
1227+
1228+ let has_int_desc = if wallet_config. int_descriptor . is_some ( ) {
1229+ "Yes"
1230+ } else {
1231+ "No"
1232+ } ;
1233+ row. push ( has_int_desc. cell ( ) ) ;
1234+
1235+ rows. push ( row) ;
1236+ }
1237+
1238+ let mut title_cells = vec ! [ "Wallet Name" . cell( ) . bold( true ) , "Network" . cell( ) . bold( true ) ] ;
1239+
1240+ #[ cfg( any( feature = "sqlite" , feature = "redb" ) ) ]
1241+ title_cells. push ( "Database" . cell ( ) . bold ( true ) ) ;
1242+
1243+ #[ cfg( any(
1244+ feature = "electrum" ,
1245+ feature = "esplora" ,
1246+ feature = "rpc" ,
1247+ feature = "cbf"
1248+ ) ) ]
1249+ title_cells. push ( "Client" . cell ( ) . bold ( true ) ) ;
1250+
1251+ #[ cfg( any( feature = "electrum" , feature = "esplora" , feature = "rpc" ) ) ]
1252+ title_cells. push ( "Server URL" . cell ( ) . bold ( true ) ) ;
1253+
1254+ title_cells. push ( "External Desc" . cell ( ) . bold ( true ) ) ;
1255+ title_cells. push ( "Internal Desc" . cell ( ) . bold ( true ) ) ;
1256+
1257+ let table = rows
1258+ . table ( )
1259+ . title ( title_cells)
1260+ . display ( )
1261+ . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
1262+
1263+ Ok ( format ! ( "{table}" ) )
1264+ } else {
1265+ let wallets_summary: Vec < _ > = config
1266+ . wallets
1267+ . iter ( )
1268+ . map ( |( name, wallet_config) | {
1269+ let mut wallet_json = json ! ( {
1270+ "name" : name,
1271+ "network" : wallet_config. network,
1272+ "ext_descriptor" : wallet_config. ext_descriptor,
1273+ "int_descriptor" : wallet_config. int_descriptor,
1274+ } ) ;
1275+
1276+ #[ cfg( any( feature = "sqlite" , feature = "redb" ) ) ]
1277+ {
1278+ wallet_json[ "database_type" ] = json ! ( wallet_config. database_type. clone( ) ) ;
1279+ }
1280+
1281+ #[ cfg( any(
1282+ feature = "electrum" ,
1283+ feature = "esplora" ,
1284+ feature = "rpc" ,
1285+ feature = "cbf"
1286+ ) ) ]
1287+ {
1288+ wallet_json[ "client_type" ] = json ! ( wallet_config. client_type. clone( ) ) ;
1289+ }
1290+
1291+ #[ cfg( any( feature = "electrum" , feature = "esplora" , feature = "rpc" ) ) ]
1292+ {
1293+ wallet_json[ "server_url" ] = json ! ( wallet_config. server_url. clone( ) ) ;
1294+ }
1295+
1296+ wallet_json
1297+ } )
1298+ . collect ( ) ;
1299+
1300+ Ok ( serde_json:: to_string_pretty ( & json ! ( {
1301+ "wallets" : wallets_summary
1302+ } ) ) ?)
1303+ }
1304+ }
1305+
11461306/// The global top level handler.
11471307pub ( crate ) async fn handle_command ( cli_opts : CliOpts ) -> Result < String , Error > {
1148- let network = cli_opts. network ;
11491308 let pretty = cli_opts. pretty ;
11501309 let subcommand = cli_opts. subcommand . clone ( ) ;
11511310
@@ -1162,9 +1321,8 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
11621321 } => {
11631322 let home_dir = prepare_home_dir ( cli_opts. datadir ) ?;
11641323
1165- let config = WalletConfig :: load ( & home_dir) ?
1166- . ok_or ( Error :: Generic ( "No config found" . to_string ( ) ) ) ?;
1167- let wallet_opts = config. get_wallet_opts ( & wallet) ?;
1324+ let ( wallet_opts, network) = load_wallet_config ( & home_dir, & wallet) ?;
1325+
11681326 let database_path = prepare_wallet_db_dir ( & home_dir, & wallet) ?;
11691327
11701328 #[ cfg( any( feature = "sqlite" , feature = "redb" ) ) ]
@@ -1220,13 +1378,10 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12201378 wallet : wallet_name,
12211379 subcommand : WalletSubCommand :: OfflineWalletSubCommand ( offline_subcommand) ,
12221380 } => {
1223- let network = cli_opts. network ;
12241381 let datadir = cli_opts. datadir . clone ( ) ;
12251382 let home_dir = prepare_home_dir ( datadir) ?;
1226- let config = WalletConfig :: load ( & home_dir) ?. ok_or ( Error :: Generic ( format ! (
1227- "No config found for wallet '{wallet_name}'"
1228- ) ) ) ?;
1229- let wallet_opts = config. get_wallet_opts ( & wallet_name) ?;
1383+ let ( wallet_opts, network) = load_wallet_config ( & home_dir, & wallet_name) ?;
1384+
12301385 #[ cfg( any( feature = "sqlite" , feature = "redb" ) ) ]
12311386 let result = {
12321387 let mut persister: Persister = match & wallet_opts. database_type {
@@ -1281,9 +1436,15 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12811436 let result = handle_config_subcommand ( & home_dir, network, wallet, & wallet_opts, force) ?;
12821437 Ok ( result)
12831438 }
1439+ CliSubCommand :: Wallets => {
1440+ let home_dir = prepare_home_dir ( cli_opts. datadir ) ?;
1441+ let result = handle_wallets_subcommand ( & home_dir, pretty) ?;
1442+ Ok ( result)
1443+ }
12841444 CliSubCommand :: Key {
12851445 subcommand : key_subcommand,
12861446 } => {
1447+ let network = cli_opts. network ;
12871448 let result = handle_key_subcommand ( network, key_subcommand, pretty) ?;
12881449 Ok ( result)
12891450 }
@@ -1292,27 +1453,20 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12921453 policy,
12931454 script_type,
12941455 } => {
1456+ let network = cli_opts. network ;
12951457 let result = handle_compile_subcommand ( network, policy, script_type, pretty) ?;
12961458 Ok ( result)
12971459 }
12981460 #[ cfg( feature = "repl" ) ]
12991461 CliSubCommand :: Repl {
13001462 wallet : wallet_name,
1301- mut wallet_opts,
13021463 } => {
1303- let network = cli_opts. network ;
13041464 let home_dir = prepare_home_dir ( cli_opts. datadir . clone ( ) ) ?;
1305- wallet_opts. wallet = Some ( wallet_name. clone ( ) ) ;
1306-
1307- let config = WalletConfig :: load ( & home_dir) ?. ok_or ( Error :: Generic ( format ! (
1308- "No config found for wallet {}" ,
1309- wallet_name. clone( )
1310- ) ) ) ?;
1311- let loaded_wallet_opts = config. get_wallet_opts ( & wallet_name) ?;
1465+ let ( wallet_opts, network) = load_wallet_config ( & home_dir, & wallet_name) ?;
13121466
13131467 #[ cfg( any( feature = "sqlite" , feature = "redb" ) ) ]
13141468 let ( mut wallet, mut persister) = {
1315- let mut persister: Persister = match & loaded_wallet_opts . database_type {
1469+ let mut persister: Persister = match & wallet_opts . database_type {
13161470 #[ cfg( feature = "sqlite" ) ]
13171471 DatabaseType :: Sqlite => {
13181472 let database_path = prepare_wallet_db_dir ( & home_dir, & wallet_name) ?;
@@ -1331,7 +1485,7 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
13311485 Persister :: RedbStore ( store)
13321486 }
13331487 } ;
1334- let wallet = new_persisted_wallet ( network, & mut persister, & loaded_wallet_opts ) ?;
1488+ let wallet = new_persisted_wallet ( network, & mut persister, & wallet_opts ) ?;
13351489 ( wallet, persister)
13361490 } ;
13371491 #[ cfg( not( any( feature = "sqlite" , feature = "redb" ) ) ) ]
0 commit comments