@@ -21,6 +21,8 @@ use bdk_redb::Store as RedbStore;
2121use bdk_wallet:: bip39:: { Language , Mnemonic } ;
2222use bdk_wallet:: bitcoin:: base64:: Engine ;
2323use bdk_wallet:: bitcoin:: base64:: prelude:: BASE64_STANDARD ;
24+ #[ cfg( feature = "hwi" ) ]
25+ use bdk_wallet:: bitcoin:: hex:: DisplayHex ;
2426use bdk_wallet:: bitcoin:: {
2527 Address , Amount , FeeRate , Network , Psbt , Sequence , Txid ,
2628 bip32:: { DerivationPath , KeySource } ,
@@ -29,8 +31,6 @@ use bdk_wallet::bitcoin::{
2931 secp256k1:: Secp256k1 ,
3032} ;
3133use bdk_wallet:: chain:: ChainPosition ;
32- #[ cfg( feature = "hwi" ) ]
33- use bdk_wallet:: bitcoin:: hex:: DisplayHex ;
3434use bdk_wallet:: descriptor:: Segwitv0 ;
3535use bdk_wallet:: keys:: {
3636 DerivableKey , DescriptorKey , DescriptorKey :: Secret , ExtendedKey , GeneratableKey , GeneratedKey ,
@@ -88,7 +88,7 @@ const NUMS_UNSPENDABLE_KEY_HEX: &str =
8888/// Execute an offline wallet sub-command
8989///
9090/// Offline wallet sub-commands are described in [`OfflineWalletSubCommand`].
91- pub async fn handle_offline_wallet_subcommand (
91+ pub fn handle_offline_wallet_subcommand (
9292 wallet : & mut Wallet ,
9393 wallet_opts : & WalletOpts ,
9494 cli_opts : & CliOpts ,
@@ -582,56 +582,6 @@ pub async fn handle_offline_wallet_subcommand(
582582 & json ! ( { "psbt" : BASE64_STANDARD . encode( final_psbt. serialize( ) ) } ) ,
583583 ) ?)
584584 }
585- #[ cfg( feature = "hwi" ) ]
586- Hwi { subcommand } => match subcommand {
587- HwiSubCommand :: Devices => {
588- let device = crate :: utils:: connect_to_hardware_wallet (
589- wallet. network ( ) ,
590- wallet_opts,
591- Some ( wallet) ,
592- )
593- . await ?;
594- let device = if let Some ( device) = device {
595- json ! ( {
596- "type" : device. device_kind( ) . to_string( ) ,
597- "fingerprint" : device. get_master_fingerprint( ) . await ?. to_string( ) ,
598- "model" : device. device_kind( ) . to_string( ) ,
599- } )
600- } else {
601- json ! ( null)
602- } ;
603- Ok ( json ! ( { "devices" : device } ) )
604- }
605- HwiSubCommand :: Register => {
606- let policy = wallet_opts. ext_descriptor . clone ( ) . ok_or_else ( || {
607- Error :: Generic (
608- "External descriptor required for wallet registration" . to_string ( ) ,
609- )
610- } ) ?;
611- let wallet_name = wallet_opts. wallet . clone ( ) . ok_or_else ( || {
612- Error :: Generic ( "Wallet name is required for wallet registration" . to_string ( ) )
613- } ) ?;
614-
615- let device = crate :: utils:: connect_to_hardware_wallet (
616- wallet. network ( ) ,
617- wallet_opts,
618- Some ( wallet) ,
619- )
620- . await ?;
621- let hmac = if let Some ( device) = device {
622- let hmac = device. register_wallet ( & wallet_name, & policy) . await ?;
623- hmac. map ( |h| h. to_lower_hex_string ( ) )
624- } else {
625- None
626- } ;
627- //TODO: return status of wallet registration
628- Ok ( json ! ( { "hmac" : hmac } ) )
629- }
630- HwiSubCommand :: Address => {
631- let address = wallet. next_unused_address ( KeychainKind :: External ) ;
632- Ok ( json ! ( { "address" : address. address } ) )
633- }
634- } ,
635585 }
636586}
637587
@@ -1096,6 +1046,138 @@ pub(crate) fn handle_compile_subcommand(
10961046 }
10971047}
10981048
1049+ /// Handle hardware wallet operations
1050+ #[ cfg( feature = "hwi" ) ]
1051+ pub async fn handle_hwi_subcommand (
1052+ network : Network ,
1053+ wallet_opts : & WalletOpts ,
1054+ subcommand : HwiSubCommand ,
1055+ ) -> Result < serde_json:: Value , Error > {
1056+ match subcommand {
1057+ HwiSubCommand :: Devices => {
1058+ let devices = crate :: utils:: connect_to_hardware_wallet (
1059+ wallet. network ( ) ,
1060+ wallet_opts,
1061+ Some ( wallet) ,
1062+ )
1063+ . await ?;
1064+ let device = if let Some ( device) = device {
1065+ json ! ( {
1066+ "type" : device. device_kind( ) . to_string( ) ,
1067+ "fingerprint" : device. get_master_fingerprint( ) . await ?. to_string( ) ,
1068+ "model" : device. device_kind( ) . to_string( ) ,
1069+ } )
1070+ } else {
1071+ json ! ( null)
1072+ } ;
1073+ Ok ( json ! ( { "devices" : device } ) )
1074+ }
1075+ HwiSubCommand :: Register => {
1076+ let policy = wallet_opts. ext_descriptor . clone ( ) . ok_or_else ( || {
1077+ Error :: Generic ( "External descriptor required for wallet registration" . to_string ( ) )
1078+ } ) ?;
1079+ let wallet_name = wallet_opts. wallet . clone ( ) . ok_or_else ( || {
1080+ Error :: Generic ( "Wallet name is required for wallet registration" . to_string ( ) )
1081+ } ) ?;
1082+
1083+ let home_dir = prepare_home_dir ( None ) ?;
1084+ let database_path = prepare_wallet_db_dir ( & wallet_opts. wallet , & home_dir) ?;
1085+ #[ cfg( feature = "sqlite" ) ]
1086+ let wallet = {
1087+ let mut persister = match & wallet_opts. database_type {
1088+ DatabaseType :: Sqlite => {
1089+ let db_file = database_path. join ( "wallet.sqlite" ) ;
1090+ let connection = Connection :: open ( db_file) ?;
1091+ log:: debug!( "Sqlite database opened successfully" ) ;
1092+ connection
1093+ }
1094+ } ;
1095+ let mut wallet = new_persisted_wallet ( network, & mut persister, wallet_opts) ?;
1096+ wallet. persist ( & mut persister) ?;
1097+ wallet
1098+ } ;
1099+ #[ cfg( not( feature = "sqlite" ) ) ]
1100+ let wallet = new_wallet ( network, wallet_opts) ?;
1101+
1102+ let device = crate :: utils:: connect_to_hardware_wallet (
1103+ wallet. network ( ) ,
1104+ wallet_opts,
1105+ Some ( wallet) ,
1106+ )
1107+ . await ?;
1108+ let hmac = if let Some ( device) = device {
1109+ let hmac = device. register_wallet ( & wallet_name, & policy) . await ?;
1110+ hmac. map ( |h| h. to_lower_hex_string ( ) )
1111+ } else {
1112+ None
1113+ } ;
1114+ Ok ( json ! ( { "hmac" : hmac } ) )
1115+ }
1116+ HwiSubCommand :: Address => {
1117+ let home_dir = prepare_home_dir ( None ) ?;
1118+ let database_path = prepare_wallet_db_dir ( & wallet_opts. wallet , & home_dir) ?;
1119+ #[ cfg( feature = "sqlite" ) ]
1120+ let wallet = {
1121+ let mut persister = match & wallet_opts. database_type {
1122+ DatabaseType :: Sqlite => {
1123+ let db_file = database_path. join ( "wallet.sqlite" ) ;
1124+ let connection = Connection :: open ( db_file) ?;
1125+ log:: debug!( "Sqlite database opened successfully" ) ;
1126+ connection
1127+ }
1128+ } ;
1129+ let mut wallet = new_persisted_wallet ( network, & mut persister, wallet_opts) ?;
1130+ wallet. persist ( & mut persister) ?;
1131+ wallet
1132+ } ;
1133+ #[ cfg( not( feature = "sqlite" ) ) ]
1134+ let wallet = new_wallet ( network, wallet_opts) ?;
1135+
1136+ let address = wallet. next_unused_address ( KeychainKind :: External ) ;
1137+ Ok ( json ! ( { "address" : address. address } ) )
1138+ }
1139+ HwiSubCommand :: Sign { psbt } => {
1140+ let home_dir = prepare_home_dir ( None ) ?;
1141+ let database_path = prepare_wallet_db_dir ( & wallet_opts. wallet , & home_dir) ?;
1142+ #[ cfg( feature = "sqlite" ) ]
1143+ let wallet = {
1144+ let mut persister = match & wallet_opts. database_type {
1145+ DatabaseType :: Sqlite => {
1146+ let db_file = database_path. join ( "wallet.sqlite" ) ;
1147+ let connection = Connection :: open ( db_file) ?;
1148+ log:: debug!( "Sqlite database opened successfully" ) ;
1149+ connection
1150+ }
1151+ } ;
1152+ let mut wallet = new_persisted_wallet ( network, & mut persister, wallet_opts) ?;
1153+ wallet. persist ( & mut persister) ?;
1154+ wallet
1155+ } ;
1156+ #[ cfg( not( feature = "sqlite" ) ) ]
1157+ let wallet = new_wallet ( network, wallet_opts) ?;
1158+
1159+ let mut psbt = Psbt :: from_str ( & psbt)
1160+ . map_err ( |e| Error :: Generic ( format ! ( "Failed to parse PSBT: {e}" ) ) ) ?;
1161+ let device = crate :: utils:: connect_to_hardware_wallet (
1162+ wallet. network ( ) ,
1163+ wallet_opts,
1164+ Some ( wallet) ,
1165+ )
1166+ . await ?;
1167+ let signed_psbt = if let Some ( device) = device {
1168+ device
1169+ . sign_tx ( & mut psbt)
1170+ . await
1171+ . map_err ( |e| Error :: Generic ( format ! ( "Failed to sign PSBT: {e}" ) ) ) ?;
1172+ Some ( psbt. to_string ( ) )
1173+ } else {
1174+ None
1175+ } ;
1176+ Ok ( json ! ( { "psbt" : signed_psbt } ) )
1177+ }
1178+ }
1179+ }
1180+
10991181/// The global top level handler.
11001182pub ( crate ) async fn handle_command ( cli_opts : CliOpts ) -> Result < String , Error > {
11011183 let network = cli_opts. network ;
@@ -1197,9 +1279,12 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
11971279 } ;
11981280
11991281 let mut wallet = new_persisted_wallet ( network, & mut persister, wallet_opts) ?;
1200- let result =
1201- handle_offline_wallet_subcommand ( & mut wallet, wallet_opts, & cli_opts, offline_subcommand. clone ( ) )
1202- . await ?;
1282+ let result = handle_offline_wallet_subcommand (
1283+ & mut wallet,
1284+ wallet_opts,
1285+ & cli_opts,
1286+ offline_subcommand. clone ( ) ,
1287+ ) ;
12031288 wallet. persist ( & mut persister) ?;
12041289 result
12051290 } ;
@@ -1213,7 +1298,7 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12131298 offline_subcommand. clone ( ) ,
12141299 ) ?
12151300 } ;
1216- Ok ( result)
1301+ Ok ( result? )
12171302 }
12181303 CliSubCommand :: Key {
12191304 subcommand : key_subcommand,
@@ -1345,9 +1430,9 @@ async fn respond(
13451430 ReplSubCommand :: Wallet {
13461431 subcommand : WalletSubCommand :: OfflineWalletSubCommand ( offline_subcommand) ,
13471432 } => {
1348- let value = handle_offline_wallet_subcommand ( wallet , wallet_opts , cli_opts , offline_subcommand )
1349- . await
1350- . map_err ( |e| e. to_string ( ) ) ?;
1433+ let value =
1434+ handle_offline_wallet_subcommand ( wallet , wallet_opts , cli_opts , offline_subcommand )
1435+ . map_err ( |e| e. to_string ( ) ) ?;
13511436 Some ( value)
13521437 }
13531438 ReplSubCommand :: Key { subcommand } => {
0 commit comments